Compare commits

..

693 Commits
3.1.2 ... 3.3.1

Author SHA1 Message Date
James Cole
2722f0b749 Merge branch 'release/3.3.1' 2015-02-27 20:42:12 +01:00
James Cole
3aedfed432 Updated version. 2015-02-27 20:42:04 +01:00
James Cole
edeaf13259 New CSS / JS code. 2015-02-27 18:05:40 +01:00
James Cole
c1290c4e9b Forgot to commit one view. 2015-02-27 16:53:06 +01:00
James Cole
b2c1527b17 Lost a lot of stuff along the way. 2015-02-27 16:48:33 +01:00
James Cole
a4a65ea56e Merge branch 'release/3.3' into develop 2015-02-27 16:15:30 +01:00
James Cole
ccf0e1875e Merge branch 'release/3.3' 2015-02-27 16:15:29 +01:00
James Cole
9c009aceaf Updated the readme. 2015-02-27 16:15:16 +01:00
James Cole
da4b1c7276 Merge branch 'laravel5' into develop
Conflicts:
	resources/views/reports/month.blade.php

Upgraded to Laravel 5.
2015-02-27 16:13:36 +01:00
James Cole
add098d5c0 Update example environment file and composer lock. 2015-02-27 16:09:00 +01:00
James Cole
aca096548c Fixed user login and authentication. 2015-02-27 16:08:46 +01:00
James Cole
75aa3abcae Update and create transactions. 2015-02-27 14:27:04 +01:00
James Cole
e685d262cc Sub/addDay routine to fix the account list. 2015-02-27 12:55:24 +01:00
James Cole
d2599d6ef9 Removed a subDay() call that ruined some reports. 2015-02-27 12:54:24 +01:00
James Cole
cd5223d98d Removed a group clause that broke the budget report. 2015-02-27 12:15:58 +01:00
James Cole
7b68d9047d Order budgets by name. 2015-02-27 12:02:14 +01:00
James Cole
9a7ea06d66 Order budgets by name 2015-02-27 11:58:49 +01:00
James Cole
c69ef34ac9 Order accounts by name. 2015-02-27 11:57:47 +01:00
James Cole
021999d05f Ignore .env file. 2015-02-27 11:46:07 +01:00
James Cole
6054430a5e Remove default .env file. 2015-02-27 11:45:47 +01:00
James Cole
8f578ed95a Search controller. 2015-02-27 11:09:23 +01:00
James Cole
fc5c339e27 Repeated expenses. 2015-02-27 11:02:08 +01:00
James Cole
defad3d820 More code. 2015-02-25 21:19:06 +01:00
James Cole
c0f96aa948 Piggy banks 2015-02-25 19:32:33 +01:00
James Cole
f2eae2fc98 Implemented the help. 2015-02-25 16:10:02 +01:00
James Cole
0e4f786978 Fixed currencies. 2015-02-25 15:57:43 +01:00
James Cole
d36b2318fd New code for bills. 2015-02-25 15:19:14 +01:00
James Cole
d83b508bbc Add and remove transactions. 2015-02-24 22:53:38 +01:00
James Cole
5b9c2cdc13 Newly converted code. 2015-02-24 21:10:25 +01:00
James Cole
49066c282a Updates 2015-02-23 21:55:52 +01:00
James Cole
3e28c0c00a Extra report and cleanup. 2015-02-23 21:19:16 +01:00
James Cole
220d689f69 Reports! 2015-02-23 20:25:48 +01:00
James Cole
5a0a28a04c Stuff for categories. 2015-02-22 16:19:32 +01:00
James Cole
b44e69e09b Fixed tests. 2015-02-22 15:45:32 +01:00
James Cole
fcbe10f5ec New code. 2015-02-22 15:40:13 +01:00
James Cole
182fe170fd Up to budgets now! 2015-02-22 09:46:21 +01:00
James Cole
184e9bdaf6 Fixed even more tests! 2015-02-22 08:38:46 +01:00
James Cole
0096f50cde Update month.blade.php
Add decryption routine.
2015-02-21 12:20:42 +01:00
James Cole
460f14deca Some new code. 2015-02-21 12:16:41 +01:00
James Cole
910ad45bee Update test [skip ci] 2015-02-14 17:35:45 +01:00
James Cole
74e319855d Updated the test to be more dynamic. 2015-02-14 17:31:51 +01:00
James Cole
9af0fb4cd5 Update tests 2015-02-14 16:22:33 +01:00
James Cole
185f5cce29 environment files. 2015-02-14 16:13:28 +01:00
James Cole
aca1174566 New git ignore. 2015-02-14 16:11:20 +01:00
James Cole
e791f7fde2 Fixed test. 2015-02-14 15:53:56 +01:00
James Cole
7bba7fcf66 Should fix the test. 2015-02-14 14:31:43 +01:00
James Cole
1d78f98ec8 This should complete the account handler. 2015-02-14 14:25:29 +01:00
James Cole
7ed2e03654 Merge pull request #62 from kilya11/master
Handle foreign keys
2015-02-11 14:29:19 +01:00
Ilya Kil
549e0f3477 Handle foreign keys
Installation fails without these changes
2015-02-11 13:19:24 +01:00
James Cole
7785ec0222 Code cleanup. 2015-02-11 07:35:10 +01:00
James Cole
ca504965f9 Should allow to store new accounts. 2015-02-09 07:56:24 +01:00
James Cole
3841259779 First attempt at storing an account. 2015-02-09 07:23:39 +01:00
James Cole
169d1065cc Removed debug 2015-02-08 09:45:00 +01:00
James Cole
e864f5507a No exit is kind of cheating. 2015-02-08 09:35:09 +01:00
James Cole
434b4ded4a Reimplemented forms, added an overdue fix. 2015-02-08 01:15:15 +01:00
James Cole
3d01669cea Reinstated soft deletes, added first steps for account controller. 2015-02-07 23:19:28 +01:00
James Cole
1499b2cd40 Login and some routes fixed. 2015-02-07 22:50:47 +01:00
James Cole
6b54ef8398 More code. 2015-02-07 13:15:40 +01:00
James Cole
07ad43f7a2 Updated models and tests to match. 2015-02-07 12:15:53 +01:00
James Cole
72e72c60c2 Laravel 4 > Laravel 5 2015-02-07 12:00:31 +01:00
James Cole
f9a242d33e Updated codeception config. 2015-02-07 11:05:56 +01:00
James Cole
b0f43eaa07 Moved C3 handler. 2015-02-07 11:01:47 +01:00
James Cole
b02046b884 Moar packages! 2015-02-07 10:52:51 +01:00
James Cole
864c931ee9 Fixed factory muffin 2015-02-07 10:46:14 +01:00
James Cole
5cd8da6d91 Added factory muffin. 2015-02-07 10:42:40 +01:00
James Cole
4a4671c2ae Add debug info to codeception 2015-02-07 10:39:36 +01:00
James Cole
8de142cd9a Add c3 code coverage. 2015-02-07 10:38:27 +01:00
James Cole
1a42bec51c Database in the right spot. 2015-02-07 10:32:15 +01:00
James Cole
577e38759e Try again. 2015-02-07 10:30:41 +01:00
James Cole
9cdf43a2c9 Commit new env files. 2015-02-07 10:24:54 +01:00
James Cole
62d43c2cb2 Lets see fit his works. 2015-02-07 10:16:14 +01:00
James Cole
2dc67d1674 Added all the old test things. 2015-02-07 08:42:20 +01:00
James Cole
fb1c78c657 Chart re-implemented and added coveralls and other instructions, which will probably not work at all. 2015-02-07 08:23:44 +01:00
James Cole
37e58ac13a Fixed more of the index. 2015-02-07 06:49:24 +01:00
James Cole
de715c14be Some new stuffs. 2015-02-06 21:23:14 +01:00
James Cole
c4d8a0da05 Fixed the range thing 2015-02-06 20:43:19 +01:00
James Cole
1b54b14671 All kinds of new stuff. 2015-02-06 19:33:31 +01:00
James Cole
a92efbc55f Exceptions. 2015-02-06 19:31:38 +01:00
James Cole
1bd02529e0 New resources mostly. 2015-02-06 07:23:26 +01:00
James Cole
bc16298b6e Some updates. 2015-02-06 05:35:00 +01:00
James Cole
804a97cad7 References 2015-02-06 05:14:27 +01:00
James Cole
ab52bdec15 Not changed much 2015-02-06 05:05:40 +01:00
James Cole
ddc3e82c14 Update references 2015-02-06 05:04:06 +01:00
James Cole
57691471bb Updated models and imports. 2015-02-06 05:01:24 +01:00
James Cole
c502dd445b New and old code. 2015-02-06 04:52:16 +01:00
James Cole
ed475b1b9c Database & seeds. 2015-02-06 04:41:00 +01:00
James Cole
df165a817c Fresh L5 installation. 2015-02-06 04:39:52 +01:00
James Cole
d16015d625 Moved all old code for Laravel 5. 2015-02-06 04:27:37 +01:00
James Cole
a4b3bf3ef4 Moved to hidden "stash" directory. 2015-02-06 04:22:46 +01:00
James Cole
6b006853e6 Stash is a directory which will hold pretty much all code before it's implemented into Laravel 5. 2015-02-06 04:21:22 +01:00
James Cole
6001180e29 Capitalisation for code coverage. 2015-02-04 06:25:19 +01:00
James Cole
662fbed1d0 Added a "valid" email address so certain tests go through. 2015-02-04 06:15:14 +01:00
James Cole
19c7e08c5d Code coverage. 2015-02-03 19:40:13 +01:00
James Cole
72f04aaedc Some cleaning up. 2015-02-01 17:20:51 +01:00
James Cole
2998382969 Check if entries have an encrypted description and act accordingly. 2015-02-01 14:56:25 +01:00
James Cole
37fe79944f Reorder bills [skip ci] 2015-02-01 14:52:40 +01:00
James Cole
536735519a Fixed related transaction tests. 2015-02-01 12:31:11 +01:00
James Cole
6b0a711395 Expanded test coverage. 2015-02-01 12:07:06 +01:00
James Cole
13d3d40376 More code cleanup [skip ci] 2015-02-01 09:30:27 +01:00
James Cole
6873336aca More cleanup [skip ci] 2015-02-01 09:08:39 +01:00
James Cole
c2d2eb53e8 Some code cleanup. 2015-02-01 08:51:27 +01:00
James Cole
210d597a48 Close issue #46 2015-01-31 21:10:00 +01:00
James Cole
e41ede0a6b Forgot the return statement 2015-01-31 17:30:41 +01:00
James Cole
4a8b17ac7c Fixed the search for related transfers. 2015-01-31 17:24:26 +01:00
James Cole
1f5f515d72 Cleanup JS. 2015-01-31 15:10:07 +01:00
James Cole
1e6242b89f Updated composer.lock 2015-01-31 15:10:00 +01:00
James Cole
dde09f9f89 Various code cleanup things inspired by Code Climate [skip ci] 2015-01-31 08:51:40 +01:00
James Cole
916d85c3fe Simplified the account opening balance code. 2015-01-31 08:32:00 +01:00
James Cole
b243ed93aa Merge branch 'release/3.2.5' 2015-01-31 06:35:37 +01:00
James Cole
70d28bbf6e Merge branch 'release/3.2.5' into develop 2015-01-31 06:35:37 +01:00
James Cole
3c76da7132 Update version in readme file. 2015-01-31 06:35:19 +01:00
James Cole
3254565c09 Seed only in test [skip ci] 2015-01-31 06:34:31 +01:00
James Cole
cd0033791f New SQL reference for 3.2.5 [skip ci] 2015-01-31 06:34:10 +01:00
James Cole
2427ee44a5 Updated travis to handle SSL errors when communicating with Code climate. 2015-01-31 06:29:45 +01:00
James Cole
ff0e617b2a This triggers CC 2015-01-31 06:21:40 +01:00
James Cole
e6cfe040b5 Clean up some javascript, small fix for the test reporter 2015-01-31 06:11:55 +01:00
James Cole
94e2f9b6dc Updated tests to understand encrypted database content. 2015-01-30 22:43:52 +01:00
James Cole
512b81ad93 Fixed some tests. 2015-01-30 22:32:12 +01:00
James Cole
fc0e76f431 Some new seeds and the ability to search encrypted transaction journals. 2015-01-30 22:24:02 +01:00
James Cole
9da69358e2 Included code for code climate. 2015-01-30 22:09:47 +01:00
James Cole
aa246b0b2b New full set of favicon. 2015-01-30 21:40:37 +01:00
James Cole
6ed649bc8a Update list of what currency support there is in Firefly. 2015-01-30 18:40:30 +01:00
James Cole
725f5b7110 Creating and editing an account now allows the opening balance to be set in a certain currency. Issue #37 2015-01-30 18:22:55 +01:00
James Cole
5a890c5c3a It is now possible to select a different currency if desired (issue #37). 2015-01-30 18:09:13 +01:00
James Cole
7752329b94 Fixed a bug where the rules where too tight in transaction journal. 2015-01-29 18:21:56 +01:00
James Cole
5f48f13890 Exceptions for stupid sqlite. 2015-01-29 17:44:13 +01:00
James Cole
a734e04561 Default value and rename for encrypted field values. 2015-01-29 17:31:18 +01:00
James Cole
5aa1db293f 3.2.5 will once again be capable of encrypting journal descriptions (and more, in the future). 2015-01-29 05:23:31 +01:00
James Cole
f89aee37f5 Fixed a bug where the referer might not be picked up correctly. 2015-01-29 05:11:00 +01:00
James Cole
538018fed1 Should fix query [skip ci] 2015-01-27 19:22:54 +01:00
James Cole
a2327c50ec Should fix query [skip ci] 2015-01-27 19:19:44 +01:00
James Cole
c2711023e2 Updated query, lets see what happens [skip ci] 2015-01-27 18:52:59 +01:00
James Cole
cac30f0b4c Updated code coverage. 2015-01-25 15:29:09 +01:00
James Cole
4bb17019a4 Test transaction views for transactions without budgets and categories. 2015-01-25 13:17:32 +01:00
James Cole
ba2a40bdf3 Removed unused methods. 2015-01-25 12:21:56 +01:00
James Cole
f3460cca49 Ignore methods in code coverage that are not implemented. 2015-01-25 11:36:34 +01:00
James Cole
64ce53ac30 Test for opening balance. 2015-01-25 10:55:15 +01:00
James Cole
a43238360c Update caps. 2015-01-25 10:50:44 +01:00
James Cole
b2cbadf5d8 New chart route. 2015-01-25 10:06:31 +01:00
James Cole
81640ba06d Revamped and simplified form code. 2015-01-25 10:06:19 +01:00
James Cole
9327430484 Small optimalizations. 2015-01-25 08:28:59 +01:00
James Cole
a24c90eae8 Other redirects [skip ci] 2015-01-24 21:27:07 +01:00
James Cole
1d3987ece6 Fixed the tests. 2015-01-24 17:58:03 +01:00
James Cole
83f5b5e293 New budget charts for year report. 2015-01-24 08:54:33 +01:00
James Cole
f231263085 Updated chart, closed magic number issue. 2015-01-24 08:43:35 +01:00
James Cole
4ad67a87f1 Fixed budget charts. 2015-01-24 07:46:57 +01:00
James Cole
b766d93d9a Fixed the account role view. 2015-01-24 07:15:03 +01:00
James Cole
0905ceb1d5 Codeception will not run in hhvm [skip ci] 2015-01-24 07:00:58 +01:00
James Cole
2fbf837354 Closed #40 [skip ci] 2015-01-24 06:56:02 +01:00
James Cole
4bd79c880c Build in hack. 2015-01-24 06:55:42 +01:00
James Cole
4af041e015 Let's see what happens when we run Hack. 2015-01-24 06:52:22 +01:00
James Cole
8dc3e3ec93 Add an actions menu to the account view. 2015-01-23 06:50:40 +01:00
James Cole
f4b68d26d6 Fixed a bug where a deposit would not get linked to a cash account. 2015-01-23 06:45:20 +01:00
James Cole
1887977b92 Small experimental cleaning up. 2015-01-19 07:21:44 +01:00
James Cole
8eb84acf4f Show the view for transactions without a category. 2015-01-19 06:33:30 +01:00
James Cole
1b685da3e3 Code cleanup and query optimisation. 2015-01-18 21:40:00 +01:00
James Cole
406b658801 Code cleanup. 2015-01-18 21:07:40 +01:00
James Cole
bba1ee1264 Last attempt. 2015-01-18 11:18:06 +01:00
James Cole
02b6191d47 Another attempt at catching the sqlite error. 2015-01-18 11:12:17 +01:00
James Cole
c5a3de09cd Catch a sqlite error. 2015-01-18 10:33:01 +01:00
James Cole
0afe2a680e Gave report a subtitle. 2015-01-18 09:50:51 +01:00
James Cole
03e0510c4f Fixed a bug where certain reports would not show incomes from shared accounts. 2015-01-18 09:49:53 +01:00
James Cole
1068dcb8a4 Cleanup and refactor 2015-01-18 09:49:32 +01:00
James Cole
10a93df653 Clean up test data seeder 2015-01-18 09:48:58 +01:00
James Cole
79ff67852f Deleted an old unique index. 2015-01-18 09:48:48 +01:00
James Cole
a36cab969f Code cleanup. 2015-01-18 09:48:36 +01:00
James Cole
45447646fa Code cleanup. 2015-01-18 09:48:29 +01:00
James Cole
8a0f76ab68 Code cleanup. 2015-01-18 09:48:24 +01:00
James Cole
037135e764 A complete gamble on my side to fix a bug where transfers FROM shared accounts were not counted as income. 2015-01-18 00:10:57 +01:00
James Cole
21e89c3b64 Remove composer.lock when running Travis. 2015-01-17 11:31:12 +01:00
James Cole
bd11ec69fa Reinstated test data seeder, fixed the tests. 2015-01-17 10:41:29 +01:00
James Cole
9e2b34bc12 Various cleanup. 2015-01-17 10:06:12 +01:00
James Cole
0faebc290f Suppress warnings. 2015-01-17 10:05:51 +01:00
James Cole
fc0ef4b79d Small optimizations. 2015-01-17 10:05:43 +01:00
James Cole
027b954b50 Cleanup various factories and libraries. 2015-01-17 08:58:49 +01:00
James Cole
33c830a432 Cleaned up seeders. 2015-01-17 08:58:30 +01:00
James Cole
a7887f1e25 Added inspections for all migrations. 2015-01-17 08:58:19 +01:00
James Cole
fa7a59572a Code cleanup for all controllers. 2015-01-17 08:57:55 +01:00
James Cole
d9c2df5b0d Removed unused methods. 2015-01-17 07:33:43 +01:00
James Cole
a854b2c17e Some code cleanup in the account code. 2015-01-17 07:25:44 +01:00
James Cole
7d4006b205 Fixed some bugs while registering users. 2015-01-17 07:14:01 +01:00
James Cole
86ecca6011 Removed weird config values. 2015-01-16 07:11:03 +01:00
James Cole
0ea5cf2caa Merge branch 'master' of github.com:JC5/firefly-iii
Conflicts:
	app/config/app.php
2015-01-16 07:09:46 +01:00
James Cole
a950e02e9b Revert "Cleanup in preparation of an overhaul."
This reverts commit 5662a02a36.
2015-01-16 07:08:56 +01:00
James Cole
9eec6641dd Cleanup in preparation of an overhaul. 2015-01-16 07:08:56 +01:00
James Cole
18f46676fd Bug fix in new accounts. 2015-01-16 07:08:56 +01:00
James Cole
9735ef6d41 Revert "Cleanup in preparation of an overhaul."
This reverts commit 5662a02a36.
2015-01-16 07:07:57 +01:00
James Cole
d3e8ceee00 Cleanup in preparation of an overhaul. 2015-01-14 12:24:08 +01:00
James Cole
144e329eca Merge branch 'release/3.2.4'
Conflicts:
	app/config/app.php
2015-01-13 20:44:40 +01:00
James Cole
21d5420b2a Bug fix in new accounts. 2015-01-13 20:43:54 +01:00
James Cole
3011b5074d Merge branch 'release/3.2.3' 2015-01-13 19:08:33 +01:00
James Cole
83190572c7 New composer.lock 2015-01-10 18:07:39 +01:00
James Cole
9cf9e5f865 Added a missing field. 2015-01-05 20:22:39 +01:00
James Cole
5bdef7f1c7 Added a missing field. 2015-01-05 20:22:19 +01:00
James Cole
ba285a2d2d Added a missing field. 2015-01-05 20:21:43 +01:00
James Cole
0dff371e62 Added a missing field. 2015-01-05 20:20:03 +01:00
James Cole
ce4a2a5851 Added a missing field. 2015-01-05 20:19:34 +01:00
James Cole
2c978dc89a Removed experimental routes. 2015-01-05 17:53:38 +01:00
James Cole
4b8b819109 Redirect update. 2015-01-04 20:50:35 +01:00
James Cole
c230b3a806 This broke the tests. 2015-01-03 18:09:44 +01:00
James Cole
df08b9c5c6 Updated composer.lock 2015-01-03 13:46:53 +01:00
James Cole
eca65376a3 Fixed a route. 2015-01-03 09:25:40 +01:00
James Cole
88e3705636 Do some redirection. 2015-01-02 22:47:34 +01:00
James Cole
5476509ef5 Disabled some seeds, updated some routes. 2015-01-02 22:44:25 +01:00
James Cole
0bd6636453 SQL reference file [skip ci] 2015-01-02 20:04:26 +01:00
James Cole
105894e00d Small bug in budget helper [skip ci] 2015-01-02 19:53:09 +01:00
James Cole
230a319510 Removed files no longer used [skip ci] 2015-01-02 18:52:55 +01:00
James Cole
ae16a2b14f Routes and views for transactions without a budget / category [skip ci] 2015-01-02 18:48:06 +01:00
James Cole
da0c0742bf Covered some more lines of code. 2015-01-02 13:57:40 +01:00
James Cole
61d60a9048 Updated read me [skip ci] 2015-01-02 12:56:07 +01:00
James Cole
3e28e9a016 Merge branch 'release/3.2.2' 2015-01-02 12:53:44 +01:00
James Cole
423f9fefa9 Removed todo entries and made issues instead. [skip ci] 2015-01-02 12:42:29 +01:00
James Cole
5707dc7579 Lots of cleaning up. 2015-01-02 12:38:13 +01:00
James Cole
3be1cdb249 Some cleaning up in the reports. [skip ci] 2015-01-02 11:04:51 +01:00
James Cole
426d3d948c Added newlines [skip ci] 2015-01-02 10:55:59 +01:00
James Cole
9a3aed8038 Moved code to relate transfers to another class. Still needs some work. 2015-01-02 10:53:18 +01:00
James Cole
fb58bf1bf5 Cleaned up some todo entries [skip ci] 2015-01-02 10:01:33 +01:00
James Cole
a6dbd912c6 Code cleanup. 2015-01-02 09:06:44 +01:00
James Cole
65ce277a20 Updated models [skip ci] 2015-01-02 08:59:16 +01:00
James Cole
0b2d423c87 Updated ignore file. 2015-01-02 06:26:57 +01:00
James Cole
da056092fb Code cleanup [skip ci] 2015-01-02 06:26:04 +01:00
James Cole
45aa85d690 Added new lines [skip ci] 2015-01-02 06:24:48 +01:00
James Cole
5c35fee0c2 New lines at end of file [skip ci] 2015-01-02 06:16:49 +01:00
James Cole
24bdc319dd Some refactoring [skip ci] 2015-01-02 06:05:40 +01:00
James Cole
f1dcc41e42 Removed invalid composer.json entry [skip ci] 2015-01-02 06:00:14 +01:00
James Cole
550f301ba2 Code cleanup [skip ci] 2015-01-02 05:52:38 +01:00
James Cole
d9bf4d1c0d Removed c3.php from lib. [skip ci] 2015-01-02 05:37:25 +01:00
James Cole
c3c1a6eb22 Changed permissions [skip ci] 2015-01-02 05:36:49 +01:00
James Cole
2c4454418e Remove possible xsrf [skip ci] 2015-01-02 05:36:05 +01:00
James Cole
e44de572f5 Code cleanup [skip ci] 2015-01-01 23:12:12 +01:00
James Cole
f27919f91b Fixed transaction journal test. 2015-01-01 22:57:15 +01:00
James Cole
ba9968bde0 Fixed a bug in "number between" tests. 2015-01-01 22:53:03 +01:00
James Cole
05ea8216ff Fixed transaction coverage. 2015-01-01 22:51:38 +01:00
James Cole
fa1695672a Cleaning up. 2015-01-01 22:32:25 +01:00
James Cole
ac6f98fc47 First attempt to make the year charts and month reports report the same thing [skip ci] 2015-01-01 21:35:05 +01:00
James Cole
1a1f89f555 First attempt to make the year charts and month reports report the same thing [skip ci] 2015-01-01 21:27:51 +01:00
James Cole
6c3262e176 First attempt to make the year charts and month reports report the same thing [skip ci] 2015-01-01 21:26:40 +01:00
James Cole
b4bdb48f1e First attempt to make the year charts and month reports report the same thing [skip ci] 2015-01-01 21:23:12 +01:00
James Cole
823afe877b First attempt to make the year charts and month reports report the same thing [skip ci] 2015-01-01 21:20:41 +01:00
James Cole
cb8e082414 Fixed the bill tests. 2015-01-01 21:06:24 +01:00
James Cole
98c1fcc68f New content and tests ensure the coverage of all code. 2015-01-01 20:53:36 +01:00
James Cole
8c439a2852 Added debug information [skip ci] 2015-01-01 20:02:02 +01:00
James Cole
50c6109be7 Fixed broken tests. 2015-01-01 19:50:36 +01:00
James Cole
6e362663b5 Clean up content seeder and tests. 2015-01-01 19:35:10 +01:00
James Cole
74c9feb53f More tests! 2015-01-01 13:43:34 +01:00
James Cole
402e8588cf Even more tests! 2015-01-01 13:32:31 +01:00
James Cole
778a42bcc0 More unit tests. 2015-01-01 13:12:05 +01:00
James Cole
584f7ced84 New tests and new configuration for tests. 2015-01-01 12:33:07 +01:00
James Cole
8e892e7ea5 New unit tests to cover missed methods. 2015-01-01 12:06:42 +01:00
James Cole
3386c8b455 More spelling checks and small clean ups. 2014-12-31 17:15:59 +01:00
James Cole
6fa73ee28d Complexity cleanup [skip ci] 2014-12-31 16:45:12 +01:00
James Cole
8ec8042045 Some spell checking [skip ci] 2014-12-31 16:17:43 +01:00
James Cole
cddc123539 Removed dead code. 2014-12-31 09:02:36 +01:00
James Cole
4c2938c5cd New content and a fix for the bill controller. 2014-12-31 08:31:18 +01:00
James Cole
6d03ddadcc Covered the final lines. 2014-12-31 08:11:00 +01:00
James Cole
64311da4b4 Full coverage for home controller 2014-12-31 07:43:33 +01:00
James Cole
0cbb50ae9d Full coverage for user controller. 2014-12-31 07:17:33 +01:00
James Cole
7e96054dc2 Covered everything in the user controller except configuration controlled statements. 2014-12-31 00:57:12 +01:00
James Cole
578298580e Last tests for transaction controller. 2014-12-31 00:25:17 +01:00
James Cole
ee5afaa6bc First tests for transaction controller. 2014-12-30 22:25:30 +01:00
James Cole
15b023d116 Updated composer.lock [skip ci] 2014-12-30 21:04:01 +01:00
James Cole
1ef96c0b4d Finally updated the transaction controller to have some more sensible code. 2014-12-30 21:03:42 +01:00
James Cole
8c3ae40de1 Some refactoring. 2014-12-30 18:44:58 +01:00
James Cole
94fcfacec4 Tests for search [skip ci] 2014-12-30 18:44:49 +01:00
James Cole
ba7c01c6bc Fixed the tests. 2014-12-30 18:25:38 +01:00
James Cole
9f92e1b7bd Some tests and a rename. 2014-12-30 17:55:46 +01:00
James Cole
1f0e692ee2 New tests for the repeated expenses. 2014-12-30 17:27:31 +01:00
James Cole
0acd75a24f Updated tests for help controller. 2014-12-30 15:55:21 +01:00
James Cole
eedf27f8a5 Fixed the tests [skip ci] 2014-12-30 15:24:10 +01:00
James Cole
b451e207e2 Finally implemented repeated expenses properly. [skip ci] 2014-12-30 15:17:01 +01:00
James Cole
c0c37eec7b Most views now show the transaction the current journal/transaction is set in, even if it's not the current default currency. See issue #37 2014-12-30 06:30:20 +01:00
James Cole
89363ecfa3 Tests for reminders. 2014-12-29 21:49:43 +01:00
James Cole
593e799ca1 New and matching icon for bills [skip ci] #38 2014-12-29 20:39:27 +01:00
James Cole
8fc055cad9 Renamed a container [skip ci] #38 2014-12-29 20:36:56 +01:00
James Cole
75f86462e2 All code for issue #38. 2014-12-29 20:28:17 +01:00
James Cole
40892ccfa7 Some small updates to piggy banks. 2014-12-28 18:03:35 +01:00
James Cole
87fbf9c1a5 Added the ability to see cash accounts. [skip ci] 2014-12-28 09:00:22 +01:00
James Cole
4944b233b6 Greatly expanded report functionality. 2014-12-28 08:54:53 +01:00
James Cole
9f23462c42 Expanded reports. 2014-12-27 17:21:15 +01:00
James Cole
84a24f0333 Something with migrations. 2014-12-27 05:38:33 +01:00
James Cole
7a885bfc3c Fixed various bugs that made tests fail. 2014-12-26 22:59:13 +01:00
James Cole
3ba0cf1454 Expanded summary [skip ci] 2014-12-26 21:14:45 +01:00
James Cole
2d67a3159d Expanded reports 2014-12-26 21:08:44 +01:00
James Cole
290f25f1a0 First attempt at new month report. 2014-12-25 09:50:01 +01:00
James Cole
1659904f81 Various cleanup and spelling fixes. 2014-12-25 08:07:17 +01:00
James Cole
230bd6e40a Tests for the recurring transaction controller. 2014-12-25 08:00:09 +01:00
James Cole
ce27e97b92 More tests! Yay! 2014-12-25 00:42:31 +01:00
James Cole
18c1223c7b Tests for the profile controller 2014-12-24 22:52:14 +01:00
James Cole
8ef659f5de Tests for preferences controller. 2014-12-24 22:39:23 +01:00
James Cole
037452e525 Spelling errors fixed. 2014-12-24 21:20:47 +01:00
James Cole
e3482011d5 Updated two views. 2014-12-24 21:07:20 +01:00
James Cole
62748fa255 Migrations for future version 3.2.2 2014-12-24 20:56:05 +01:00
James Cole
7a9df05f6b A giant rename action in preparation of v3.2.2 2014-12-24 20:55:42 +01:00
James Cole
335279e728 Renamed lots of "piggybank" to "piggyBank". 2014-12-24 19:13:15 +01:00
James Cole
0332104738 Expanded tests for piggy banks. 2014-12-24 19:00:31 +01:00
James Cole
9f04854902 Merge branch 'release/3.2.1' 2014-12-24 14:34:51 +01:00
James Cole
73008a35fe Clean up tests. 2014-12-24 14:33:02 +01:00
James Cole
eae96cd2af Covered currency controller in tests. 2014-12-24 14:02:21 +01:00
James Cole
cb670bb27d Update, edit and delete currencies. 2014-12-24 12:32:18 +01:00
James Cole
fe1fb23e5b Extend JS to include currency code #37 2014-12-24 06:10:39 +01:00
James Cole
c2dd61e96b Methods to grab the requested currency symbol. #37 2014-12-24 05:58:26 +01:00
James Cole
80f5e61b6b Append default data to current currency table (assuming default entries). 2014-12-24 05:51:18 +01:00
James Cole
dbcae16b75 Made two new columns nullable. #37 2014-12-24 05:43:08 +01:00
James Cole
886dcae822 Route for #37 2014-12-23 22:27:58 +01:00
James Cole
ed495ec600 Menu for #37 2014-12-23 22:27:52 +01:00
James Cole
ddb60ccdc5 New view for #37 2014-12-23 22:27:45 +01:00
James Cole
335e2083af Expand helper for #37 2014-12-23 22:27:29 +01:00
James Cole
7b1d9d4962 First basic controller for #37 2014-12-23 22:27:14 +01:00
James Cole
da6ff9f90a Migrations for #37 2014-12-23 22:27:00 +01:00
James Cole
48f26c7bf1 Added a route for future currency options. 2014-12-23 21:55:30 +01:00
James Cole
3ce317b170 New tests. 2014-12-23 21:55:19 +01:00
James Cole
b741565f57 New test content. 2014-12-23 21:55:08 +01:00
James Cole
d8fea44968 New setting called 'budgetMaximum', see issue #36 2014-12-23 21:14:26 +01:00
James Cole
778300b67e Updated view for setting called 'budgetMaximum', see issue #36 2014-12-23 21:14:17 +01:00
James Cole
cb2b44fef3 New setting called 'budgetMaximum', see issue #36 2014-12-23 21:13:59 +01:00
James Cole
cdb5875d6b Some cleaning up for tests. 2014-12-23 21:13:42 +01:00
James Cole
01c5e15bcd Retrieve the new setting called 'budgetMaximum' which allows you to set the maximum budget. 2014-12-23 21:13:32 +01:00
James Cole
f0babb4be7 Made sure the migrations are reversible and updated the test seeder. 2014-12-22 20:43:38 +01:00
James Cole
10b00da874 Try to fix the tests. 2014-12-22 18:41:16 +01:00
James Cole
e9f391b2eb First tests for piggy bank controller. 2014-12-22 07:10:18 +01:00
James Cole
50be39b054 Fixed a bug where an update to a transaction journal would trigger the wrong response. 2014-12-21 18:43:01 +01:00
James Cole
a94e0bb3da Some cleaning up in prep for tests [skip ci] 2014-12-21 18:40:37 +01:00
James Cole
3f65d5d760 Cover JSON controller. 2014-12-21 18:09:35 +01:00
James Cole
48cb528ae4 Increased test coverage. Also updated read me. 2014-12-21 17:59:47 +01:00
James Cole
e62e0345df Also covered help. 2014-12-21 11:15:37 +01:00
James Cole
441f011fba More coverage. 2014-12-21 10:54:25 +01:00
James Cole
af1349160a Lots of clean up in the Google Chart controller. 2014-12-20 22:38:56 +01:00
James Cole
2072607889 Cleanup, mostly I removed the coding standard ignore instructions. 2014-12-20 22:07:21 +01:00
James Cole
073fd5aa0d 4 instead of 3. 2014-12-20 17:41:33 +01:00
James Cole
7b4703e4ff Category controller covered. 2014-12-20 16:53:32 +01:00
James Cole
1484621300 More tests! 2014-12-20 16:06:25 +01:00
James Cole
40709c8367 +x for execute 2014-12-20 15:33:55 +01:00
James Cole
b6ab5770a2 Lets see if this works. 2014-12-20 15:30:48 +01:00
James Cole
83b7cb4ff9 Test updates. 2014-12-20 15:25:22 +01:00
James Cole
256dba66b2 Standard test database. 2014-12-20 15:04:26 +01:00
James Cole
6ac12f8ffa Updated codeception instructions. 2014-12-20 15:01:09 +01:00
James Cole
82e438d29b Updated tests. 2014-12-20 15:00:53 +01:00
James Cole
e86547645c Small bug fixes. 2014-12-20 07:33:59 +01:00
James Cole
8b901084fe Fixed the tests. 2014-12-19 21:36:53 +01:00
James Cole
1a0cbbdb31 Removed previous warning suppressions. 2014-12-19 21:18:42 +01:00
James Cole
30ac62ffb7 All kinds of code cleanup, mostly to get some mess detection fixed. 2014-12-19 20:47:33 +01:00
James Cole
8ab294e90b Move budget / category save. 2014-12-18 19:53:06 +01:00
James Cole
f5edb15f43 Fix bug in create transaction with cash account. 2014-12-18 19:51:08 +01:00
James Cole
eed6107ce7 Revamp migrations. 2014-12-18 19:01:00 +01:00
James Cole
d49dc599a2 Removed references to components. 2014-12-17 21:32:27 +01:00
James Cole
3c5179f145 Removed repair route [skip ci] 2014-12-17 21:21:08 +01:00
James Cole
067d17c09c New repair route [skip ci] 2014-12-17 21:20:23 +01:00
James Cole
4c88c9af86 Fix. [skip ci] 2014-12-17 20:54:33 +01:00
James Cole
1c84afe186 Temporary repair route to fix stuff [skip ci] 2014-12-17 20:52:08 +01:00
James Cole
9d4c4be468 Removed single table inheritance. 2014-12-17 20:47:46 +01:00
James Cole
de7db8db78 Slightly extended tests. 2014-12-16 21:41:16 +01:00
James Cole
407ba4dd6d Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop 2014-12-16 20:26:51 +01:00
James Cole
5135be3000 Expanded test coverage. 2014-12-16 20:25:24 +01:00
James Cole
b8ab7d1a14 Update README.md 2014-12-16 09:15:04 +01:00
James Cole
6211fd8496 Update README.md
Added new badge.
2014-12-16 09:13:29 +01:00
James Cole
a4f273b48b Updated ignore file [skip ci] 2014-12-16 07:09:08 +01:00
James Cole
68d820a97c Attempt #5 for coveralls. 2014-12-16 07:03:52 +01:00
James Cole
ebadfd6358 Attempt #4, disable phpbrowser. 2014-12-16 06:53:07 +01:00
James Cole
cf93a88adc Attempt #3 2014-12-16 06:48:53 +01:00
James Cole
bd2c4252bb Attempt #2. 2014-12-15 20:56:18 +01:00
James Cole
84e7af04b9 Attempt to fix the tests over at Travis. 2014-12-15 20:38:20 +01:00
James Cole
37c63bc6b5 Add some tests. 2014-12-15 20:24:19 +01:00
James Cole
e7d3716549 Universal database stuff. 2014-12-15 20:01:33 +01:00
James Cole
73bc5372c0 Update migrations to include index. 2014-12-15 19:48:59 +01:00
James Cole
b999a8f0fb Some cleaning up. About to release 3.2.1 2014-12-15 19:39:01 +01:00
James Cole
16678aa5e1 Fixed bugs! 2014-12-15 18:16:48 +01:00
James Cole
f9ab49911d Bug fix where the account's balance would be cached when it shouldn't. 2014-12-15 18:03:16 +01:00
James Cole
3fabe2e9fb Small bug fix. 2014-12-15 07:24:01 +01:00
James Cole
9a8a3e94d6 Fix a chart. 2014-12-14 21:27:13 +01:00
James Cole
32ef2ef801 Renamed a table, added a bug fix. 2014-12-14 21:24:20 +01:00
James Cole
76cd3d35e2 Forgot some routes & filters. 2014-12-14 20:41:43 +01:00
James Cole
900dea2c66 Code cleanup. 2014-12-14 20:40:02 +01:00
James Cole
8e6ca0dd05 Some cleaning up. 2014-12-13 23:40:25 +01:00
James Cole
4b4ad7f1a8 Moved all references. 2014-12-13 22:54:52 +01:00
James Cole
21a0a5d573 Remapped all library classes. 2014-12-13 22:11:51 +01:00
James Cole
3cfa3f3b27 Code cleanup. 2014-12-13 21:59:02 +01:00
James Cole
c77b43458e Removed some charts, removed lots of dead code. 2014-12-13 20:03:44 +01:00
James Cole
9ab0a83f7c Default value for when session is empty. 2014-12-13 13:07:32 +01:00
James Cole
d3cda8811d Added some new tests. 2014-12-13 13:07:12 +01:00
James Cole
55740c0d97 Add c3.php to repository. 2014-12-13 09:54:32 +01:00
James Cole
c0a524c8a3 Add debug info. 2014-12-13 09:51:19 +01:00
James Cole
e51c2d10f0 Generate coverage. 2014-12-13 09:39:11 +01:00
James Cole
7af55b7268 Updated some tests, fixed some bugs. 2014-12-13 09:36:30 +01:00
James Cole
7350b1da1b Cleanup and added todo's 2014-12-12 16:22:16 +01:00
James Cole
136adbe723 Some cleanup and a small bug fix. 2014-12-12 07:42:38 +01:00
James Cole
cb2863eaf3 Cleaned up the budget controller. 2014-12-12 07:13:40 +01:00
James Cole
d054f9b92f Added composer.lock. 2014-12-10 19:15:14 +01:00
James Cole
9c83c18137 Fixed some query-heavy things in the report controller. 2014-12-09 18:24:53 +01:00
James Cole
999a7481e4 Removed references to the field "migrated". 2014-12-09 06:37:36 +01:00
James Cole
a3b684b4ed Merge branch 'release/3.2.0'
Conflicts:
	app/config/app.php
2014-12-08 20:46:23 +01:00
James Cole
cb312ca025 Remove packages only found in development. 2014-12-08 20:44:06 +01:00
James Cole
1335a52db3 Remove packages only found in development. 2014-12-08 20:27:33 +01:00
James Cole
727221e2cb Cleanup migrations. 2014-12-08 06:57:12 +01:00
James Cole
360f286ed3 Some cleanup. 2014-12-07 20:45:52 +01:00
James Cole
6aecd77b77 New stuff! 2014-12-07 15:37:53 +01:00
James Cole
ca85cab6fb Merge branch 'release/3.1.6' 2014-12-06 22:29:17 +01:00
James Cole
742479bb01 Update for coveralls. 2014-12-06 22:12:02 +01:00
James Cole
68e54a9297 Include coveralls. 2014-12-06 22:04:29 +01:00
James Cole
056d83eda4 Fixed the codeception command line 2014-12-06 21:59:03 +01:00
James Cole
42ec55d0db Upgrade to php 5.6 2014-12-06 21:53:09 +01:00
James Cole
c89bd89d9a Include codeception in travis. 2014-12-06 21:52:39 +01:00
James Cole
31a0be5bb4 All kinds of cleanup. 2014-12-06 21:48:23 +01:00
James Cole
07610ae8fb All kinds of new stuff for Codeception, which isn't working at ALL 2014-12-06 19:47:43 +01:00
James Cole
dbc95dd878 Cleanup. 2014-12-06 17:53:25 +01:00
James Cole
792e8a9947 Start with code cleanup. 2014-12-06 17:45:29 +01:00
James Cole
d774cde109 Add soft deletes. 2014-12-06 17:37:05 +01:00
James Cole
cc111d14b0 Lots of cleanup and formatting. 2014-12-06 17:34:39 +01:00
James Cole
bb3ba42ce2 Replaced Ardent with another package. 2014-12-06 12:12:55 +01:00
James Cole
f4ecf2d1aa Some new report data. Also, the report page now uses in excess of 3000 queries. Lol 2014-12-06 09:16:54 +01:00
James Cole
1997666196 Update comment to match methods. 2014-12-05 22:50:26 +01:00
James Cole
3aa9057c5f Small fixes. 2014-12-05 22:06:33 +01:00
James Cole
834f8382e9 Added some todo's. 2014-12-05 21:45:48 +01:00
James Cole
357638a26c Also do some account meta on the create screen. 2014-12-05 21:44:01 +01:00
James Cole
919a0c01e4 Expand views and JS. 2014-12-05 21:39:48 +01:00
James Cole
6ff618e388 Expand models. 2014-12-05 21:39:34 +01:00
James Cole
ac765b7e4c Expand model to get meta data 2014-12-05 21:39:26 +01:00
James Cole
2134e87c31 Method to unrelate transactions. 2014-12-05 21:39:16 +01:00
James Cole
0d37288129 Expand account controller with meta data (edit / update routine). 2014-12-05 21:39:04 +01:00
James Cole
7cbd41137d New configuration. 2014-12-05 21:38:45 +01:00
James Cole
ba43d7063f New route to unrelate transactions. 2014-12-05 21:38:36 +01:00
James Cole
c9b2e29ba0 Manage related transactions. 2014-12-04 20:38:45 +01:00
James Cole
4720519aef Clean up models, fixed a bug. 2014-12-01 06:09:27 +01:00
James Cole
d7b0106e7d Expand code for reports. 2014-12-01 05:57:03 +01:00
James Cole
0a2cbaa047 Some new code, transaction groups among them. 2014-11-30 14:52:17 +01:00
James Cole
3e5f615ffc Expand bread crumbs to include opening balances. 2014-11-28 17:29:05 +01:00
James Cole
00dc73e6d9 Added all bread crumbs. 2014-11-28 16:35:11 +01:00
James Cole
aa5b9a1727 More bread crumbs. 2014-11-28 16:15:30 +01:00
James Cole
f63a287a6c More bread crumbs. 2014-11-28 16:09:18 +01:00
James Cole
b9d4c8dcd6 More bread crumbs. 2014-11-28 16:04:06 +01:00
James Cole
94433f1714 Bread crumbs for categories. 2014-11-28 15:58:54 +01:00
James Cole
a4dd4358b4 Small fix for budget bread crumbs. 2014-11-28 15:53:50 +01:00
James Cole
4e79b43395 More bread crumbs. 2014-11-28 15:52:52 +01:00
James Cole
6bb54bfa85 More bread crumbs. 2014-11-28 15:46:44 +01:00
James Cole
0df6c3a8dc Some work on bread crumbs and reports. 2014-11-28 15:41:32 +01:00
James Cole
dc25086eab Updated composer.json 2014-11-28 14:55:59 +01:00
James Cole
3299188edf New report 2014-11-28 14:41:58 +01:00
James Cole
f6afb46f6f Some new help functions, some cleanup. 2014-11-28 14:12:16 +01:00
James Cole
98993cfa9b Updated hasManyThrough 2014-11-28 07:40:59 +01:00
James Cole
5a920d5efd Fix the view for accounts. 2014-11-28 07:40:04 +01:00
James Cole
935276af88 Updated accounts so actions will trigger cache flush. 2014-11-27 16:20:16 +01:00
James Cole
5a505c8469 Show everything on demand. 2014-11-27 15:42:07 +01:00
James Cole
2c2abe8b8e Fixed a bug where editing an expense account wasn't actually possible. 2014-11-27 15:37:31 +01:00
James Cole
638099d989 Fixed a bug where editing an expense account wasn't actually possible. 2014-11-27 15:36:37 +01:00
James Cole
0f32f6be4c Fixed a bug where editing an expense account wasn't actually possible. 2014-11-27 15:35:51 +01:00
James Cole
1f7c98bdcf Installed codeception. 2014-11-26 21:31:12 +01:00
James Cole
72068a4b78 Fixed the preselected piggy bank 2014-11-26 21:11:13 +01:00
James Cole
c8038e0774 A test to see if transfers and transactions and what-not can be related somehow. 2014-11-26 21:07:14 +01:00
James Cole
6b39beecb4 Expanded the report thing. 2014-11-26 20:46:21 +01:00
James Cole
b1ba64db12 New and updated views. 2014-11-26 17:20:43 +01:00
James Cole
ad53832766 Fixed a ORM thing. 2014-11-26 17:20:32 +01:00
James Cole
d5bffc8ed7 Removed debug messages. 2014-11-26 17:20:23 +01:00
James Cole
69b36ddd1d New methods. 2014-11-26 17:20:18 +01:00
James Cole
9f926394a6 Some new reports. 2014-11-26 17:20:05 +01:00
James Cole
1e7ecbdf9d Removed debug messages. 2014-11-26 17:19:59 +01:00
James Cole
beb8a461cf Some new reports. 2014-11-26 17:19:50 +01:00
James Cole
e235a57e2f Fixed a bug where the paginator for transactions would skip pages. 2014-11-26 09:43:21 +01:00
James Cole
114b27079e Added a routine that will cache stuff that costs a lot of queries. 2014-11-25 22:04:50 +01:00
James Cole
6e19bc01f5 Because this function generates a lot of queries, i've disabled it for now. 2014-11-25 21:15:49 +01:00
James Cole
f9dfdeafb3 Fixed a bug where deposits and withdrawals might show the wrong accounts when editing. 2014-11-25 21:09:52 +01:00
James Cole
f05d626e38 Mostly cleanup and bug fixes. 2014-11-25 21:04:00 +01:00
James Cole
918041258e Removed cleanup method. 2014-11-25 20:47:33 +01:00
James Cole
a5b13aa67f A very simple tool to do some cleanup. 2014-11-25 20:44:46 +01:00
James Cole
1383cbd4d5 Fixed a bug where a null transaction would be used anyway. 2014-11-25 20:22:32 +01:00
James Cole
e9b7e82aea Some visual updates to repeated expenses. 2014-11-24 23:02:08 +01:00
James Cole
fd6e7fc1ab Fixed a bug where the associated total amount for a bar would be too high. 2014-11-24 18:08:20 +01:00
James Cole
743deb4227 More stuff for repeated expenses. 2014-11-24 18:06:21 +01:00
James Cole
b051278d2e Expand repeated expenses. 2014-11-24 17:01:37 +01:00
James Cole
bfda4bc199 Lots of new stuff. 2014-11-24 10:12:34 +01:00
James Cole
4456ef2326 Some expansion on the index of repeated expenses, and the first reference to the show page. 2014-11-23 06:29:29 +01:00
James Cole
7336367eff Duh. Bug fixed. 2014-11-22 23:38:58 +01:00
James Cole
9cb6c7697e Some debug information. 2014-11-22 23:38:08 +01:00
James Cole
cd44f51072 Add some floatvals() just in case. 2014-11-22 23:37:03 +01:00
James Cole
d8976379b1 Some debug information. 2014-11-22 23:35:39 +01:00
James Cole
8aa847c718 Expanded the view. 2014-11-22 23:34:38 +01:00
James Cole
6691b238f7 New routes for repeated expenses. 2014-11-22 23:32:34 +01:00
James Cole
cca2758138 New views for the repeated expenses. 2014-11-22 23:32:24 +01:00
James Cole
4a74e68e31 Cleanup and expansion to accomodate repeated expenses. 2014-11-22 23:32:10 +01:00
James Cole
3d3842b9d6 Reinstated "periodshow" and some other stuff. 2014-11-22 23:31:55 +01:00
James Cole
9d889d05e4 Expanded Events to check on repeated expenses. 2014-11-22 23:31:40 +01:00
James Cole
9f920bcfe3 Expanded the transaction controller to show repeated expenses 2014-11-22 23:31:25 +01:00
James Cole
1bb49fa496 Expanded the repeated expenses. 2014-11-22 23:31:06 +01:00
James Cole
6b1d8d3aaa Added the possibility to save per quarter. 2014-11-22 23:30:58 +01:00
James Cole
c6b6ed7fa8 Removed a bunch of methods and code that wasn't used. Also added a repository for repeated expenses. 2014-11-22 23:30:45 +01:00
James Cole
34454261d2 First view for the return of the repeated expenses. 2014-11-22 17:17:28 +01:00
James Cole
ddf9f52737 Mar cleanup. 2014-11-22 08:21:10 +01:00
James Cole
10b969a074 New cleanup command for artisan 2014-11-22 08:17:37 +01:00
James Cole
5df0634380 Cleanup. 2014-11-22 07:34:08 +01:00
James Cole
f0f965421c Implemented some todo's. 2014-11-22 07:30:46 +01:00
James Cole
6381408fba Remove some often used long calls with shorter ones. 2014-11-21 19:33:09 +01:00
James Cole
43e738cb44 Remove todo 2014-11-21 19:19:06 +01:00
James Cole
6d4303aa3f Clone Carbon. 2014-11-21 19:18:53 +01:00
James Cole
13c2db5378 Removed some todo's 2014-11-21 19:18:44 +01:00
James Cole
36f6bda525 Fixed a namespace bug. 2014-11-21 11:21:48 +01:00
James Cole
78886e7b1f Updated git ignore. 2014-11-21 11:18:02 +01:00
James Cole
3dce194930 Use facades. 2014-11-21 11:12:22 +01:00
James Cole
ec776bb6eb Add some hasManyThrough references 2014-11-21 11:11:52 +01:00
James Cole
243d942a6e Remove unused library 2014-11-21 11:11:14 +01:00
James Cole
ea6aba62c4 Remove unused code. 2014-11-21 11:11:03 +01:00
James Cole
73743721b1 Fixed a bug where a non-matching journal would keep its recurring transaction. 2014-11-20 11:36:53 +01:00
James Cole
61eb5b341d Update piggy banks repetitions when the piggy bank is edited. 2014-11-20 11:19:35 +01:00
James Cole
d758f72393 Better visualisation in the budget charts. 2014-11-20 11:15:00 +01:00
James Cole
1b1367f4c2 Make sure the progress bar has a minimal width to improve readability. 2014-11-20 11:10:42 +01:00
James Cole
07388dd58a Ability to dismiss reminders, added some logic to pre fill transaction form. 2014-11-20 07:51:01 +01:00
James Cole
359c71ef2f Fixed a bug where the reminders would be created inactive. 2014-11-20 07:41:48 +01:00
James Cole
93d4d3df1d Wrong column names. 2014-11-20 07:41:30 +01:00
James Cole
d3a7596be2 A fix for a bug that would prevent both the category and the budget to be updated. 2014-11-20 07:12:41 +01:00
James Cole
114d3812cc Some logging. 2014-11-19 21:22:21 +01:00
James Cole
b2d4dcfbf1 Also catch transaction journal updates. 2014-11-19 21:19:13 +01:00
James Cole
0baf8f6d18 First attempt at testing the home controller. 2014-11-19 21:18:29 +01:00
James Cole
2f8e3a0707 Display bug in recurring transactions. 2014-11-19 21:18:16 +01:00
James Cole
d7a4bf22c6 Scan transaction journals for recurring transactions on store. 2014-11-19 21:18:06 +01:00
James Cole
0c2f9d22b9 With no category, the transaction journal store procedure would fail. 2014-11-19 21:17:21 +01:00
James Cole
e75c5aac49 A fix for the phpunit coverage. 2014-11-19 21:16:48 +01:00
James Cole
77e5024f54 Fixed a display bug. I commented this out, but no idea why. 2014-11-19 21:16:23 +01:00
James Cole
7329c0b200 All of this should contain one working test. 2014-11-18 10:33:38 +01:00
James Cole
0afe3c48a1 Fixed some things so coveralls will fire. 2014-11-18 10:18:50 +01:00
James Cole
1ab5e923bc Removed codeception. 2014-11-18 10:11:31 +01:00
James Cole
90b3bd77e7 Empty tests. 2014-11-18 10:11:18 +01:00
James Cole
9c60443f97 Clean up and added some TODO's. 2014-11-18 09:47:50 +01:00
James Cole
058e5602a3 For consistency, renamed some events. 2014-11-18 09:41:43 +01:00
James Cole
eebac2a66d Some formatting, cleanup, and a new chart. 2014-11-18 09:37:54 +01:00
James Cole
7e8f5c9548 Expanded the selection of reminders. 2014-11-18 01:53:52 +01:00
James Cole
d34b49bd48 Fixed a bug where the end date would be incorrectly calculated. 2014-11-18 01:52:08 +01:00
James Cole
ccffae287d Fixed a bug where the end date would be incorrectly calculated. 2014-11-18 01:51:15 +01:00
James Cole
ad69011ac5 Support 'month' as well as 'monthly'. 2014-11-17 23:09:52 +01:00
James Cole
f8ea0f971d This should fix reminders! 2014-11-17 23:08:36 +01:00
James Cole
9918410954 Implemented a proper way of generating reminders (we hope). 2014-11-17 22:32:55 +01:00
James Cole
4f4e6fac16 Fixed a bug where limits and limit repetitions would not be updated and/or created because of double post calls and missing events. 2014-11-17 21:50:11 +01:00
James Cole
15e99bd672 Removed some old code, added todo's. 2014-11-17 20:55:31 +01:00
James Cole
696e9a6fde Some general cleaning up. 2014-11-17 20:45:55 +01:00
James Cole
36cbb3d71f Added some code for reminders but most of its commented out. 2014-11-17 20:18:14 +01:00
Sander Dorigo
f69598c6aa Lots of work on the reminders. 2014-11-17 16:14:28 +01:00
Sander Dorigo
5fc31f3c1e First attempt at generating and showing reminders. 2014-11-17 10:10:57 +01:00
Sander Dorigo
6581ee0ee0 Fixed some more menu stuff. 2014-11-17 09:40:04 +01:00
James Cole
69e7501d47 Start with cleaning up the menu. 2014-11-17 08:20:05 +01:00
James Cole
314abbea8b Code cleanup. 2014-11-17 07:33:18 +01:00
James Cole
82c9a75578 Expand piggy banks 2014-11-16 22:55:34 +01:00
James Cole
651101912c Route for chart. 2014-11-16 20:05:03 +01:00
James Cole
fb0d463040 Chart for piggy banks 2014-11-16 20:03:53 +01:00
James Cole
8c254554eb Display bug for piggy banks with no reminders. 2014-11-16 10:36:23 +01:00
James Cole
2d9c89375a New chart and lots of stuff for piggy banks 2014-11-16 10:31:19 +01:00
James Cole
61aba29df7 More stuffs. Too lazy to explain. 2014-11-15 11:36:27 +01:00
James Cole
8c949e6190 Events that keep track of piggy bank money add/remove 2014-11-15 09:32:25 +01:00
James Cole
6eb9188690 Cleaned up preferences view. 2014-11-15 09:23:07 +01:00
James Cole
6832f2ebd0 Lots of code cleanup. 2014-11-15 07:46:01 +01:00
James Cole
3e02b50ea1 All kinds of todo items because otherwise I'd forget them. 2014-11-14 19:58:01 +01:00
James Cole
7b7743c03e Fixed some bugs 2014-11-14 19:33:50 +01:00
James Cole
de20563275 Removed all event triggers. 2014-11-14 19:33:40 +01:00
James Cole
9e720c3a38 Some view fixes. 2014-11-14 14:33:41 +01:00
James Cole
54685c1f5f Some new lists for recurring transactions. 2014-11-14 12:54:49 +01:00
James Cole
eb8f8fa935 Expanded on categories. 2014-11-14 11:56:45 +01:00
James Cole
9adbbd872c New chart for budgets. 2014-11-14 11:43:08 +01:00
James Cole
4bd38f97a2 Whoops. But not to worry, changed it already. 2014-11-14 10:41:35 +01:00
James Cole
78ab1e200a Remove transactions now works. 2014-11-14 10:39:34 +01:00
James Cole
11280e473d Catch broken journals. 2014-11-14 10:37:19 +01:00
James Cole
f9750a64f8 Fixed transactions lists. 2014-11-14 10:28:18 +01:00
James Cole
ac2ab65471 Got up to categories with the new tables. 2014-11-14 10:17:12 +01:00
James Cole
0530c0402c First full transaction list (again) and removed some google table references. 2014-11-14 09:34:53 +01:00
James Cole
a58a560bbb Fixed the tables (again) for account and for index. 2014-11-14 09:07:13 +01:00
James Cole
96ab112b22 Removed most, if not all, references to Google Charts (the tables part). 2014-11-14 08:52:58 +01:00
James Cole
b388dcc7d4 First attempt at generating proper paging google tables. However, somehow they are kind of messed up, so I'm probably going to drop this. 2014-11-14 08:40:16 +01:00
James Cole
f511a25c94 Added the input when creating new transactions. 2014-11-13 21:27:50 +01:00
James Cole
58d8b6f95b Fixed the charts, added some todo items. 2014-11-13 21:11:00 +01:00
James Cole
953d68c3a2 Build some transaction handling. 2014-11-13 17:01:09 +01:00
James Cole
9e2f7af59b New route for table. 2014-11-13 16:14:07 +01:00
James Cole
840dfa6696 Expanded recurring transactions. 2014-11-13 16:13:32 +01:00
James Cole
4a20c008ff Implementing recurring transactions. 2014-11-13 11:17:39 +01:00
James Cole
981ffe4194 Added a show piggybank view, removed some others. 2014-11-13 07:50:55 +01:00
James Cole
2597633b0e New migration and code cleanup. 2014-11-13 07:25:47 +01:00
James Cole
71d174d765 Code clean up and reformat. 2014-11-12 22:37:44 +01:00
James Cole
4aa9a04516 Code clean up and reformat. 2014-11-12 22:37:09 +01:00
James Cole
258d6a1688 Code clean up and reformat. 2014-11-12 22:36:02 +01:00
James Cole
2e2c12d6a4 General clean up. 2014-11-12 22:21:48 +01:00
James Cole
44d189d7d3 Fixed a bug where the transaction list was a giant mess. 2014-11-12 21:19:31 +01:00
James Cole
ab508a3d9e Recreated the JSON controller to fix auto-complete forms. 2014-11-12 20:52:34 +01:00
James Cole
4e166c7d2e This should fix the transactions again. 2014-11-12 20:46:55 +01:00
Sander Dorigo
7f175a4870 Some more work on the transactions. 2014-11-12 15:34:32 +01:00
Sander Dorigo
0a627f6f9e More work done for the transaction controller. 2014-11-12 15:22:01 +01:00
Sander Dorigo
d34cc65984 Start cleaning up transactions controller. 2014-11-12 14:38:32 +01:00
Sander Dorigo
78d034d366 There's a giant mix brewing between "old" code, bad code and not implemented exceptions. I suspect the next change will be to cut out all old stuff, throw a lot of NotImplementedExceptions and get going. 2014-11-12 10:54:53 +01:00
James Cole
638fa9005f Excluded more files from the "old" libraries and included new ones instead. This should greatly clean up the code base. 2014-11-12 07:31:48 +01:00
James Cole
5cb9907bf8 Various changes to support the removed old code. 2014-11-11 21:09:56 +01:00
Sander Dorigo
f08fcc36fb Half-way through with some cleaning up. 2014-11-11 18:16:59 +01:00
Sander Dorigo
d231cd9f61 Added the homestead configuration because it's not really secret. 2014-11-11 07:35:00 +01:00
Sander Dorigo
86a586f866 Ignore homestead configuration. 2014-11-11 07:26:16 +01:00
Sander Dorigo
9d4cba1620 Fixed some initial startup bugs when working with Homestead. 2014-11-11 07:20:52 +01:00
Sander Dorigo
e9afd55e9d Updated the default user seeder. 2014-11-10 22:36:25 +01:00
Sander Dorigo
9fa326f630 First attempt at unifying code for categories and budgets, which are basically the same thing. 2014-11-10 21:55:22 +01:00
Sander Dorigo
af9473c126 Fixed some bugs in various controllers and started rebuilding the category controller. 2014-11-10 19:03:03 +01:00
Sander Dorigo
cb08df0770 I broke some stuff but it's fixed again. 2014-11-10 18:39:50 +01:00
Sander Dorigo
d49ca2eb11 Merge branch 'master' of https://github.com/JC5/firefly-iii 2014-11-10 18:39:06 +01:00
Sander Dorigo
a3f8841ec3 I have no idea what's happening! 2014-11-10 18:38:58 +01:00
Sander Dorigo
fd678c286d Some cleanup. 2014-11-10 18:37:25 +01:00
Sander Dorigo
2a3f9b621b Removed unused imports. 2014-11-10 18:36:25 +01:00
Sander Dorigo
359e1b3943 Cleanup all models and migrations. 2014-11-10 18:33:00 +01:00
Sander Dorigo
754336b3cf Merge branch 'develop' of https://github.com/JC5/firefly-iii 2014-11-10 11:40:16 +01:00
James Cole
4918f1c4cb Merge pull request #31 from sonikarc/develop
[Fixes #21] Change the majority of View::share to View::make()->with()
2014-11-10 10:37:49 +01:00
Sander Dorigo
5d5e308942 Updated read me. 2014-11-10 07:55:52 +01:00
Stewart Malik
6a18f81cec Merge branch 'develop' of https://github.com/sonikarc/firefly-iii into develop 2014-11-09 16:01:20 +00:00
Stewart Malik
1ff135d172 [Fixes #21] Change the majority of View::share to View::make()->with() 2014-11-10 02:27:19 +10:30
Sander Dorigo
e666e5e9e3 Clean up composer.json 2014-11-09 11:32:18 +01:00
Sander Dorigo
9874e77ddf Remove ignore instructions. 2014-11-09 11:30:00 +01:00
Sander Dorigo
27e3ec693a New screenshot. 2014-11-09 11:20:54 +01:00
Sander Dorigo
6bb1415ad7 Updated readme file. 2014-11-09 11:10:57 +01:00
Sander Dorigo
83594e6f1f Merge branch 'release/3.1.5' 2014-11-09 11:09:38 +01:00
Sander Dorigo
0f6008705c Added a screenshot 2014-11-09 11:08:53 +01:00
Sander Dorigo
c58b653bb7 Updated read me file. 2014-11-09 11:01:57 +01:00
Sander Dorigo
f69b6f9b4e New chart for budget-overview. 2014-11-09 08:42:09 +01:00
Sander Dorigo
7750b06476 Added a "spent"-bar in budgets. 2014-11-09 08:02:47 +01:00
Sander Dorigo
873384a34b Built the 'show'-view for budgets. 2014-11-08 19:11:51 +01:00
Sander Dorigo
ac299e7279 More rounding. 2014-11-08 11:38:50 +01:00
Sander Dorigo
7895d7f5d0 Format amount. 2014-11-08 11:37:55 +01:00
Sander Dorigo
fe05d218fc Allow piggy bank edit/update. 2014-11-08 11:36:20 +01:00
Sander Dorigo
8196313ac0 Optimized queries. 2014-11-08 10:16:12 +01:00
Sander Dorigo
6d8f84654f Also add stuff not in budgets. 2014-11-08 09:15:03 +01:00
Sander Dorigo
ab4f34a96b Extended reports 2014-11-07 22:06:30 +01:00
Sander Dorigo
139d985904 All kinds of fixes and things. I should really start organizing. 2014-11-07 11:18:06 +01:00
Sander Dorigo
44705f0e18 Some bugfixes and cleanup. 2014-11-06 20:33:37 +01:00
Sander Dorigo
ddea7d696a Delete and update routines. 2014-11-06 07:38:15 +01:00
Sander Dorigo
f814f45e36 Test fix for budgeting. 2014-11-05 21:37:24 +01:00
Sander Dorigo
f7117d47c2 Whoops, bugfix. 2014-11-05 21:23:23 +01:00
Sander Dorigo
01b0a1058d Form fix. 2014-11-05 21:22:31 +01:00
Sander Dorigo
21f362c7b9 Lots of stuff for budgets, accounts and others! 2014-11-05 19:57:56 +01:00
Sander Dorigo
aaab7f8e0e Some fixes for budgets 2014-11-04 20:37:00 +01:00
Sander Dorigo
09e1f68c69 Font fix. 2014-11-02 21:26:41 +01:00
Sander Dorigo
03729aa5ae Chart cleanup 2014-11-02 21:24:50 +01:00
Sander Dorigo
ef39f31ea1 First reports. 2014-11-02 18:46:01 +01:00
Sander Dorigo
0f1437dd6a Delete piggy banks. 2014-11-02 16:47:01 +01:00
Sander Dorigo
03aac2f744 Implemented method stub. 2014-11-02 14:59:09 +01:00
Sander Dorigo
2f8b10e82c All kinds of new code, especially for the piggy banks. 2014-11-02 14:58:12 +01:00
Sander Dorigo
3231effd20 Cleanup and fix everything related to piggy banks. 2014-10-31 07:32:43 +01:00
Sander Dorigo
f7722c1189 Clean up piggy bank controller. 2014-10-30 19:26:52 +01:00
Sander Dorigo
70c2450ac4 Clean up the routes. 2014-10-30 19:26:43 +01:00
Sander Dorigo
2d5b0d0f99 Moved some stuff around. 2014-10-30 19:26:28 +01:00
Sander Dorigo
f0c0002a6d Some cleanup 2014-10-30 18:24:10 +01:00
Sander Dorigo
dd9f08d4fa New code for charts. 2014-10-30 18:12:27 +01:00
Sander Dorigo
de2e384225 Merge branch 'feature/google-charts' into develop
Conflicts:
	app/views/accounts/show.blade.php
2014-10-30 18:09:03 +01:00
Sander Dorigo
ffcd1fde0f Even more charts and tables. 2014-10-30 18:06:29 +01:00
Sander Dorigo
d5e1da5948 Some more stats on the piggy banks. 2014-10-29 12:39:15 +01:00
Sander Dorigo
ad479a5c7f Added some placeholders. 2014-10-29 12:33:19 +01:00
Sander Dorigo
0707603b63 Add buttons and a placeholder. 2014-10-29 12:26:26 +01:00
Sander Dorigo
2f9c383004 All new stufs! 2014-10-29 10:30:52 +01:00
Sander Dorigo
8ad0d7af93 Update views and CSS 2014-10-28 16:29:31 +01:00
Sander Dorigo
9b4391c0bf Initial set of code required for GCharts. 2014-10-28 16:29:24 +01:00
Sander Dorigo
da7802a0a4 New controller, updated composer. 2014-10-28 16:15:16 +01:00
Sander Dorigo
9c69949e8c Added edit and new buttons. 2014-10-28 15:59:10 +01:00
Sander Dorigo
b1d7a9451a Cleaner piggybank view. 2014-10-28 11:25:22 +01:00
Sander Dorigo
004488d453 New todo items. 2014-10-28 11:10:40 +01:00
Sander Dorigo
fc91372dd0 Merge branch 'feature/account-cleanup' into develop 2014-10-28 09:33:54 +01:00
Sander Dorigo
5970a9dc91 Merge branch 'master' of https://github.com/JC5/firefly-iii into feature/account-cleanup 2014-10-28 09:32:22 +01:00
Sander Dorigo
264cac4f9b Merge branch 'develop' of https://github.com/JC5/firefly-iii into feature/account-cleanup 2014-10-28 09:32:16 +01:00
Sander Dorigo
633328a965 Removed references to sankey. 2014-10-28 09:29:47 +01:00
Sander Dorigo
4d4b62a766 Add todo-text 2014-10-28 09:28:14 +01:00
Sander Dorigo
aeb2c7deeb Remove function. 2014-10-28 09:27:46 +01:00
Sander Dorigo
c323942d92 Add placeholder. 2014-10-28 09:27:36 +01:00
Sander Dorigo
a0afa25145 Cleanup and prep-work for new charts (will be a new feature). 2014-10-28 09:25:29 +01:00
Sander Dorigo
4533b46436 Updated menu logic 2014-10-28 06:00:41 +01:00
Sander Dorigo
e5f8db78f9 New views and layouts for the account controller. 2014-10-28 05:59:14 +01:00
Sander Dorigo
899f61671f All new library set for the account controller (and others). 2014-10-28 05:58:48 +01:00
Sander Dorigo
d84d88cc10 Completely revamped the account controller. 2014-10-28 05:58:32 +01:00
Sander Dorigo
97e7ac4052 New routes for accounts. 2014-10-28 05:58:10 +01:00
Sander Dorigo
ba4ffa44d2 Fixed a spelling error. 2014-10-27 21:28:58 +01:00
Sander Dorigo
07caeccf68 Code formatting and a reference to a new form element. 2014-10-27 21:28:30 +01:00
Sander Dorigo
d54832f61f Code formatting. 2014-10-27 21:28:15 +01:00
Sander Dorigo
b212753633 A new form field. 2014-10-27 21:27:45 +01:00
James Cole
f38d80cbf5 Update composer.json 2014-10-23 16:42:11 +02:00
James Cole
8bea1acd8e Update composer.json 2014-10-23 16:42:01 +02:00
Sander Dorigo
42458ce11d Merge branch 'release/3.1.4' 2014-10-14 07:32:00 +02:00
Sander Dorigo
aceb683d07 Remove chart when nothing to show. 2014-10-14 07:27:06 +02:00
Sander Dorigo
b7517b49ed Expand match to include expense account. 2014-10-14 07:24:59 +02:00
Sander Dorigo
849b711b79 Recycle code instead of copy pasting. 2014-10-14 07:24:41 +02:00
Sander Dorigo
25585b28c7 Removed "date in the future" demand. 2014-10-14 07:24:27 +02:00
Sander Dorigo
073da8fb2a Small layout changes. 2014-10-13 20:47:36 +02:00
Sander Dorigo
a787ff3f3c Merge branch 'release/3.1.3' 2014-10-13 19:13:44 +02:00
Sander Dorigo
733b6d7eb7 Expand the chart. 2014-10-13 18:50:37 +02:00
Sander Dorigo
36d8dee853 Building a chart for the recurring transactions. 2014-10-13 17:54:20 +02:00
Sander Dorigo
65a2e07d24 Cleaned out most of the "reminders" code. 2014-10-12 09:45:57 +02:00
Sander Dorigo
7c97c558ab No reference to reminders. 2014-10-12 09:41:10 +02:00
Sander Dorigo
a6bb61050c Remove piggy bank reminders. 2014-10-12 09:41:00 +02:00
Sander Dorigo
b184aa2315 No trigger for recurring transactions. 2014-10-12 09:39:40 +02:00
Sander Dorigo
e4595333e7 Do not destroy reminders since there aren't any. 2014-10-12 09:39:29 +02:00
Sander Dorigo
41dd139bde Trigger no longer fires or creates reminders for piggy banks. 2014-10-12 09:37:31 +02:00
Sander Dorigo
c577dd302a Removed unused reminder methods. 2014-10-12 09:36:24 +02:00
Sander Dorigo
0ab87de78b Removed unused routes. 2014-10-12 09:34:47 +02:00
Sander Dorigo
8a22509b41 Removed a no longer used view. 2014-10-12 09:34:30 +02:00
Sander Dorigo
b024c18441 Cleaned out the reminder controller. 2014-10-12 09:34:10 +02:00
Sander Dorigo
d9ac681a68 Bug fixes in recurring transaction matching. 2014-10-12 09:31:25 +02:00
Sander Dorigo
637a5579ec Small cleanup. 2014-10-12 09:29:19 +02:00
Sander Dorigo
4794156e80 Merge branch 'master' into develop 2014-10-12 08:20:31 +02:00
Sander Dorigo
5f4db7874c New view and what-not for feature. 2014-10-12 08:19:18 +02:00
Sander Dorigo
b4ea1839a5 Fixed some bugs in the recurring transaction match. 2014-10-12 08:03:35 +02:00
Sander Dorigo
6a6d889983 Merge branch 'release/3.1.1' 2014-10-12 07:39:27 +02:00
Sander Dorigo
81662473a6 Merge branch 'release/3.1' 2014-10-11 12:11:17 +02:00
803 changed files with 48943 additions and 27708 deletions

View File

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

16
.env.example Normal file
View File

@@ -0,0 +1,16 @@
APP_ENV=local
APP_DEBUG=true
APP_KEY=SomeRandomString
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
EMAIL_SMTP=
EMAIL_USERNAME=
EMAIL_PASSWORD=

13
.env.testing Normal file
View File

@@ -0,0 +1,13 @@
APP_ENV=testing
APP_DEBUG=true
APP_KEY=SomeRandomString
DB_CONNECTION=sqlite
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file

19
.gitignore vendored
View File

@@ -1,10 +1,6 @@
/bootstrap/compiled.php
/vendor
composer.phar
composer.lock
.env.*.php
.env.php
.DS_Store
Thumbs.db
.idea/
tests/_output/*
@@ -14,3 +10,18 @@ index.html*
app/storage/firefly-export*
.vagrant
firefly-iii-import-*.json
tests/_output/*
testing.sqlite
_ide_helper_models.php
clean.sqlite
tests/acceptance/AcceptanceTester.php
tests/functional/FunctionalTester.php
tests/unit/UnitTester.php
pi.php
tests/_data/db.sqlite
tests/_data/dump.sql
db.sqlite_snapshot
c3.php
db.sqlite-journal
tests/_output/*
.env

View File

@@ -2,12 +2,25 @@ language: php
php:
- 5.5
- 5.4
- hhvm
- 5.6
addons:
code_climate:
repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61
install:
- rm composer.lock
- composer install
- php artisan env
- mv -v .env.testing .env
script:
- ./tests/_data/db.sh
- php vendor/bin/codecept build
- php vendor/bin/codecept run --coverage --coverage-xml --no-exit
after_script:
- cp -v tests/_output/coverage.xml build/logs/clover.xml
- php vendor/bin/coveralls
- vendor/bin/test-reporter --stdout > codeclimate.json
- "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports"

View File

@@ -1,49 +1,55 @@
firefly-iii
Firefly III (v3.3.1)
===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii)
![Still maintained?](http://stillmaintained.com/JC5/firefly-iii.png)
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Test Coverage](https://codeclimate.com/github/JC5/firefly-iii/badges/coverage.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Latest Unstable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/unstable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![License](https://poser.pugx.org/grumpydictator/firefly-iii/license.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
Firefly III is a tool to help you manage your finances. Please read the full description [in the wiki](https://github.com/JC5/firefly-iii/wiki/full-description).
Firefly Mark III is a new version of Firefly built upon best practices and lessons learned
from building [Firefly](https://github.com/JC5/Firefly). It's Mark III since the original Firefly never made it outside of my
laptop and [Firefly II](https://github.com/JC5/Firefly) is live.
## Current features
- [A double-entry bookkeeping system](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system).
- [A double-entry bookkeeping system](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- You can store, edit and remove withdrawals, deposits and transfers. This allows you full financial management;
- It's possible to create, change and manage money using _budgets_;
- Organize transactions using categories;
- Save towards a goal using piggy banks;
- Predict and anticipate large expenses using "repeated expenses" (ie. yearly taxes);
- Predict and anticipate bills using "recurring transactions" (rent for example).
- Predict and anticipate bills using "recurring transactions" (rent for example);
- View basic income / expense reports.
- Lots of help text in case you don't get it;
Everything is organised:
- Clear views that should show you how you're doing;
- Easy navigation through your records;
- Browse back and forth to see previous months or even years;
- Lots of help text in case you don't get it;
- Lots of charts because we all love them.
- Financial reporting showing you how well you are doing;
## Changes
Firefly III will feature, but does not feature yet:
- Financial reporting showing you how well you are doing;
- More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts
- Credit cards
- More test-coverage (aka: actual test coverage);
- More test-coverage;
- Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control.
- Firefly will be able to join transactions.
- Transfers and transactions are combined into one internal datatype which is more consistent with what you're actually doing: moving money from A to B. The fact that A or B or both are yours should not matter.
- Any other features I might not have thought of.
Some stuff has been removed:
@@ -51,14 +57,20 @@ Some stuff has been removed:
- The nesting of budgets, categories and beneficiaries is removed because it was pretty pointless.
- Firefly will not encrypt the content of the (MySQL) tables. Old versions of Firefly had this capability but it sucks when searching, sorting and organizing entries.
## Screenshots
![Index](http://i.imgur.com/TkZNIer.png)
![Accounts](http://i.imgur.com/YE8WavP.png)
![Budgets](http://i.imgur.com/Go0M6Nd.png)
![Reports](http://i.imgur.com/EnEIyQI.png)
## Current state
I have the basics up and running. Test coverage is currently non-existent.
I have the basics up and running. Test coverage is currently coming, slowly.
Although I have not checked extensively, some forms and views have CSRF vulnerabilities. This is because not all
views escape all characters by default. Will be fixed.
The current layout / look & feel is a pretty basic Bootstrap3 template. I am currently working on a more consistent,
expanded layout which will feature shiny AJAX things and data tables and all the Web 3.0 goodies you've come to expect
from social media sites.
Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!
Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!

11
app/Commands/Command.php Normal file
View File

@@ -0,0 +1,11 @@
<?php namespace FireflyIII\Commands;
/**
* Class Command
*
* @package FireflyIII\Commands
*/
abstract class Command
{
}

View File

@@ -0,0 +1,37 @@
<?php namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Foundation\Inspiring;
/**
* Class Inspire
*
* @package FireflyIII\Console\Commands
*/
class Inspire extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display an inspiring quote';
/**
* The console command name.
*
* @var string
*/
protected $name = 'inspire';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->comment(PHP_EOL . Inspiring::quote() . PHP_EOL);
}
}

37
app/Console/Kernel.php Normal file
View File

@@ -0,0 +1,37 @@
<?php namespace FireflyIII\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
/**
* Class Kernel
*
* @package FireflyIII\Console
*/
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands
= [
'FireflyIII\Console\Commands\Inspire',
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
*
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('inspire')
->hourly();
}
}

13
app/Events/Event.php Normal file
View File

@@ -0,0 +1,13 @@
<?php namespace FireflyIII\Events;
/**
* Class Event
*
* @package FireflyIII\Events
*/
abstract class Event
{
//
}

View File

@@ -0,0 +1,20 @@
<?php namespace FireflyIII\Events;
use Illuminate\Queue\SerializesModels;
class JournalDeleted extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
}

View File

@@ -1,14 +1,14 @@
<?php
namespace Firefly\Exception;
namespace FireflyIII\Exceptions;
/**
* Class FireflyException
*
* @package Firefly\Exception
* @package FireflyIII\Exceptions
*/
class FireflyException extends \Exception
{
}
}

View File

@@ -0,0 +1,56 @@
<?php namespace FireflyIII\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
/**
* Class Handler
*
* @package FireflyIII\Exceptions
*/
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport
= [
'Symfony\Component\HttpKernel\Exception\HttpException'
];
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
*
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if ($this->isHttpException($e)) {
return $this->renderHttpException($e);
} else {
return parent::render($request, $e);
}
}
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
*
* @return void
*/
public function report(Exception $e)
{
/** @noinspection PhpInconsistentReturnPointsInspection */
return parent::report($e);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace FireflyIII\Exceptions;
/**
* Class NotImplementedException
*
* @package FireflyIII\Exceptions
*/
class NotImplementedException extends \Exception
{
}

View File

@@ -0,0 +1,12 @@
<?php
namespace FireflyIII\Exceptions;
/**
* Class ValidationExceptions
*
* @package FireflyIII\Exception
*/
class ValidationException extends \Exception
{
}

View File

@@ -0,0 +1,33 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalDeleted;
use Illuminate\Contracts\Queue\ShouldBeQueued;
use Illuminate\Queue\InteractsWithQueue;
class JournalDeletedHandler
{
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalDeleted $event
*
* @return void
*/
public function handle(JournalDeleted $event)
{
//
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace FireflyIII\Helpers\Report;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class ReportHelper
*
* @package FireflyIII\Helpers\Report
*/
class ReportHelper implements ReportHelperInterface
{
/**
* This methods fails to take in account transfers FROM shared accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
*
* @return Collection
*/
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15)
{
$result = $this->_queries->journalsByExpenseAccount($start, $end);
$array = $this->_helper->makeArray($result);
$limited = $this->_helper->limitArray($array, $limit);
return $limited;
}
/**
* @return Carbon
*/
public function firstDate()
{
$journal = Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first();
if ($journal) {
return $journal->date;
}
return Carbon::now();
}
/**
* This method gets some kind of list for a monthly overview.
*
* @param Carbon $date
*
* @return Collection
*/
public function getBudgetsForMonth(Carbon $date)
{
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
// all budgets
$set = \Auth::user()->budgets()
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = $this->_helper->makeArray($set);
$amountSet = $this->_queries->journalsByBudget($start, $end);
$amounts = $this->_helper->makeArray($amountSet);
$combined = $this->_helper->mergeArrays($budgets, $amounts);
$combined[0]['spent'] = isset($combined[0]['spent']) ? $combined[0]['spent'] : 0.0;
$combined[0]['amount'] = isset($combined[0]['amount']) ? $combined[0]['amount'] : 0.0;
$combined[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $this->_queries->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$combined[0]['spent'] += floatval($transfer->amount) * -1;
}
return $combined;
}
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$months = [];
while ($start <= $end) {
$months[] = [
'formatted' => $start->format('F Y'),
'month' => intval($start->format('m')),
'year' => intval($start->format('Y')),
];
$start->addMonth();
}
return $months;
}
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$years = [];
while ($start <= $end) {
$years[] = $start->format('Y');
$start->addYear();
}
return $years;
}
/**
* @param Carbon $date
*
* @return array
*/
public function yearBalanceReport(Carbon $date)
{
$start = clone $date;
$end = clone $date;
$sharedAccounts = [];
$sharedCollection = \Auth::user()->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', '=', 'accountRole')
->where('account_meta.data', '=', json_encode('sharedExpense'))
->get(['accounts.id']);
foreach ($sharedCollection as $account) {
$sharedAccounts[] = $account->id;
}
$accounts = \Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])->filter(
function (Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;
}
return null;
}
);
$report = [];
$start->startOfYear()->subDay();
$end->endOfYear();
foreach ($accounts as $account) {
$report[] = [
'start' => \Steam::balance($account, $start),
'end' => \Steam::balance($account, $end),
'account' => $account,
'shared' => $account->accountRole == 'sharedExpense'
];
}
return $report;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface ReportHelperInterface
*
* @package FireflyIII\Helpers\Report
*/
interface ReportHelperInterface
{
/**
* @return Carbon
*/
public function firstDate();
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function yearBalanceReport(Carbon $date);
/**
* This methods fails to take in account transfers FROM shared accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
*
* @return Collection
*/
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15);
/**
* This method gets some kind of list for a monthly overview.
*
* @param Carbon $date
*
* @return Collection
*/
public function getBudgetsForMonth(Carbon $date);
}

View File

@@ -0,0 +1,510 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 22/02/15
* Time: 18:30
*/
namespace FireflyIII\Helpers\Report;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Steam;
/**
* Class ReportQuery
*
* @package FireflyIII\Helpers\Report
*/
class ReportQuery implements ReportQueryInterface
{
/**
* This query retrieves a list of accounts that are active and not shared.
*
* @return Collection
*/
public function accountList()
{
return Auth::user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole");
}
)
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
->where('active', 1)
->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedExpense"');
$query->orWhereNull('account_meta.data');
}
)
->get(['accounts.*']);
}
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::
leftJoin('transaction_group_transaction_journal', 'transaction_group_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin(
'transaction_group_transaction_journal as otherFromGroup', function (JoinClause $join) {
$join->on('otherFromGroup.transaction_group_id', '=', 'transaction_group_transaction_journal.transaction_group_id')
->on('otherFromGroup.transaction_journal_id', '!=', 'transaction_journals.id');
}
)
->leftJoin('transaction_journals as otherJournals', 'otherJournals.id', '=', 'otherFromGroup.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'otherJournals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'otherJournals.id')
->before($end)->after($start)
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', \Auth::user()->id)
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id)
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->first(
[
DB::Raw('SUM(`transactions`.`amount`) as `amount`')
]
);
$sum = 0;
if (!is_null($set)) {
$sum = floatval($set->amount);
}
return $sum;
}
/**
* Get a users accounts combined with various meta-data related to the start and end date.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end)
{
$set = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
->accountTypeIn(['Default account', 'Asset account', 'Cash account'])
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedExpense"');
$query->orWhereNull('account_meta.data');
}
)
->get(['accounts.*']);
$set->each(
function (Account $account) use ($start, $end) {
/** @noinspection PhpParamsInspection */
$account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, $end);
}
);
return $set;
}
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC')
->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `amount`')]);
return $set;
}
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('transaction_journals.date')
->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
't_to.amount', 'transaction_journals.date', 't_from.account_id as account_id',
'ac_from.name as name']
);
}
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end)
{
return Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('account_meta.data', '!=', '"sharedExpense"')
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC')
->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) AS `spent`')]);
}
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end)
{
return Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('account_meta.data', '!=', '"sharedExpense"')
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('amount')
->get(['categories.id', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `amount`')]);
}
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)
->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('amount', 'DESC')
->get(['t_to.account_id as id', 'ac_to.name as name', DB::Raw('SUM(t_to.amount) as `amount`')]);
}
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('amount')
->get(['t_from.account_id as account_id', 'ac_from.name as name', DB::Raw('SUM(t_from.amount) as `amount`')]);
}
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '"sharedExpense"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', Auth::user()->id)
->get(
['transaction_journals.id', 'transaction_journals.description', 'transactions.account_id', 'accounts.name',
'transactions.amount']
);
}
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->where('account_meta.data', '"sharedExpense"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('categories.name')
->get(
[
'categories.id',
'categories.name as name',
DB::Raw('SUM(`transactions`.`amount`) * -1 AS `amount`')
]
);
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
/**
* Interface ReportQueryInterface
*
* @package FireflyIII\Helpers\Report
*/
interface ReportQueryInterface
{
/**
* This query retrieves a list of accounts that are active and not shared.
*
* @return Collection
*/
public function accountList();
/**
* Get a users accounts combined with various meta-data related to the start and end date.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end);
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end);
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end);
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end);
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end);
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end);
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end);
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end);
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end);
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end);
}

View File

@@ -0,0 +1,193 @@
<?php namespace FireflyIII\Http\Controllers;
use App;
use Auth;
use Carbon\Carbon;
use Config;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Redirect;
use Session;
use View;
/**
* Class AccountController
*
* @package FireflyIII\Http\Controllers
*/
class AccountController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts');
}
/**
* @param string $what
*
* @return \Illuminate\View\View
*/
public function create($what = 'asset')
{
$subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $what);
$subTitle = 'Create a new ' . e($what) . ' account';
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
}
/**
* @param Account $account
*
* @return \Illuminate\View\View
*/
public function delete(Account $account)
{
$subTitle = 'Delete ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
return view('accounts.delete', compact('account', 'subTitle'));
}
/**
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Account $account, AccountRepositoryInterface $repository)
{
$type = $account->accountType->type;
$typeName = Config::get('firefly.shortNamesByFullName.' . $type);
$name = $account->name;
$repository->destroy($account);
Session::flash('success', 'The ' . e($typeName) . ' account "' . e($name) . '" was deleted.');
return Redirect::route('accounts.index', $typeName);
}
public function edit(Account $account, AccountRepositoryInterface $repository)
{
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$openingBalance = $repository->openingBalanceTransaction($account);
// pre fill some useful values.
// the opening balance is tricky:
$openingBalanceAmount = null;
if ($openingBalance) {
$transaction = $openingBalance->transactions()->where('account_id', $account->id)->first();
$openingBalanceAmount = $transaction->amount;
}
$preFilled = [
'accountRole' => $account->getMeta('accountRole'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount
];
Session::flash('preFilled', $preFilled);
return view('accounts.edit', compact('account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
}
/**
* @param string $what
*
* @return View
*/
public function index($what = 'default')
{
$subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what);
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$types = Config::get('firefly.accountTypesByIdentifier.' . $what);
$accounts = Auth::user()->accounts()->accountTypeIn($types)->get(['accounts.*']);
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts'));
}
/**
* @param Account $account
* @param string $range
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\View\View
*/
public function show(Account $account, $range = 'session')
{
/** @var \FireflyIII\Repositories\Account\AccountRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type);
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$journals = $repository->getJournals($account, $page, $range);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
return view('accounts.show', compact('account', 'what', 'range', 'subTitleIcon', 'journals', 'subTitle'));
}
/**
* @param AccountFormRequest $request
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(AccountFormRequest $request, AccountRepositoryInterface $repository)
{
$accountData = [
'name' => $request->input('name'),
'accountType' => $request->input('what'),
'active' => true,
'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'),
'openingBalance' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
];
$account = $repository->store($accountData);
Session::flash('success', 'New account "' . $account->name . '" stored!');
return Redirect::route('accounts.index', $request->input('what'));
}
/**
* @param Account $account
* @param AccountFormRequest $request
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Account $account, AccountFormRequest $request, AccountRepositoryInterface $repository)
{
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$accountData = [
'name' => $request->input('name'),
'active' => $request->input('active'),
'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'),
'openingBalance' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
];
$repository->update($account, $accountData);
Session::flash('success', 'Account "' . $account->name . '" updated.');
return Redirect::route('accounts.index', $what);
}
}

View File

@@ -0,0 +1,46 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Registrar;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
/**
* Class AuthController
*
* @package FireflyIII\Http\Controllers\Auth
*/
class AuthController extends Controller
{
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesAndRegistersUsers;
public $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\Registrar $registrar
*
*/
public function __construct(Guard $auth, Registrar $registrar)
{
$this->auth = $auth;
$this->registrar = $registrar;
$this->middleware('guest', ['except' => 'getLogout']);
}
}

View File

@@ -0,0 +1,48 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Foundation\Auth\ResetsPasswords;
/**
* Class PasswordController
*
* @package FireflyIII\Http\Controllers\Auth
*/
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
protected $redirectPath = '/';
/**
* Create a new password controller instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwords
*
*/
public function __construct(Guard $auth, PasswordBroker $passwords)
{
$this->auth = $auth;
$this->passwords = $passwords;
$this->middleware('guest');
}
}

View File

@@ -0,0 +1,240 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Redirect;
use Session;
use URL;
use View;
/**
* Class BillController
*
* @package FireflyIII\Http\Controllers
*/
class BillController extends Controller
{
public function __construct()
{
View::share('title', 'Bills');
View::share('mainTitleIcon', 'fa-calendar-o');
}
/**
* @return $this
*/
public function create()
{
$periods = \Config::get('firefly.periods_to_text');
return view('bills.create')->with('periods', $periods)->with('subTitle', 'Create new');
}
/**
* @param Bill $bill
*
* @return $this
*/
public function delete(Bill $bill)
{
return view('bills.delete')->with('bill', $bill)->with('subTitle', 'Delete "' . e($bill->name) . '"');
}
/**
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Bill $bill)
{
$bill->delete();
Session::flash('success', 'The bill was deleted.');
return Redirect::route('bills.index');
}
/**
* @param Bill $bill
*
* @return $this
*/
public function edit(Bill $bill)
{
$periods = \Config::get('firefly.periods_to_text');
return view('bills.edit')->with('periods', $periods)->with('bill', $bill)->with('subTitle', 'Edit "' . e($bill->name) . '"');
}
/**
* @param BillRepositoryInterface $repository
*
* @return \Illuminate\View\View
*/
public function index(BillRepositoryInterface $repository)
{
$bills = Auth::user()->bills()->get();
$bills->each(
function (Bill $bill) use ($repository) {
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$last = $bill->transactionjournals()->orderBy('date', 'DESC')->first();
$bill->lastFoundMatch = null;
if ($last) {
$bill->lastFoundMatch = $last->date;
}
}
);
return view('bills.index', compact('bills'));
}
/**
* @param Bill $bill
*
* @return mixed
*/
public function rescan(Bill $bill, BillRepositoryInterface $repository)
{
if (intval($bill->active) == 0) {
Session::flash('warning', 'Inactive bills cannot be scanned.');
return Redirect::intended('/');
}
$set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get(['transaction_journal_id']);
$ids = [];
/** @var Transaction $entry */
foreach ($set as $entry) {
$ids[] = intval($entry->transaction_journal_id);
}
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id',$ids)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$repository->scan($bill, $journal);
}
}
Session::flash('success', 'Rescanned everything.');
return Redirect::to(URL::previous());
}
/**
* @param Bill $bill
*
* @return mixed
*/
public function show(Bill $bill, BillRepositoryInterface $repository)
{
$journals = $bill->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get();
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$hideBill = true;
return view('bills.show', compact('journals', 'hideBill', 'bill'))->with('subTitle', e($bill->name));
}
/**
* @return $this
*/
public function store(BillFormRequest $request, BillRepositoryInterface $repository)
{
var_dump($request->all());
$billData = [
'name' => $request->get('name'),
'match' => $request->get('match'),
'amount_min' => floatval($request->get('amount_min')),
'amount_currency_id' => floatval($request->get('amount_currency_id')),
'amount_max' => floatval($request->get('amount_max')),
'date' => new Carbon($request->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $request->get('repeat_freq'),
'skip' => intval($request->get('skip')),
'automatch' => intval($request->get('automatch')) === 1,
'active' => intval($request->get('active')) === 1,
];
$bill = $repository->store($billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
return Redirect::route('bills.index');
}
/**
* @param Bill $bill
*
* @return $this
*/
public function update(Bill $bill, BillFormRequest $request, BillRepositoryInterface $repository)
{
$billData = [
'name' => $request->get('name'),
'match' => $request->get('match'),
'amount_min' => floatval($request->get('amount_min')),
'amount_currency_id' => floatval($request->get('amount_currency_id')),
'amount_max' => floatval($request->get('amount_max')),
'date' => new Carbon($request->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $request->get('repeat_freq'),
'skip' => intval($request->get('skip')),
'automatch' => intval($request->get('automatch')) === 1,
'active' => intval($request->get('active')) === 1,
];
$bill = $repository->update($bill, $billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" updated.');
return Redirect::route('bills.index');
// $data = Input::except('_token');
// $data['active'] = intval(Input::get('active'));
// $data['automatch'] = intval(Input::get('automatch'));
// $data['user_id'] = Auth::user()->id;
//
// // always validate:
// $messages = $this->_repository->validate($data);
//
// // flash messages:
// Session::flash('warnings', $messages['warnings']);
// Session::flash('successes', $messages['successes']);
// Session::flash('errors', $messages['errors']);
// if ($messages['errors']->count() > 0) {
// Session::flash('error', 'Could not update bill: ' . $messages['errors']->first());
//
// return Redirect::route('bills.edit', $bill->id)->withInput();
// }
//
// // return to update screen:
// if ($data['post_submit_action'] == 'validate_only') {
// return Redirect::route('bills.edit', $bill->id)->withInput();
// }
//
// // update
// $this->_repository->update($bill, $data);
// Session::flash('success', 'Bill "' . e($data['name']) . '" updated.');
//
// // go back to list
// if ($data['post_submit_action'] == 'update') {
// return Redirect::route('bills.index');
// }
//
// // go back to update screen.
// return Redirect::route('bills.edit', $bill->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}

View File

@@ -0,0 +1,222 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Input;
use Preferences;
use Redirect;
use Response;
use Session;
use View;
/**
* Class BudgetController
*
* @package FireflyIII\Http\Controllers
*/
class BudgetController extends Controller
{
public function __construct()
{
View::share('title', 'Budgets');
View::share('mainTitleIcon', 'fa-tasks');
}
/**
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function amount(Budget $budget, BudgetRepositoryInterface $repository)
{
$amount = intval(Input::get('amount'));
$date = Session::get('start', Carbon::now()->startOfMonth());
$limitRepetition = $repository->updateLimitAmount($budget, $date, $amount);
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0]);
}
/**
* @return $this
*/
public function create()
{
return view('budgets.create')->with('subTitle', 'Create a new budget');
}
/**
* @param Budget $budget
*
* @return \Illuminate\View\View
*/
public function delete(Budget $budget)
{
$subTitle = 'Delete budget' . e($budget->name) . '"';
return view('budgets.delete', compact('budget', 'subTitle'));
}
/**
* @param Budget $budget
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Budget $budget, BudgetRepositoryInterface $repository)
{
$name = $budget->name;
$repository->destroy($budget);
Session::flash('success', 'The budget "' . e($name) . '" was deleted.');
return Redirect::route('budgets.index');
}
/**
* @param Budget $budget
*
* @return $this
*/
public function edit(Budget $budget)
{
$subTitle = 'Edit budget "' . e($budget->name) . '"';
return view('budgets.edit', compact('budget', 'subTitle'));
}
/**
* @return mixed
*/
public function index(BudgetRepositoryInterface $repository)
{
$budgets = Auth::user()->budgets()->get();
// loop the budgets:
$budgets->each(
function (Budget $budget) use ($repository) {
$date = Session::get('start', Carbon::now()->startOfMonth());
$budget->spent = $repository->spentInMonth($budget, $date);
$budget->currentRep = $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']);
}
);
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
$spent = $budgets->sum('spent');
$amount = Preferences::get('budgetIncomeTotal' . $date, 1000)->data;
$overspent = $spent > $amount;
$spentPCT = $overspent ? ceil($amount / $spent * 100) : ceil($spent / $amount * 100);
$budgetMax = Preferences::get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data;
return view('budgets.index', compact('budgetMaximum', 'budgets', 'spent', 'spentPCT', 'overspent', 'amount'));
}
/**
* @return \Illuminate\View\View
*/
public function noBudget()
{
$start = \Session::get('start', Carbon::now()->startOfMonth());
$end = \Session::get('end', Carbon::now()->startOfMonth());
$list = Auth::user()
->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('budget_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a budget in ' . $start->format('F Y');
return view('budgets.noBudget', compact('list', 'subTitle'));
}
/**
* @return mixed
*/
public function postUpdateIncome()
{
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
Preferences::set('budgetIncomeTotal' . $date, intval(Input::get('amount')));
return Redirect::route('budgets.index');
}
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
'name' => $request->input('name'),
'user' => Auth::user()->id,
];
$budget = $repository->store($budgetData);
Session::flash('success', 'New budget "' . $budget->name . '" stored!');
return Redirect::route('budgets.index');
}
/**
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Illuminate\View\View
*/
public function show(Budget $budget, LimitRepetition $repetition = null, BudgetRepositoryInterface $repository)
{
if (!is_null($repetition->id) && $repetition->budgetLimit->budget->id != $budget->id) {
return view('error')->with('message', 'Invalid selection.');
}
$hideBudget = true; // used in transaction list.
$journals = $repository->getJournals($budget, $repetition);
$limits = !is_null($repetition->id) ? [$repetition->budgetLimit] : $budget->budgetLimits()->orderBy('startdate', 'DESC')->get();
$subTitle = !is_null($repetition->id) ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget'));
}
/**
* @param Budget $budget
* @param BudgetFormRequest $request
* @param BudgetRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Budget $budget, BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
'name' => $request->input('name'),
];
$repository->update($budget, $budgetData);
Session::flash('success', 'Budget "' . $budget->name . '" updated.');
return Redirect::route('budgets.index');
}
/**
* @return $this
*/
public function updateIncome()
{
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
$budgetAmount = Preferences::get('budgetIncomeTotal' . $date, 1000);
return view('budgets.income')->with('amount', $budgetAmount);
}
}

View File

@@ -0,0 +1,172 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Redirect;
use Session;
use View;
use Input;
/**
* Class CategoryController
*
* @package FireflyIII\Http\Controllers
*/
class CategoryController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Categories');
View::share('mainTitleIcon', 'fa-bar-chart');
}
/**
* @return $this
*/
public function create()
{
return view('categories.create')->with('subTitle', 'Create a new category');
}
/**
* @param Category $category
*
* @return $this
*/
public function show(Category $category, CategoryRepositoryInterface $repository)
{
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
$offset = $page > 0 ? $page * 50 : 0;
$set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get(['transaction_journals.*']);
$count = $category->transactionJournals()->count();
$journals = new LengthAwarePaginator($set, $count, 50, $page);
return view('categories.show', compact('category', 'journals', 'hideCategory'));
}
/**
* @return \Illuminate\View\View
*/
public function noCategory()
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->startOfMonth());
$list = Auth::user()
->transactionjournals()
->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a category in ' . $start->format('F Y');
return view('categories.noCategory', compact('list', 'subTitle'));
}
/**
* @param Category $category
*
* @return \Illuminate\View\View
*/
public function delete(Category $category)
{
$subTitle = 'Delete category' . e($category->name) . '"';
return view('categories.delete', compact('category', 'subTitle'));
}
/**
* @param Category $category
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Category $category, CategoryRepositoryInterface $repository)
{
$name = $category->name;
$repository->destroy($category);
Session::flash('success', 'The category "' . e($name) . '" was deleted.');
return Redirect::route('categories.index');
}
/**
* @param Category $category
*
* @return $this
*/
public function edit(Category $category)
{
$subTitle = 'Edit category "' . e($category->name) . '"';
return view('categories.edit', compact('category', 'subTitle'));
}
/**
* @return $this
*/
public function index()
{
$categories = Auth::user()->categories()->get();
return view('categories.index', compact('categories'));
}
/**
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository
*
* @return mixed
*/
public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository)
{
$categoryData = [
'name' => $request->input('name'),
'user' => Auth::user()->id,
];
$category = $repository->store($categoryData);
Session::flash('success', 'New category "' . $category->name . '" stored!');
return Redirect::route('categories.index');
}
/**
* @param Category $category
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Category $category, CategoryFormRequest $request, CategoryRepositoryInterface $repository)
{
$categoryData = [
'name' => $request->input('name'),
];
$repository->update($category, $categoryData);
Session::flash('success', 'Category "' . $category->name . '" updated.');
return Redirect::route('categories.index');
}
}

View File

@@ -0,0 +1,17 @@
<?php namespace FireflyIII\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
/**
* Class Controller
*
* @package FireflyIII\Http\Controllers
*/
abstract class Controller extends BaseController
{
use DispatchesCommands, ValidatesRequests;
}

View File

@@ -0,0 +1,172 @@
<?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\CurrencyFormRequest;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use Preferences;
use Redirect;
use Session;
use View;
use Cache;
/**
* Class CurrencyController
*
* @package FireflyIII\Http\Controllers
*/
class CurrencyController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Currencies');
View::share('mainTitleIcon', 'fa-usd');
}
/**
* @return \Illuminate\View\View
*/
public function create()
{
$subTitleIcon = 'fa-plus';
$subTitle = 'Create a new currency';
return view('currency.create', compact('subTitleIcon', 'subTitle'));
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse
*/
public function defaultCurrency(TransactionCurrency $currency)
{
$currencyPreference = Preferences::get('currencyPreference', 'EUR');
$currencyPreference->data = $currency->code;
$currencyPreference->save();
Session::flash('success', $currency->name . ' is now the default currency.');
Cache::forget('FFCURRENCYSYMBOL');
Cache::forget('FFCURRENCYCODE');
return Redirect::route('currency.index');
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function delete(TransactionCurrency $currency)
{
if ($currency->transactionJournals()->count() > 0) {
Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.');
return Redirect::route('currency.index');
}
return view('currency.delete', compact('currency'));
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(TransactionCurrency $currency)
{
if ($currency->transactionJournals()->count() > 0) {
Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.');
return Redirect::route('currency.index');
}
Session::flash('success', 'Currency "' . e($currency->name) . '" deleted');
$currency->delete();
return Redirect::route('currency.index');
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\View\View
*/
public function edit(TransactionCurrency $currency)
{
$subTitleIcon = 'fa-pencil';
$subTitle = 'Edit currency "' . e($currency->name) . '"';
$currency->symbol = htmlentities($currency->symbol);
return view('currency.edit', compact('currency', 'subTitle', 'subTitleIcon'));
}
/**
* @return \Illuminate\View\View
*/
public function index()
{
$currencies = TransactionCurrency::get();
$currencyPreference = Preferences::get('currencyPreference', 'EUR');
$defaultCurrency = TransactionCurrency::whereCode($currencyPreference->data)->first();
return view('currency.index', compact('currencies', 'defaultCurrency'));
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store(CurrencyFormRequest $request)
{
// no repository, because the currency controller is relatively simple.
$currency = TransactionCurrency::create(
[
'name' => $request->get('name'),
'code' => $request->get('code'),
'symbol' => $request->get('symbol'),
]
);
Session::flash('success', 'Currency "' . $currency->name . '" created');
return Redirect::route('currency.index');
}
/**
* @param TransactionCurrency $currency
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(TransactionCurrency $currency, CurrencyFormRequest $request)
{
$currency->code = $request->get('code');
$currency->symbol = $request->get('symbol');
$currency->name = $request->get('name');
$currency->save();
Session::flash('success', 'Currency "' . e($currency->namename) . '" updated.');
return Redirect::route('currency.index');
}
}

View File

@@ -0,0 +1,635 @@
<?php namespace FireflyIII\Http\Controllers;
use App;
use Auth;
use Carbon\Carbon;
use Crypt;
use Exception;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
use Navigation;
use Preferences;
use Response;
use Session;
use DB;
use Steam;
/**
* Class GoogleChartController
*
* @package FireflyIII\Http\Controllers
*/
class GoogleChartController extends Controller
{
/**
* @param Account $account
* @param string $view
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountBalanceChart(Account $account, $view = 'session', GChart $chart)
{
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$count = $account->transactions()->count();
if ($view == 'all' && $count > 0) {
$first = $account->transactions()->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')->orderBy(
'date', 'ASC'
)->first(['transaction_journals.date']);
$last = $account->transactions()->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')->orderBy(
'date', 'DESC'
)->first(['transaction_journals.date']);
$start = new Carbon($first->date);
$end = new Carbon($last->date);
}
$current = clone $start;
while ($end >= $current) {
$chart->addRow(clone $current, Steam::balance($account, $current), false);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allAccountsBalanceChart(GChart $chart)
{
$chart->addColumn('Day of the month', 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
if ($frontPage->data == []) {
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
} else {
$accounts = Auth::user()->accounts()->whereIn('id', $frontPage->data)->get(['accounts.*']);
}
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param int $year
*
* @return $this|\Illuminate\Http\JsonResponse
*/
public function allBudgetsAndSpending($year, GChart $chart, BudgetRepositoryInterface $repository)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$budgets = Auth::user()->budgets()->get();
$budgets->sortBy('name');
$chart->addColumn('Month', 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$spent = $repository->spentInMonth($budget, $start);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allBudgetsHomeChart(GChart $chart)
{
$chart->addColumn('Budget', 'string');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$budgets = Auth::user()->budgets()->orderBy('name', 'DESC')->get();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
/** @var Budget $budget */
foreach ($budgets as $budget) {
/** @var \LimitRepetition $repetition */
$repetition = LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->first(['limit_repetitions.*']);
if (is_null($repetition)) { // use the session start and end for our search query
$searchStart = $start;
$searchEnd = $end;
$limit = 0; // the limit is zero:
} else {
// use the limit's start and end for our search query
$searchStart = $repetition->startdate;
$searchEnd = $repetition->enddate;
$limit = floatval($repetition->amount); // the limit is the repetitions limit:
}
$expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name, $limit, $expenses);
}
}
$noBudgetSet = Auth::user()
->transactionjournals()
->whereNotIn(
'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
$query
->select('transaction_journals.id')
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'));
}
)
->before($end)
->after($start)
->lessThan(0)
->transactionTypes(['Withdrawal'])
->get();
$sum = $noBudgetSet->sum('amount') * -1;
$chart->addRow('No budget', 0, $sum);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allCategoriesHomeChart(GChart $chart)
{
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
// query!
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = TransactionJournal::leftJoin(
'transactions',
function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end)
->after($start)
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('sum', 'DESC')
->get(['categories.id', 'categories.name', \DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
foreach ($set as $entry) {
$entry->name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$chart->addRow($entry->name, floatval($entry->sum));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Bill $bill
*
* @return \Illuminate\Http\JsonResponse
*/
public function billOverview(Bill $bill, GChart $chart)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number');
$chart->addColumn('Min amount', 'number');
$chart->addColumn('Current entry', 'number');
// get first transaction or today for start:
$first = $bill->transactionjournals()->orderBy('date', 'ASC')->first();
if ($first) {
$start = $first->date;
} else {
$start = new Carbon;
}
$end = new Carbon;
while ($start <= $end) {
$result = $bill->transactionjournals()->before($end)->after($start)->first();
if ($result) {
/** @var Transaction $tr */
foreach ($result->transactions()->get() as $tr) {
if (floatval($tr->amount) > 0) {
$amount = floatval($tr->amount);
}
}
} else {
$amount = 0;
}
unset($result);
$chart->addRow(clone $start, $bill->amount_max, $bill->amount_min, $amount);
$start = Navigation::addPeriod($start, $bill->repeat_freq, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billsOverview(GChart $chart)
{
$paid = ['items' => [], 'amount' => 0];
$unpaid = ['items' => [], 'amount' => 0];
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
$set = Bill::
leftJoin(
'transaction_journals', function (JoinClause $join) use ($start, $end) {
$join->on('bills.id', '=', 'transaction_journals.bill_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'));
}
)
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '>', 0);
}
)
->where('active', 1)
->groupBy('bills.id')
->get(
['bills.id', 'bills.name', 'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_journals.id as journalId',
\DB::Raw('SUM(`bills`.`amount_min` + `bills`.`amount_max`) / 2 as `averageAmount`'),
'transactions.amount AS actualAmount']
);
foreach ($set as $entry) {
if (intval($entry->journalId) == 0) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += floatval($entry->averageAmount);
} else {
$description = intval($entry->encrypted) == 1 ? Crypt::decrypt($entry->description) : $entry->description;
$paid['items'][] = $description;
$paid['amount'] += floatval($entry->actualAmount);
}
}
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate();
return Response::json($chart->getData());
}
/**
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetLimitSpending(Budget $budget, LimitRepetition $repetition, GChart $chart)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$chart->addColumn('Day', 'date');
$chart->addColumn('Left', 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($start)->sum('amount'));
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
*
* @param Budget $budget
*
* @param int $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetsAndSpending(Budget $budget, $year = 0)
{
$chart = App::make('Grumpydictator\Gchart\GChart');
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepository');
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
if ($year == 0) {
// grab the first budgetlimit ever:
$firstLimit = $budget->budgetlimits()->orderBy('startdate', 'ASC')->first();
if ($firstLimit) {
$start = new Carbon($firstLimit->startdate);
} else {
$start = Carbon::now()->startOfYear();
}
// grab the last budget limit ever:
$lastLimit = $budget->budgetlimits()->orderBy('startdate', 'DESC')->first();
if ($lastLimit) {
$end = new Carbon($lastLimit->startdate);
} else {
$end = Carbon::now()->endOfYear();
}
} else {
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
}
while ($start <= $end) {
$spent = $repository->spentInMonth($budget, $start);
$repetition = LimitRepetition::leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->first(['limit_repetitions.*']);
if ($repetition) {
$budgeted = floatval($repetition->amount);
\Log::debug('Found a repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name . '!');
} else {
\Log::debug('No repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name);
$budgeted = null;
}
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
*
* @param Category $category
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function categoriesAndSpending(Category $category, $year, GChart $chart)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
$spent = floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
$budgeted = null;
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\JsonResponse
*/
public function piggyBankHistory(PiggyBank $piggyBank, GChart $chart)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number');
$set = \DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]);
foreach ($set as $entry) {
$chart->addRow(new Carbon($entry->date), floatval($entry->sum));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
*
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExp($year, GChart $chart, ReportQueryInterface $query)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
// get report query interface.
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$income = $query->incomeByPeriod($start, $currentEnd);
$incomeSum = 0;
foreach ($income as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses:
$expense = $query->journalsByExpenseAccount($start, $currentEnd);
$expenseSum = 0;
foreach ($expense as $entry) {
$expenseSum += floatval($entry->amount);
}
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
*
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExpSum($year, GChart $chart, ReportQueryInterface $query)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$income = 0;
$expense = 0;
$count = 0;
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$incomeResult = $query->incomeByPeriod($start, $currentEnd);
$incomeSum = 0;
foreach ($incomeResult as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses:
$expenseResult = $query->journalsByExpenseAccount($start, $currentEnd);
$expenseSum = 0;
foreach ($expenseResult as $entry) {
$expenseSum += floatval($entry->amount);
}
$income += $incomeSum;
$expense += $expenseSum;
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,94 @@
<?php namespace FireflyIII\Http\Controllers;
use ErrorException;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
use League\CommonMark\CommonMarkConverter;
use Route;
use Response;
use Cache;
/**
* Class HelpController
*
* @package FireflyIII\Http\Controllers
*/
class HelpController extends Controller {
/**
* @param $route
*
* @return \Illuminate\Http\JsonResponse
*/
public function show($route)
{
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => 'Help',
];
if (!Route::has($route)) {
\Log::error('No such route: ' . $route);
return Response::json($content);
}
if ($this->_inCache($route)) {
$content = [
'text' => Cache::get('help.' . $route . '.text'),
'title' => Cache::get('help.' . $route . '.title'),
];
return Response::json($content);
}
$content = $this->_getFromGithub($route);
Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week.
Cache::put('help.' . $route . '.title', $content['title'], 10080);
return Response::json($content);
}
/**
* @param $route
*
* @return bool
*/
protected function _inCache($route)
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
}
/**
* @param $route
*
* @return array
*/
protected function _getFromGithub($route)
{
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => $route,
];
try {
$content['text'] = file_get_contents($uri);
} catch (ErrorException $e) {
\Log::error(trim($e->getMessage()));
}
if (strlen(trim($content['text'])) == 0) {
$content['text'] = '<p>There is no help for this route.</p>';
}
$converter = new CommonMarkConverter();
$content['text'] = $converter->convertToHtml($content['text']);
return $content;
}
}

View File

@@ -0,0 +1,124 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Cache;
use Carbon\Carbon;
use Navigation;
use Preferences;
use Redirect;
use Session;
use URL;
/**
* Class HomeController
*
* @package FireflyIII\Http\Controllers
*/
class HomeController extends Controller
{
/**
*
*/
public function __construct()
{
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function flush()
{
Cache::flush();
return Redirect::route('index');
}
/**
* @return \Illuminate\View\View
*/
public function index()
{
$count = Auth::user()->accounts()->accountTypeIn(['Asset account', 'Default account'])->count();
$title = 'Firefly';
$subTitle = 'What\'s playing?';
$mainTitleIcon = 'fa-fire';
$transactions = [];
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
if ($frontPage->data == []) {
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
} else {
$accounts = Auth::user()->accounts()->whereIn('id', $frontPage->data)->get(['accounts.*']);
}
foreach ($accounts as $account) {
$set = Auth::user()
->transactionjournals()
->with(['transactions', 'transactioncurrency', 'transactiontype'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $account->id)
->where('date', '>=', $start->format('Y-m-d'))
->where('date', '<=', $end->format('Y-m-d'))
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->take(10)
->get(['transaction_journals.*']);
if (count($set) > 0) {
$transactions[] = [$set, $account];
}
}
// var_dump($transactions);
return view('index', compact('count', 'title', 'subTitle', 'mainTitleIcon', 'transactions'));
}
/**
* @param string $range
*
* @return mixed
*/
public function rangeJump($range)
{
$valid = ['1D', '1W', '1M', '3M', '6M', '1Y',];
if (in_array($range, $valid)) {
Preferences::set('viewRange', $range);
Session::forget('range');
}
return Redirect::to(URL::previous());
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function sessionNext()
{
$range = Session::get('range');
$start = Session::get('start');
Session::put('start', Navigation::jumpToNext($range, clone $start));
return Redirect::to(URL::previous());
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function sessionPrev()
{
$range = Session::get('range');
$start = Session::get('start');
Session::put('start', Navigation::jumpToPrevious($range, clone $start));
return Redirect::to(URL::previous());
}
}

View File

@@ -0,0 +1,68 @@
<?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Response;
use Auth;
/**
* Class JsonController
*
* @package FireflyIII\Http\Controllers
*/
class JsonController extends Controller {
/**
* Returns a list of categories.
*
* @return \Illuminate\Http\JsonResponse
*/
public function categories()
{
$list = Auth::user()->categories()->orderBy('name','ASC')->get();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
}
return Response::json($return);
}
/**
* Returns a JSON list of all beneficiaries.
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseAccounts()
{
$list = Auth::user()->accounts()->accountTypeIn(['Expense account', 'Beneficiary account'])->get();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
}
return Response::json($return);
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function revenueAccounts()
{
$list = Auth::user()->accounts()->accountTypeIn(['Revenue account'])->get();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
}
return Response::json($return);
}
}

View File

@@ -0,0 +1,319 @@
<?php namespace FireflyIII\Http\Controllers;
use Amount;
use Auth;
use Carbon\Carbon;
use Config;
use ExpandedForm;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\PiggyBankFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
use Redirect;
use Session;
use Steam;
use View;
/**
* Class PiggyBankController
*
* @package FireflyIII\Http\Controllers
*/
class PiggyBankController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
/**
* Add money to piggy bank
*
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function add(PiggyBank $piggyBank, AccountRepositoryInterface $repository)
{
$leftOnAccount = $repository->leftOnAccount($piggyBank->account);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave);
\Log::debug('Now going to view for piggy bank #' . $piggyBank->id . ' (' . $piggyBank->name . ')');
return view('piggy-banks.add', compact('piggyBank', 'maxAmount'));
}
/**
* @return mixed
*/
public function create()
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
$subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus';
return view('piggy-banks.create', compact('accounts', 'periods', 'subTitle', 'subTitleIcon'));
}
/**
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function delete(PiggyBank $piggyBank)
{
$subTitle = 'Delete "' . e($piggyBank->name) . '"';
return view('piggy_banks.delete', compact('piggyBank', 'subTitle'));
}
/**
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(PiggyBank $piggyBank)
{
Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$this->_repository->destroy($piggyBank);
return Redirect::route('piggy_banks.index');
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function edit(PiggyBank $piggyBank)
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
$subTitle = 'Edit piggy bank "' . e($piggyBank->name) . '"';
$subTitleIcon = 'fa-pencil';
/*
* Flash some data to fill the form.
*/
if (is_null($piggyBank->targetdate) || $piggyBank->targetdate == '') {
$targetDate = null;
} else {
$targetDate = new Carbon($piggyBank->targetdate);
$targetDate = $targetDate->format('Y-m-d');
}
$preFilled = ['name' => $piggyBank->name,
'account_id' => $piggyBank->account_id,
'targetamount' => $piggyBank->targetamount,
'targetdate' => $targetDate,
'reminder' => $piggyBank->reminder,
'remind_me' => intval($piggyBank->remind_me) == 1 || !is_null($piggyBank->reminder) ? true : false
];
Session::flash('preFilled', $preFilled);
return view('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'accounts', 'periods', 'preFilled'));
}
/**
* @return $this
*/
public function index(AccountRepositoryInterface $repository)
{
/** @var Collection $piggyBanks */
$piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->get();
$accounts = [];
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$piggyBank->savedSoFar = floatval($piggyBank->currentRelevantRep()->currentamount);
$piggyBank->percentage = intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100);
$piggyBank->leftToSave = $piggyBank->targetamount - $piggyBank->savedSoFar;
/*
* Fill account information:
*/
$account = $piggyBank->account;
if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [
'name' => $account->name,
'balance' => Steam::balance($account),
'leftForPiggyBanks' => $repository->leftOnAccount($account),
'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount),
'leftToSave' => $piggyBank->leftToSave
];
} else {
$accounts[$account->id]['sumOfSaved'] += $piggyBank->savedSoFar;
$accounts[$account->id]['sumOfTargets'] += floatval($piggyBank->targetamount);
$accounts[$account->id]['leftToSave'] += $piggyBank->leftToSave;
}
}
return view('piggy-banks.index', compact('piggyBanks', 'accounts'));
}
/**
* POST add money to piggy bank
*
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postAdd(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{
$amount = round(floatval(Input::get('amount')), 2);
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);
if ($amount <= $maxAmount) {
$repetition = $piggyBank->currentRelevantRep();
$repetition->currentamount += $amount;
$repetition->save();
/*
* Create event!
*/
//Event::fire('piggy_bank.addMoney', [$piggyBank, $amount]); // new and used.
Session::flash('success', 'Added ' . Amount::format($amount, false) . ' to "' . e($piggyBank->name) . '".');
} else {
Session::flash('error', 'Could not add ' . Amount::format($amount, false) . ' to "' . e($piggyBank->name) . '".');
}
return Redirect::route('piggy-banks.index');
}
/**
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postRemove(PiggyBank $piggyBank)
{
$amount = floatval(Input::get('amount'));
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
if ($amount <= $savedSoFar) {
$repetition = $piggyBank->currentRelevantRep();
$repetition->currentamount -= $amount;
$repetition->save();
/*
* Create event!
*/
//Event::fire('piggy_bank.removeMoney', [$piggyBank, $amount]); // new and used.
Session::flash('success', 'Removed ' . Amount::format($amount, false) . ' from "' . e($piggyBank->name) . '".');
} else {
Session::flash('error', 'Could not remove ' . Amount::format($amount, false) . ' from "' . e($piggyBank->name) . '".');
}
return Redirect::route('piggy-banks.index');
}
/**
* @param PiggyBank $piggyBank
*
* @SuppressWarnings("Unused")
*
* @return \Illuminate\View\View
*/
public function remove(PiggyBank $piggyBank)
{
return view('piggy-banks.remove', compact('piggyBank'));
}
/**
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function show(PiggyBank $piggyBank)
{
$events = $piggyBank->piggyBankEvents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get();
/*
* Number of reminders:
*/
$subTitle = e($piggyBank->name);
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle'));
}
/**
*
*/
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'repeats' => false,
'name' => $request->get('name'),
'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'reminder' => $request->get('reminder'),
];
$piggyBank = $repository->store($piggyBankData);
Session::flash('success', 'Stored piggy bank "' . e($piggyBank->name) . '".');
return Redirect::route('piggy-banks.index');
}
/**
* @param PiggyBank $piggyBank
*
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @return $this
*/
public function update(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request)
{
$piggyBankData = [
'repeats' => false,
'name' => $request->get('name'),
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'reminder' => $request->get('reminder'),
];
$piggyBank = $repository->update($piggyBank, $piggyBankData);
Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".');
return Redirect::route('piggy-banks.index');
}
}

View File

@@ -0,0 +1,76 @@
<?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
use View;
use Auth;
use Preferences;
use Input;
use Session;
use Redirect;
/**
* Class PreferencesController
*
* @package FireflyIII\Http\Controllers
*/
class PreferencesController extends Controller {
/**
*
*/
public function __construct()
{
View::share('title', 'Preferences');
View::share('mainTitleIcon', 'fa-gear');
}
/**
* @return $this|\Illuminate\View\View
*/
public function index()
{
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
$viewRange = Preferences::get('viewRange', '1M');
$viewRangeValue = $viewRange->data;
$frontPage = Preferences::get('frontPageAccounts', []);
$budgetMax = Preferences::get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data;
return view('preferences.index', compact('budgetMaximum'))->with('accounts', $accounts)->with('frontPageAccounts', $frontPage)->with(
'viewRange', $viewRangeValue
);
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function postIndex()
{
// front page accounts
$frontPageAccounts = [];
foreach (Input::get('frontPageAccounts') as $id) {
$frontPageAccounts[] = intval($id);
}
Preferences::set('frontPageAccounts', $frontPageAccounts);
// view range:
Preferences::set('viewRange', Input::get('viewRange'));
// forget session values:
Session::forget('start');
Session::forget('end');
Session::forget('range');
// budget maximum:
$budgetMaximum = intval(Input::get('budgetMaximum'));
Preferences::set('budgetMaximum', $budgetMaximum);
Session::flash('success', 'Preferences saved!');
return Redirect::route('preferences');
}
}

View File

@@ -0,0 +1,90 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\ProfileFormRequest;
use Hash;
use Redirect;
use Session;
/**
* Class ProfileController
*
* @package FireflyIII\Http\Controllers
*/
class ProfileController extends Controller
{
/**
* @return \Illuminate\View\View
*/
public function changePassword()
{
return view('profile.change-password')->with('title', Auth::user()->email)->with('subTitle', 'Change your password')->with(
'mainTitleIcon', 'fa-user'
);
}
/**
* @return \Illuminate\View\View
*
*/
public function index()
{
return view('profile.index')->with('title', 'Profile')->with('subTitle', Auth::user()->email)->with('mainTitleIcon', 'fa-user');
}
/**
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function postChangePassword(ProfileFormRequest $request)
{
// old, new1, new2
if (!Hash::check($request->get('current_password'), Auth::user()->password)) {
Session::flash('error', 'Invalid current password!');
return Redirect::route('change-password');
}
$result = $this->_validatePassword($request->get('current_password'), $request->get('new_password'), $request->get('new_password_confirmation'));
if (!($result === true)) {
Session::flash('error', $result);
return Redirect::route('change-password');
}
// update the user with the new password.
Auth::user()->password = $request->get('new_password');
Auth::user()->save();
Session::flash('success', 'Password changed!');
return Redirect::route('profile');
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param string $old
* @param string $new1
* @param string $new2
*
* @return string|bool
*/
protected function _validatePassword($old, $new1, $new2)
{
if (strlen($new1) == 0 || strlen($new2) == 0) {
return 'Do fill in a password!';
}
if ($new1 == $old) {
return 'The idea is to change your password.';
}
if ($new1 !== $new2) {
return 'New passwords do not match!';
}
return true;
}
}

View File

@@ -0,0 +1,156 @@
<?php namespace FireflyIII\Http\Controllers;
use Amount;
use Auth;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection;
use Response;
use Input;
/**
* Class RelatedController
*
* @package FireflyIII\Http\Controllers
*/
class RelatedController extends Controller
{
/**
*
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
*/
public function alreadyRelated(TransactionJournal $journal)
{
$ids = [];
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $loopJournal */
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id != $journal->id) {
$ids[] = $loopJournal->id;
}
}
}
$unique = array_unique($ids);
if (count($unique) > 0) {
$set = Auth::user()->transactionjournals()->whereIn('id', $unique)->get();
$set->each(
function (TransactionJournal $journal) {
/** @var Transaction $t */
foreach ($journal->transactions()->get() as $t) {
if ($t->amount > 0) {
$journal->amount = $t->amount;
}
}
}
);
return Response::json($set->toArray());
} else {
return Response::json((new Collection)->toArray());
}
}
/**
* @param TransactionJournal $parentJournal
* @param TransactionJournal $childJournal
*
* @return \Illuminate\Http\JsonResponse
*/
public function relate(TransactionJournal $parentJournal, TransactionJournal $childJournal)
{
$group = new TransactionGroup;
$group->relation = 'balance';
$group->user_id = Auth::user()->id;
$group->save();
$group->transactionjournals()->save($parentJournal);
$group->transactionjournals()->save($childJournal);
return Response::json(true);
}
/**
* @param TransactionJournal $journal
*
* @return \Illuminate\View\View
*/
public function related(TransactionJournal $journal)
{
$groups = $journal->transactiongroups()->get();
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($groups as $group) {
/** @var TransactionJournal $loopJournal */
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id != $journal->id) {
$members->push($loopJournal);
}
}
}
return view('related.relate', compact('journal', 'members'));
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param TransactionJournal $parentJournal
* @param TransactionJournal $childJournal
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function removeRelation(TransactionJournal $parentJournal, TransactionJournal $childJournal)
{
$groups = $parentJournal->transactiongroups()->get();
/** @var TransactionGroup $group */
foreach ($groups as $group) {
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id == $childJournal->id) {
// remove from group:
$group->transactionjournals()->detach($childJournal);
}
}
if ($group->transactionjournals()->count() == 1) {
$group->delete();
}
}
return Response::json(true);
}
/**
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
*/
public function search(TransactionJournal $journal, JournalRepositoryInterface $repository)
{
$search = e(trim(Input::get('searchValue')));
$result = $repository->searchRelated($search, $journal);
$result->each(
function (TransactionJournal $journal) {
/** @var Transaction $t */
foreach ($journal->transactions()->get() as $t) {
if ($t->amount > 0) {
$journal->amount = $t->amount;
}
}
}
);
return Response::json($result->toArray());
}
}

View File

@@ -0,0 +1,199 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use Config;
use ExpandedForm;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\PiggyBankFormRequest;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Redirect;
use Session;
use View;
/**
* Class RepeatedExpenseController
*
* @package FireflyIII\Http\Controllers
*/
class RepeatedExpenseController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
}
/**
* @return $this
*/
public function create()
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
return view('repeatedExpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with(
'subTitleIcon', 'fa-plus'
);
}
/**
* @param PiggyBank $repeatedExpense
*
* @return $this
*/
public function delete(PiggyBank $repeatedExpense)
{
$subTitle = 'Delete "' . e($repeatedExpense->name) . '"';
return view('repeatedExpense.delete', compact('repeatedExpense', 'subTitle'));
}
/**
* @param PiggyBank $repeatedExpense
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(PiggyBank $repeatedExpense)
{
Session::flash('success', 'Repeated expense "' . e($repeatedExpense->name) . '" deleted.');
$repeatedExpense->delete();
return Redirect::route('repeated.index');
}
/**
* @param PiggyBank $repeatedExpense
*
* @return $this
*/
public function edit(PiggyBank $repeatedExpense)
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
$subTitle = 'Edit repeated expense "' . e($repeatedExpense->name) . '"';
$subTitleIcon = 'fa-pencil';
/*
* Flash some data to fill the form.
*/
$preFilled = ['name' => $repeatedExpense->name,
'account_id' => $repeatedExpense->account_id,
'targetamount' => $repeatedExpense->targetamount,
'reminder_skip' => $repeatedExpense->reminder_skip,
'rep_every' => $repeatedExpense->rep_every,
'rep_times' => $repeatedExpense->rep_times,
'targetdate' => $repeatedExpense->targetdate->format('Y-m-d'),
'reminder' => $repeatedExpense->reminder,
'remind_me' => intval($repeatedExpense->remind_me) == 1 || !is_null($repeatedExpense->reminder) ? true : false
];
Session::flash('preFilled', $preFilled);
return view('repeatedExpense.edit', compact('subTitle', 'subTitleIcon', 'repeatedExpense', 'accounts', 'periods', 'preFilled'));
}
/**
* @return \Illuminate\View\View
*/
public function index()
{
$subTitle = 'Overview';
$expenses = Auth::user()->piggyBanks()->where('repeats', 1)->get();
$expenses->each(
function (PiggyBank $piggyBank) {
$piggyBank->currentRelevantRep();
}
);
return view('repeatedExpense.index', compact('expenses', 'subTitle'));
}
/**
* @param PiggyBank $repeatedExpense
*
* @return \Illuminate\View\View
*/
public function show(PiggyBank $repeatedExpense, PiggyBankRepositoryInterface $repository)
{
$subTitle = $repeatedExpense->name;
$today = Carbon::now();
$repetitions = $repeatedExpense->piggyBankRepetitions()->get();
$repetitions->each(
function (PiggyBankRepetition $repetition) use ($repository) {
$repetition->bars = $repository->calculateParts($repetition);
}
);
return view('repeatedExpense.show', compact('repetitions', 'repeatedExpense', 'today', 'subTitle'));
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*/
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'repeats' => true,
'name' => $request->get('name'),
'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => new Carbon($request->get('targetdate')),
'reminder' => $request->get('reminder'),
'skip' => intval($request->get('skip')),
'rep_every' => intval($request->get('rep_every')),
'rep_times' => intval($request->get('rep_times')),
];
$piggyBank = $repository->store($piggyBankData);
Session::flash('success', 'Stored repeated expense "' . e($piggyBank->name) . '".');
return Redirect::route('repeated.index');
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param PiggyBank $repeatedExpense
*
* @return $this
*/
public function update(PiggyBank $repeatedExpense, PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'repeats' => false,
'name' => $request->get('name'),
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'rep_length' => $request->get('rep_length'),
'rep_every' => intval($request->get('rep_every')),
'rep_times' => intval($request->get('rep_times')),
'remind_me' => intval($request->get('remind_me')) == 1 ? true : false ,
'reminder' => $request->get('reminder'),
];
$piggyBank = $repository->update($repeatedExpense, $piggyBankData);
Session::flash('success', 'Updated repeated expense "' . e($piggyBank->name) . '".');
return Redirect::route('repeated.index');
}
}

View File

@@ -0,0 +1,284 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use Exception;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause;
use Steam;
use View;
/**
* Class ReportController
*
* @package FireflyIII\Http\Controllers
*/
class ReportController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Reports');
View::share('mainTitleIcon', 'fa-line-chart');
}
/**
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function budget($year = '2014', $month = '1', ReportQueryInterface $query)
{
try {
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
return view('error')->with('message', 'Invalid date');
}
$date = new Carbon($year . '-' . $month . '-01');
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
$start->subDay();
$dayEarly = clone $date;
$subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$dayEarly = $dayEarly->subDay();
$accounts = $query->getAllAccounts($start, $end);
$start->addDay();
$accounts->each(
function (Account $account) use ($start, $end, $query) {
$budgets = $query->getBudgetSummary($account, $start, $end);
$balancedAmount = $query->balancedTransactionsList($account, $start, $end);
$array = [];
foreach ($budgets as $budget) {
$id = intval($budget->id);
$data = $budget->toArray();
$array[$id] = $data;
}
$account->budgetInformation = $array;
$account->balancedAmount = $balancedAmount;
}
);
$start = clone $date;
$start->startOfMonth();
/**
* Start getBudgetsForMonth DONE
*/
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC')
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
/**
* End getBudgetsForMonth DONE
*/
return view('reports.budget', compact('subTitle', 'subTitleIcon', 'date', 'accounts', 'budgets', 'dayEarly'));
}
/**
* @param ReportHelperInterface $helper
*
* @return View
*/
public function index(ReportHelperInterface $helper)
{
$start = $helper->firstDate();
$months = $helper->listOfMonths($start);
$years = $helper->listOfYears($start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
return view('reports.index', compact('years', 'months', 'title', 'mainTitleIcon'));
}
/**
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function month($year = '2014', $month = '1', ReportQueryInterface $query)
{
try {
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
return view('error')->with('message', 'Invalid date.');
}
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report.
/**
*
* get income for month (date)
*
*/
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
/**
* Start getIncomeForMonth DONE
*/
$income = $query->incomeByPeriod($start, $end);
/**
* End getIncomeForMonth DONE
*/
/**
* Start getExpenseGroupedForMonth DONE
*/
$set = $query->journalsByExpenseAccount($start, $end);
$expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10);
/**
* End getExpenseGroupedForMonth DONE
*/
/**
* Start getBudgetsForMonth DONE
*/
$set = Auth::user()->budgets()
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
/**
* End getBudgetsForMonth DONE
*/
/**
* Start getCategoriesForMonth DONE
*/
// all categories.
$result = $query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result);
// all transfers
$result = $query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
// sort.
$sorted = Steam::sortNegativeArray($merged);
// limit to $limit:
$categories = Steam::limitArray($sorted, 10);
/**
* End getCategoriesForMonth DONE
*/
/**
* Start getAccountsForMonth
*/
$list = $query->accountList();
$accounts = [];
/** @var Account $account */
foreach ($list as $account) {
$id = intval($account->id);
/** @noinspection PhpParamsInspection */
$accounts[$id] = [
'name' => $account->name,
'startBalance' => Steam::balance($account, $start),
'endBalance' => Steam::balance($account, $end)
];
$accounts[$id]['difference'] = $accounts[$id]['endBalance'] - $accounts[$id]['startBalance'];
}
/**
* End getAccountsForMonth
*/
return view(
'reports.month',
compact(
'income', 'expenses', 'budgets', 'accounts', 'categories',
'date', 'subTitle', 'displaySum', 'subTitleIcon'
)
);
}
/**
* @param $year
*
* @return $this
*/
public function year($year, ReportHelperInterface $helper, ReportQueryInterface $query)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid date.');
}
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$end->endOfYear();
$title = 'Reports';
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart';
$balances = $helper->yearBalanceReport($date);
$groupedIncomes = $query->journalsByRevenueAccount($date, $end);
$groupedExpenses = $query->journalsByExpenseAccount($date, $end);
//$groupedExpenses = $helper-> expensesGroupedByAccount($date, $end, 15);
return view(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')
);
}
}

View File

@@ -0,0 +1,44 @@
<?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\Search\SearchInterface;
use Illuminate\Http\Request;
use Input;
/**
* Class SearchController
*
* @package FireflyIII\Http\Controllers
*/
class SearchController extends Controller {
/**
* Results always come in the form of an array [results, count, fullCount]
*/
public function index(SearchInterface $searcher)
{
$subTitle = null;
$rawQuery = null;
$result = [];
if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) {
$rawQuery = trim(Input::get('q'));
$words = explode(' ', $rawQuery);
$subTitle = 'Results for "' . e($rawQuery) . '"';
$transactions = $searcher->searchTransactions($words);
$accounts = $searcher->searchAccounts($words);
$categories = $searcher->searchCategories($words);
$budgets = $searcher->searchBudgets($words);
$tags = $searcher->searchTags($words);
$result = ['transactions' => $transactions, 'accounts' => $accounts, 'categories' => $categories, 'budgets' => $budgets, 'tags' => $tags];
}
return view('search.index')->with('title', 'Search')->with('subTitle', $subTitle)->with(
'mainTitleIcon', 'fa-search'
)->with('query', $rawQuery)->with('result', $result);
}
}

View File

@@ -0,0 +1,307 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
use Redirect;
use Session;
use View;
/**
* Class TransactionController
*
* @package FireflyIII\Http\Controllers
*/
class TransactionController extends Controller
{
/**
*/
public function __construct()
{
View::share('title', 'Transactions');
View::share('mainTitleIcon', 'fa-repeat');
}
/**
* Shows the view helping the user to create a new transaction journal.
*
* @param string $what
*
* @return \Illuminate\View\View
*/
public function create($what = 'deposit')
{
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->where('active', 1)->orderBy('name', 'DESC')->get(['accounts.*'])
);
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)';
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)';
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id'];
$subTitle = 'Add a new ' . e($what);
foreach ($respondTo as $r) {
if (!is_null(Input::get($r))) {
$preFilled[$r] = Input::get($r);
}
}
Session::put('preFilled', $preFilled);
asort($piggies);
return view('transactions.create', compact('accounts', 'budgets', 'what', 'piggies', 'subTitle'));
}
/**
* Shows the form that allows a user to delete a transaction journal.
*
* @param TransactionJournal $journal
*
* @return $this
*/
public function delete(TransactionJournal $journal)
{
$type = strtolower($journal->transactionType->type);
$subTitle = 'Delete ' . e($type) . ' "' . e($journal->description) . '"';
return View::make('transactions.delete', compact('journal', 'subTitle'));
}
/**
* @param TransactionJournal $transactionJournal
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(TransactionJournal $transactionJournal)
{
$type = $transactionJournal->transactionType->type;
$return = 'withdrawal';
Session::flash('success', 'Transaction "' . e($transactionJournal->description) . '" destroyed.');
$transactionJournal->delete();
switch ($type) {
case 'Deposit':
$return = 'deposit';
break;
case 'Transfer':
$return = 'transfers';
break;
}
return Redirect::route('transactions.index', $return);
}
/**
* Shows the view to edit a transaction.
*
* @param TransactionJournal $journal
*
* @return $this
*/
public function edit(TransactionJournal $journal, JournalRepositoryInterface $repository)
{
$what = strtolower($journal->transactiontype->type);
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->where('active', 1)->orderBy('name', 'DESC')->get(['accounts.*'])
);
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)';
$transactions = $journal->transactions()->orderBy('amount', 'DESC')->get();
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)';
$preFilled = [
'date' => $journal->date->format('Y-m-d'),
'category' => '',
'budget_id' => 0,
'piggy_bank_id' => 0
];
$category = $journal->categories()->first();
if (!is_null($category)) {
$preFilled['category'] = $category->name;
}
$budget = $journal->budgets()->first();
if (!is_null($budget)) {
$preFilled['budget_id'] = $budget->id;
}
if ($journal->piggyBankEvents()->count() > 0) {
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->first()->piggy_bank_id;
}
$preFilled['amount'] = 0;
/** @var Transaction $t */
foreach ($transactions as $t) {
if (floatval($t->amount) > 0) {
$preFilled['amount'] = floatval($t->amount);
}
}
$preFilled['account_id'] = $repository->getAssetAccount($journal);
$preFilled['expense_account'] = $transactions[0]->account->name;
$preFilled['revenue_account'] = $transactions[1]->account->name;
$preFilled['account_from_id'] = $transactions[1]->account->id;
$preFilled['account_to_id'] = $transactions[0]->account->id;
return View::make('transactions.edit', compact('journal', 'accounts', 'what', 'budgets', 'piggies', 'subTitle'))->with('data', $preFilled);
}
/**
* @param $what
*
* @return $this
*/
public function index($what)
{
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses';
//$journals = $this->_repository->getWithdrawalsPaginated(50);
$types = ['Withdrawal'];
break;
case 'revenue':
case 'deposit':
$subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits';
// $journals = $this->_repository->getDepositsPaginated(50);
$types = ['Deposit'];
break;
case 'transfer':
case 'transfers':
$subTitleIcon = 'fa-arrows-h';
$subTitle = 'Transfers';
//$journals = $this->_repository->getTransfersPaginated(50);
$types = ['Transfer'];
break;
}
$page = intval(\Input::get('page'));
$offset = $page > 0 ? ($page - 1) * 50 : 0;
$set = Auth::user()->transactionJournals()->transactionTypes($types)->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
$count = Auth::user()->transactionJournals()->transactionTypes($types)->count();
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('transactions/' . $what);
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals'));
}
/**
* @param TransactionJournal $journal
*
* @return $this
*/
public function show(TransactionJournal $journal)
{
$journal->transactions->each(
function (Transaction $t) use ($journal) {
$t->before = floatval(
$t->account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))->where(
'transaction_journals.created_at', '<=', $journal->created_at->format('Y-m-d H:i:s')
)->where('transaction_journals.id', '!=', $journal->id)->sum('transactions.amount')
);
$t->after = $t->before + $t->amount;
}
);
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $loopJournal */
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id != $journal->id) {
$members->push($loopJournal);
}
}
}
return view('transactions.show', compact('journal', 'members'))->with(
'subTitle', e($journal->transactiontype->type) . ' "' . e($journal->description) . '"'
);
}
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
'account_id' => intval($request->get('account_id')),
'account_from_id' => intval($request->get('account_from_id')),
'account_to_id' => intval($request->get('account_to_id')),
'expense_account' => $request->get('expense_account'),
'revenue_account' => $request->get('revenue_account'),
'amount' => floatval($request->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($request->get('amount_currency_id')),
'date' => new Carbon($request->get('date')),
'budget_id' => intval($request->get('budget_id')),
'category' => $request->get('category'),
];
$journal = $repository->store($journalData);
Session::flash('success', 'New transaction "' . $journal->description . '" stored!');
return Redirect::route('transactions.index', $request->input('what'));
}
/**
* @param TransactionJournal $journal
*
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @return $this
* @throws FireflyException
*/
public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
'account_id' => intval($request->get('account_id')),
'account_from_id' => intval($request->get('account_from_id')),
'account_to_id' => intval($request->get('account_to_id')),
'expense_account' => $request->get('expense_account'),
'revenue_account' => $request->get('revenue_account'),
'amount' => floatval($request->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($request->get('amount_currency_id')),
'date' => new Carbon($request->get('date')),
'budget_id' => intval($request->get('budget_id')),
'category' => $request->get('category'),
];
$repository->update($journal, $journalData);
Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.');
return Redirect::route('transactions.index', $journalData['what']);
}
}

41
app/Http/Kernel.php Normal file
View File

@@ -0,0 +1,41 @@
<?php namespace FireflyIII\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
/**
* Class Kernel
*
* @package FireflyIII\Http
*/
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* @var array
*/
protected $middleware
= [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'FireflyIII\Http\Middleware\VerifyCsrfToken',
];
/**
* The application's route middleware.
*
* @var array
*/
protected $routeMiddleware
= [
'auth' => 'FireflyIII\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated',
'range' => 'FireflyIII\Http\Middleware\Range',
];
}

View File

@@ -0,0 +1,53 @@
<?php namespace FireflyIII\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
/**
* Class Authenticate
*
* @package FireflyIII\Http\Middleware
*/
class Authenticate
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
*
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
}
}
return $next($request);
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace FireflyIII\Http\Middleware;
use Carbon\Carbon;
use Closure;
use Illuminate\Contracts\Auth\Guard;
use Navigation;
use Preferences;
use Session;
/**
* Class SessionFilter
*
* @package FireflyIII\Http\Middleware
*/
class Range
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
*
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $theNext
*
* @return mixed
*/
public function handle($request, Closure $theNext)
{
if ($this->auth->check()) {
// user's view range comes from preferences, gets set in session:
/** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M');
// the start and end date are checked and stored:
$start = Session::has('start') ? Session::get('start') : new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start);
$end = Navigation::updateEndDate($viewRange->data, $start);
$period = Navigation::periodName($viewRange->data, $start);
$prev = Navigation::jumpToPrevious($viewRange->data, clone $start);
$next = Navigation::jumpToNext($viewRange->data, clone $start);
Session::put('range', $viewRange->data);
Session::put('start', $start);
Session::put('end', $end);
Session::put('period', $period);
Session::put('prev', Navigation::periodName($viewRange->data, $prev));
Session::put('next', Navigation::periodName($viewRange->data, $next));
}
return $theNext($request);
}
}

View File

@@ -0,0 +1,50 @@
<?php namespace FireflyIII\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\RedirectResponse;
/**
* Class RedirectIfAuthenticated
*
* @package FireflyIII\Http\Middleware
*/
class RedirectIfAuthenticated
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
*
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->check()) {
return new RedirectResponse(url('/'));
}
return $next($request);
}
}

View File

@@ -0,0 +1,27 @@
<?php namespace FireflyIII\Http\Middleware;
use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
/**
* Class VerifyCsrfToken
*
* @package FireflyIII\Http\Middleware
*/
class VerifyCsrfToken extends BaseVerifier
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
return parent::handle($request, $next);
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
use Config;
use FireflyIII\Models\Account;
use Input;
/**
* Class AccountFormRequest
*
* @package FireflyIII\Http\Requests
*/
class AccountFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$accountRoles = join(',', array_keys(Config::get('firefly.accountRoles')));
$types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier')));
$nameRule = 'required|between:1,100|uniqueForUser:accounts,name';
if (Account::find(Input::get('id'))) {
$nameRule = 'required|between:1,100';
}
return [
'name' => $nameRule,
'openingBalance' => 'numeric',
'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean',
'balance_currency_id' => 'exists:transaction_currencies,id',
'what' => 'in:' . $types
];
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 12:29
*/
namespace FireflyIII\Http\Requests;
use Auth;
use Input;
/**
* Class BillFormRequest
*
* @package FireflyIII\Http\Requests
*/
class BillFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,255|uniqueForUser:bills,name';
if(intval(Input::get('id')) > 0) {
$nameRule .= ','.intval(Input::get('id'));
}
$rules = [
'name' => $nameRule,
'match' => 'required|between:1,255',
'amount_min' => 'required|numeric|min:0.01',
'amount_max' => 'required|numeric|min:0.01',
'amount_currency_id' => 'required|exists:transaction_currencies,id',
'date' => 'required|date',
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
'skip' => 'required|between:0,31',
'automatch' => 'in:1',
'active' => 'in:1',
];
return $rules;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
use FireflyIII\Models\Budget;
use Input;
/**
* Class BudgetFormRequest
*
* @package FireflyIII\Http\Requests
*/
class BudgetFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,100|uniqueForUser:budgets,name';
if (Budget::find(Input::get('id'))) {
$nameRule = 'required|between:1,100';
}
return [
'name' => $nameRule,
];
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
use FireflyIII\Models\Category;
use Input;
/**
* Class CategoryFormRequest
*
* @package FireflyIII\Http\Requests
*/
class CategoryFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,100|uniqueForUser:categories,name';
if (Category::find(Input::get('id'))) {
$nameRule = 'required|between:1,100';
}
return [
'name' => $nameRule,
];
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 12:29
*/
namespace FireflyIII\Http\Requests;
use Auth;
use Input;
/**
* Class BillFormRequest
*
* @package FireflyIII\Http\Requests
*/
class CurrencyFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$rules = [
'code' => 'required|min:3|max:3|unique:transaction_currencies,code',
'name' => 'required|max:48|min:1|unique:transaction_currencies,name',
'symbol' => 'required|min:1|max:8|unique:transaction_currencies,symbol',
];
if (intval(Input::get('id')) > 0) {
$rules = [
'code' => 'required|min:3|max:3',
'name' => 'required|max:48|min:1',
'symbol' => 'required|min:1|max:8',
];
}
return $rules;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
use FireflyIII\Models\Account;
use Input;
/**
* Class JournalFormRequest
*
* @package FireflyIII\Http\Requests
*/
class JournalFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
// can we switch on the "what"?
$what = Input::get('what');
$rules = [
'description' => 'required|min:1,max:255',
'what' => 'required|in:withdrawal,deposit,transfer|exists:transaction_types,type',
'amount' => 'numeric|required|min:0.01',
'date' => 'required|date',
'amount_currency_id' => 'required|exists:transaction_currencies,id',
];
switch ($what) {
case 'withdrawal':
$rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
$rules['expense_account'] = 'between:1,255';
$rules['category'] = 'between:1,255';
if (intval(Input::get('budget_id')) != 0) {
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
}
break;
case 'deposit':
$rules['category'] = 'between:1,255';
$rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
$rules['revenue_account'] = 'between:1,255';
break;
case 'transfer':
$rules['account_from_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_to_id';
$rules['account_to_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_from_id';
$rules['category'] = 'between:1,255';
break;
default:
die('Cannot handle ' . $what);
break;
}
return $rules;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
use FireflyIII\Models\Account;
use Input;
/**
* Class PiggyBankFormRequest
*
* @package FireflyIII\Http\Requests
*/
class PiggyBankFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,255|uniqueForUser:piggy_banks,name';
$targetDateRule = 'date';
if (intval(Input::get('id'))) {
$nameRule = 'required|between:1,255';
}
if (intval(Input::get('repeats')) == 1) {
$targetDateRule = 'required|date|after:' . date('Y-m-d');
}
$rules = [
'repeats' => 'required|boolean',
'name' => $nameRule,
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'required|min:0.01',
'amount_currency_id' => 'exists:transaction_currencies,id',
'startdate' => 'date',
'targetdate' => $targetDateRule,
'rep_length' => 'in:day,week,quarter,month,year',
'rep_every' => 'integer|min:0|max:31',
'rep_times' => 'integer|min:0|max:99',
'reminder' => 'in:day,week,quarter,month,year',
'reminder_skip' => 'integer|min:0|max:99',
'remind_me' => 'boolean',
'order' => 'integer|min:1',
];
return $rules;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
use FireflyIII\Models\Account;
/**
* Class ProfileFormRequest
*
* @package FireflyIII\Http\Requests
*/
class ProfileFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
return [
'current_password' => 'required',
'new_password' => 'required|confirmed',
'new_password_confirmation' => 'required',
];
}
}

View File

@@ -0,0 +1,15 @@
<?php namespace FireflyIII\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
/**
* Class Request
*
* @package FireflyIII\Http\Requests
*/
abstract class Request extends FormRequest
{
//
}

376
app/Http/breadcrumbs.php Normal file
View File

@@ -0,0 +1,376 @@
<?php
use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
/*
* Back home.
*/
Breadcrumbs::register(
'home',
function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index'));
}
);
// accounts
Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
$breadcrumbs->push(ucfirst(e($what)) . ' accounts', route('accounts.index', $what));
}
);
Breadcrumbs::register(
'accounts.show', function (Generator $breadcrumbs, Account $account) {
switch ($account->accountType->type) {
default:
throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"');
break;
case 'Default account':
case 'Asset account':
$what = 'asset';
break;
case 'Cash account':
$what = 'cash';
break;
case 'Expense account':
case 'Beneficiary account':
$what = 'expense';
break;
case 'Revenue account':
$what = 'revenue';
break;
}
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push(e($account->name), route('accounts.show', $account->id));
}
);
Breadcrumbs::register(
'accounts.delete', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Delete ' . e($account->name), route('accounts.delete', $account->id));
}
);
Breadcrumbs::register(
'accounts.edit', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Edit ' . e($account->name), route('accounts.edit', $account->id));
}
);
// budgets.
Breadcrumbs::register(
'budgets.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Budgets', route('budgets.index'));
}
);
Breadcrumbs::register(
'budgets.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push('Create new budget', route('budgets.create'));
}
);
Breadcrumbs::register(
'budgets.edit', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Edit ' . e($budget->name), route('budgets.edit', $budget->id));
}
);
Breadcrumbs::register(
'budgets.delete', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Delete ' . e($budget->name), route('budgets.delete', $budget->id));
}
);
Breadcrumbs::register(
'budgets.show', function (Generator $breadcrumbs, Budget $budget, LimitRepetition $repetition = null) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push(e($budget->name), route('budgets.show', $budget->id));
if (!is_null($repetition) && !is_null($repetition->id)) {
$breadcrumbs->push(
Navigation::periodShow($repetition->startdate, $repetition->budgetlimit->repeat_freq), route('budgets.show', $budget->id, $repetition->id)
);
}
}
);
// categories
Breadcrumbs::register(
'categories.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Categories', route('categories.index'));
}
);
Breadcrumbs::register(
'categories.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push('Create new category', route('categories.create'));
}
);
Breadcrumbs::register(
'categories.edit', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Edit ' . e($category->name), route('categories.edit', $category->id));
}
);
Breadcrumbs::register(
'categories.delete', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Delete ' . e($category->name), route('categories.delete', $category->id));
}
);
Breadcrumbs::register(
'categories.show', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name), route('categories.show', $category->id));
}
);
// piggy banks
Breadcrumbs::register(
'piggy-banks.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Piggy banks', route('piggy-banks.index'));
}
);
Breadcrumbs::register(
'piggy-banks.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push('Create new piggy bank', route('piggy-banks.create'));
}
);
Breadcrumbs::register(
'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Edit ' . e($piggyBank->name), route('piggy-banks.edit', $piggyBank->id));
}
);
Breadcrumbs::register(
'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Delete ' . e($piggyBank->name), route('piggy-banks.delete', $piggyBank->id));
}
);
Breadcrumbs::register(
'piggy-banks.show', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push(e($piggyBank->name), route('piggy-banks.show', $piggyBank->id));
}
);
// preferences
Breadcrumbs::register(
'preferences', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Preferences', route('preferences'));
}
);
// profile
Breadcrumbs::register(
'profile', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Profile', route('profile'));
}
);
Breadcrumbs::register(
'change-password', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile');
$breadcrumbs->push('Change your password', route('change-password'));
}
);
// bills
Breadcrumbs::register(
'bills.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Bills', route('bills.index'));
}
);
Breadcrumbs::register(
'bills.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('bills.index');
$breadcrumbs->push('Create new bill', route('bills.create'));
}
);
Breadcrumbs::register(
'bills.edit', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Edit ' . e($bill->name), route('bills.edit', $bill->id));
}
);
Breadcrumbs::register(
'bills.delete', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Delete ' . e($bill->name), route('bills.delete', $bill->id));
}
);
Breadcrumbs::register(
'bills.show', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.index');
$breadcrumbs->push(e($bill->name), route('bills.show', $bill->id));
}
);
// reminders
Breadcrumbs::register(
'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Reminder #' . $reminder->id, route('reminders.show', $reminder->id));
}
);
// repeated expenses
Breadcrumbs::register(
'repeated.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Repeated expenses', route('repeated.index'));
}
);
Breadcrumbs::register(
'repeated.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('repeated.index');
$breadcrumbs->push('Create new repeated expense', route('repeated.create'));
}
);
Breadcrumbs::register(
'repeated.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('repeated.show', $piggyBank);
$breadcrumbs->push('Edit ' . e($piggyBank->name), route('repeated.edit', $piggyBank->id));
}
);
Breadcrumbs::register(
'repeated.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('repeated.show', $piggyBank);
$breadcrumbs->push('Delete ' . e($piggyBank->name), route('repeated.delete', $piggyBank->id));
}
);
Breadcrumbs::register(
'repeated.show', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('repeated.index');
$breadcrumbs->push(e($piggyBank->name), route('repeated.show', $piggyBank->id));
}
);
// reports
Breadcrumbs::register(
'reports.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Reports', route('reports.index'));
}
);
Breadcrumbs::register(
'reports.year', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push($date->format('Y'), route('reports.year', $date->format('Y')));
}
);
Breadcrumbs::register(
'reports.month', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push('Monthly report for ' . $date->format('F Y'), route('reports.month', $date));
}
);
Breadcrumbs::register(
'reports.budget', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push('Budget report for ' . $date->format('F Y'), route('reports.budget', $date));
}
);
// search
Breadcrumbs::register(
'search', function (Generator $breadcrumbs, $query) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Search for "' . e($query) . '"', route('search'));
}
);
// transactions
Breadcrumbs::register(
'transactions.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitle = 'Expenses';
break;
case 'revenue':
case 'deposit':
$subTitle = 'Revenue, income and deposits';
break;
case 'transfer':
case 'transfers':
$subTitle = 'Transfers';
break;
case 'opening balance':
$subTitle = 'Opening balances';
break;
default:
throw new FireflyException('Cannot handle $what "' . e($what) . '" in bread crumbs');
}
$breadcrumbs->push($subTitle, route('transactions.index', $what));
}
);
Breadcrumbs::register(
'transactions.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('transactions.index', $what);
$breadcrumbs->push('Create new ' . e($what), route('transactions.create', $what));
}
);
Breadcrumbs::register(
'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Edit ' . e($journal->description), route('transactions.edit', $journal->id));
}
);
Breadcrumbs::register(
'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Delete ' . e($journal->description), route('transactions.delete', $journal->id));
}
);
Breadcrumbs::register(
'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
$breadcrumbs->push(e($journal->description), route('transactions.show', $journal->id));
}
);

344
app/Http/routes.php Normal file
View File

@@ -0,0 +1,344 @@
<?php
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\PiggyBank;
// models
Route::bind(
'account',
function ($value, $route) {
if (Auth::check()) {
$account = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.editable', 1)
->where('accounts.id', $value)
->where('user_id', Auth::user()->id)
->first(['accounts.*']);
if ($account) {
return $account;
}
}
App::abort(404);
}
);
Route::bind(
'repeatedExpense', function ($value, $route) {
if (Auth::check()) {
return PiggyBank::
where('piggy_banks.id', $value)
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->where('accounts.user_id', Auth::user()->id)
->where('repeats', 1)->first(['piggy_banks.*']);
}
return null;
}
);
Route::bind(
'tjSecond', function ($value, $route) {
if (Auth::check()) {
return TransactionJournal::
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'tj', function ($value, $route) {
if (Auth::check()) {
return TransactionJournal::
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'currency', function ($value, $route) {
return TransactionCurrency::find($value);
}
);
Route::bind(
'bill', function ($value, $route) {
if (Auth::check()) {
return Bill::where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'budget', function ($value, $route) {
if (Auth::check()) {
return Budget::where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'limitrepetition', function ($value, $route) {
if (Auth::check()) {
return LimitRepetition::where('limit_repetitions.id', $value)
->leftjoin('budget_limits', 'budget_limits.id', '=', 'limit_repetitions.budget_limit_id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('budgets.user_id', Auth::user()->id)
->first(['limit_repetitions.*']);
}
return null;
}
);
Route::bind(
'piggyBank', function ($value, $route) {
if (Auth::check()) {
return PiggyBank::
where('piggy_banks.id', $value)
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->where('accounts.user_id', Auth::user()->id)
->where('repeats', 0)->first(['piggy_banks.*']);
}
return null;
}
);
Route::bind(
'category', function ($value, $route) {
if (Auth::check()) {
return Category::where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
/**
* Auth\AuthController
*/
Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'register']);
Route::controllers(
[
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]
);
/**
* Home Controller
*/
Route::group(
['middleware' => ['auth', 'range']], function () {
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::get('/prev', ['uses' => 'HomeController@sessionPrev', 'as' => 'sessionPrev']);
Route::get('/next', ['uses' => 'HomeController@sessionNext', 'as' => 'sessionNext']);
Route::get('/jump/{range}', ['uses' => 'HomeController@rangeJump', 'as' => 'rangeJump']);
Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']);
/**
* Account Controller
*/
Route::get('/accounts/{what}', ['uses' => 'AccountController@index', 'as' => 'accounts.index'])->where('what', 'revenue|asset|expense');
Route::get('/accounts/create/{what}', ['uses' => 'AccountController@create', 'as' => 'accounts.create'])->where('what', 'revenue|asset|expense');
Route::get('/accounts/edit/{account}', ['uses' => 'AccountController@edit', 'as' => 'accounts.edit']);
Route::get('/accounts/delete/{account}', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']);
Route::get('/accounts/show/{account}/{view?}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']);
Route::post('/accounts/store', ['uses' => 'AccountController@store', 'as' => 'accounts.store']);
Route::post('/accounts/update/{account}', ['uses' => 'AccountController@update', 'as' => 'accounts.update']);
Route::post('/accounts/destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']);
/**
* Bills Controller
*/
Route::get('/bills', ['uses' => 'BillController@index', 'as' => 'bills.index']);
Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching.
Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']);
Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);
Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']);
Route::post('/bills/update/{bill}', ['uses' => 'BillController@update', 'as' => 'bills.update']);
Route::post('/bills/destroy/{bill}', ['uses' => 'BillController@destroy', 'as' => 'bills.destroy']);
/**
* Budget Controller
*/
Route::get('/budgets', ['uses' => 'BudgetController@index', 'as' => 'budgets.index']);
Route::get('/budgets/income', ['uses' => 'BudgetController@updateIncome', 'as' => 'budgets.income']); # extra.
Route::get('/budgets/create', ['uses' => 'BudgetController@create', 'as' => 'budgets.create']);
Route::get('/budgets/edit/{budget}', ['uses' => 'BudgetController@edit', 'as' => 'budgets.edit']);
Route::get('/budgets/delete/{budget}', ['uses' => 'BudgetController@delete', 'as' => 'budgets.delete']);
Route::get('/budgets/show/{budget}/{limitrepetition?}', ['uses' => 'BudgetController@show', 'as' => 'budgets.show']);
Route::get('/budgets/list/noBudget', ['uses' => 'BudgetController@noBudget', 'as' => 'budgets.noBudget']);
Route::post('/budgets/income', ['uses' => 'BudgetController@postUpdateIncome', 'as' => 'budgets.postIncome']);
Route::post('/budgets/store', ['uses' => 'BudgetController@store', 'as' => 'budgets.store']);
Route::post('/budgets/update/{budget}', ['uses' => 'BudgetController@update', 'as' => 'budgets.update']);
Route::post('/budgets/destroy/{budget}', ['uses' => 'BudgetController@destroy', 'as' => 'budgets.destroy']);
Route::post('budgets/amount/{budget}', ['uses' => 'BudgetController@amount']);
/**
* Category Controller
*/
Route::get('/categories', ['uses' => 'CategoryController@index', 'as' => 'categories.index']);
Route::get('/categories/create', ['uses' => 'CategoryController@create', 'as' => 'categories.create']);
Route::get('/categories/edit/{category}', ['uses' => 'CategoryController@edit', 'as' => 'categories.edit']);
Route::get('/categories/delete/{category}', ['uses' => 'CategoryController@delete', 'as' => 'categories.delete']);
Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']);
Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noCategory']);
Route::post('/categories/store', ['uses' => 'CategoryController@store', 'as' => 'categories.store']);
Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']);
Route::post('/categories/destroy/{category}', ['uses' => 'CategoryController@destroy', 'as' => 'categories.destroy']);
/**
* Currency Controller
*/
Route::get('/currency', ['uses' => 'CurrencyController@index', 'as' => 'currency.index']);
Route::get('/currency/create', ['uses' => 'CurrencyController@create', 'as' => 'currency.create']);
Route::get('/currency/edit/{currency}', ['uses' => 'CurrencyController@edit', 'as' => 'currency.edit']);
Route::get('/currency/delete/{currency}', ['uses' => 'CurrencyController@delete', 'as' => 'currency.delete']);
Route::get('/currency/default/{currency}', ['uses' => 'CurrencyController@defaultCurrency', 'as' => 'currency.default']);
Route::post('/currency/store', ['uses' => 'CurrencyController@store', 'as' => 'currency.store']);
Route::post('/currency/update/{currency}', ['uses' => 'CurrencyController@update', 'as' => 'currency.update']);
Route::post('/currency/destroy/{currency}', ['uses' => 'CurrencyController@destroy', 'as' => 'currency.destroy']);
/**
* Google Chart Controller
*/
Route::get('/chart/home/account', ['uses' => 'GoogleChartController@allAccountsBalanceChart']);
Route::get('/chart/home/budgets', ['uses' => 'GoogleChartController@allBudgetsHomeChart']);
Route::get('/chart/home/categories', ['uses' => 'GoogleChartController@allCategoriesHomeChart']);
Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']);
Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']);
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
Route::get('/chart/category/{category}/spending/{year}', ['uses' => 'GoogleChartController@categoriesAndSpending']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']);
Route::get('/chart/piggy-history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']);
/**
* Help Controller
*/
Route::get('/help/{route}', ['uses' => 'HelpController@show', 'as' => 'help.show']);
/**
* JSON Controller
*/
Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']);
Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']);
Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']);
/**
* Piggy Bank Controller
*/
Route::get('/piggy-banks', ['uses' => 'PiggyBankController@index', 'as' => 'piggy-banks.index']);
Route::get('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@add']); # add money
Route::get('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@remove']); #remove money
Route::get('/piggy-banks/create', ['uses' => 'PiggyBankController@create', 'as' => 'piggy-banks.create']);
Route::get('/piggy-banks/edit/{piggyBank}', ['uses' => 'PiggyBankController@edit', 'as' => 'piggy-banks.edit']);
Route::get('/piggy-banks/delete/{piggyBank}', ['uses' => 'PiggyBankController@delete', 'as' => 'piggy-banks.delete']);
Route::get('/piggy-banks/show/{piggyBank}', ['uses' => 'PiggyBankController@show', 'as' => 'piggy-banks.show']);
Route::post('/piggy-banks/store', ['uses' => 'PiggyBankController@store', 'as' => 'piggy-banks.store']);
Route::post('/piggy-banks/update/{piggyBank}', ['uses' => 'PiggyBankController@update', 'as' => 'piggy-banks.update']);
Route::post('/piggy-banks/destroy/{piggyBank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy-banks.destroy']);
Route::post('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy-banks.add']); # add money
Route::post('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy-banks.remove']); # remove money.
/**
* Preferences Controller
*/
Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']);
Route::post('/preferences', ['uses' => 'PreferencesController@postIndex']);
/**
* Profile Controller
*/
Route::get('/profile', ['uses' => 'ProfileController@index', 'as' => 'profile']);
Route::get('/profile/change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']);
Route::post('/profile/change-password', ['uses' => 'ProfileController@postChangePassword','as' => 'change-password-post']);
/**
* Related transactions controller
*/
Route::get('/related/alreadyRelated/{tj}', ['uses' => 'RelatedController@alreadyRelated', 'as' => 'related.alreadyRelated']);
Route::post('/related/relate/{tj}/{tjSecond}', ['uses' => 'RelatedController@relate', 'as' => 'related.relate']);
Route::post('/related/removeRelation/{tj}/{tjSecond}', ['uses' => 'RelatedController@removeRelation', 'as' => 'related.removeRelation']);
Route::get('/related/related/{tj}', ['uses' => 'RelatedController@related', 'as' => 'related.related']);
Route::post('/related/search/{tj}', ['uses' => 'RelatedController@search', 'as' => 'related.search']);
/**
* Repeated Expenses Controller
*/
Route::get('/repeated-expenses', ['uses' => 'RepeatedExpenseController@index', 'as' => 'repeated.index']);
Route::get('/repeated-expenses/create', ['uses' => 'RepeatedExpenseController@create', 'as' => 'repeated.create']);
Route::get('/repeated-expenses/edit/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@edit', 'as' => 'repeated.edit']);
Route::get('/repeated-expenses/delete/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@delete', 'as' => 'repeated.delete']);
Route::get('/repeated-expenses/show/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@show', 'as' => 'repeated.show']);
Route::post('/repeated-expense/store', ['uses' => 'RepeatedExpenseController@store', 'as' => 'repeated.store']);
Route::post('/repeated-expense/update/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@update', 'as' => 'repeated.update']);
Route::post('/repeated-expense/destroy/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@destroy', 'as' => 'repeated.destroy']);
/**
* Report Controller
*/
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']);
Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']);
Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']);
/**
* Search Controller
*/
Route::get('/search', ['uses' => 'SearchController@index', 'as' => 'search']);
/**
* Transaction Controller
*/
Route::get('/transactions/{what}', ['uses' => 'TransactionController@index', 'as' => 'transactions.index'])->where(
['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers']
);
Route::get('/transactions/create/{what}', ['uses' => 'TransactionController@create', 'as' => 'transactions.create'])->where(
['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers']
);
Route::get('/transaction/edit/{tj}', ['uses' => 'TransactionController@edit', 'as' => 'transactions.edit']);
Route::get('/transaction/delete/{tj}', ['uses' => 'TransactionController@delete', 'as' => 'transactions.delete']);
Route::get('/transaction/show/{tj}', ['uses' => 'TransactionController@show', 'as' => 'transactions.show']);
// transaction controller:
Route::post('/transactions/store/{what}', ['uses' => 'TransactionController@store', 'as' => 'transactions.store'])->where(
['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers']
);
Route::post('/transaction/update/{tj}', ['uses' => 'TransactionController@update', 'as' => 'transactions.update']);
Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']);
/**
* Auth\Auth Controller
*/
Route::get('/logout', ['uses' => 'Auth\AuthController@getLogout', 'as' => 'logout']);
}
);

105
app/Models/Account.php Normal file
View File

@@ -0,0 +1,105 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
/**
* Class Account
*
* @package FireflyIII\Models
*/
class Account extends Model
{
use SoftDeletes, ValidatingTrait;
protected $rules
= [
'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id',
'name' => 'required|between:1,100|uniqueForUser:accounts,name',
'active' => 'required|boolean'
];
protected $fillable = ['user_id', 'account_type_id', 'name', 'active'];
/**
* @param $fieldName
*
* @return string|null
*/
public function getMeta($fieldName)
{
foreach ($this->accountMeta as $meta) {
if ($meta->name == $fieldName) {
return $meta->data;
}
}
return null;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function accountMeta()
{
return $this->hasMany('FireflyIII\Models\AccountMeta');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function accountType()
{
return $this->belongsTo('FireflyIII\Models\AccountType');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @param EloquentBuilder $query
* @param array $types
*/
public function scopeAccountTypeIn(EloquentBuilder $query, array $types)
{
if (is_null($this->joinedAccountTypes)) {
$query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id');
$this->joinedAccountTypes = true;
}
$query->whereIn('account_types.type', $types);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactions()
{
return $this->hasMany('FireflyIII\Models\Transaction');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBanks()
{
return $this->hasMany('FireflyIII\Models\PiggyBank');
}
}

View File

@@ -0,0 +1,59 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Watson\Validating\ValidatingTrait;
/**
* Class AccountMeta
*
* @package FireflyIII\Models
*/
class AccountMeta extends Model
{
use ValidatingTrait;
protected $fillable = ['account_id', 'name', 'data'];
protected $rules
= [
'account_id' => 'required|exists:accounts,id',
'name' => 'required|between:1,100',
'data' => 'required'
];
protected $table = 'account_meta';
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function account()
{
return $this->belongsTo('FireflyIII\Models\Account');
}
/**
* @param $value
*
* @return mixed
*/
public function getDataAttribute($value)
{
return json_decode($value);
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at'];
}
/**
* @param $value
*/
public function setDataAttribute($value)
{
$this->attributes['data'] = json_encode($value);
}
}

View File

@@ -0,0 +1,29 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class AccountType
*
* @package FireflyIII\Models
*/
class AccountType extends Model
{
//
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function accounts()
{
return $this->hasMany('FireflyIII\Models\Account');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at'];
}
}

40
app/Models/Bill.php Normal file
View File

@@ -0,0 +1,40 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class Bill
*
* @package FireflyIII\Models
*/
class Bill extends Model
{
protected $fillable = ['name', 'match', 'amount_min','user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',];
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'date'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactionjournals()
{
return $this->hasMany('FireflyIII\Models\TransactionJournal');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

59
app/Models/Budget.php Normal file
View File

@@ -0,0 +1,59 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class Budget
*
* @package FireflyIII\Models
*/
class Budget extends Model
{
use SoftDeletes;
protected $fillable = ['user_id', 'name'];
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function budgetlimits()
{
return $this->hasMany('FireflyIII\Models\BudgetLimit');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function limitrepetitions()
{
return $this->hasManyThrough('FireflyIII\Models\LimitRepetition', 'FireflyIII\Models\BudgetLimit', 'budget_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function transactionjournals()
{
return $this->belongsToMany('FireflyIII\Models\TransactionJournal', 'budget_transaction_journal', 'budget_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@@ -0,0 +1,37 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class BudgetLimit
*
* @package FireflyIII\Models
*/
class BudgetLimit extends Model
{
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function budget()
{
return $this->belongsTo('FireflyIII\Models\Budget');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'startdate'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function limitrepetitions()
{
return $this->hasMany('FireflyIII\Models\LimitRepetition');
}
}

41
app/Models/Category.php Normal file
View File

@@ -0,0 +1,41 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class Category
*
* @package FireflyIII\Models
*/
class Category extends Model
{
use SoftDeletes;
protected $fillable = ['user_id', 'name'];
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function transactionjournals()
{
return $this->belongsToMany('FireflyIII\Models\TransactionJournal', 'category_transaction_journal', 'category_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

22
app/Models/Component.php Normal file
View File

@@ -0,0 +1,22 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class Component
*
* @package FireflyIII\Models
*/
class Component extends Model
{
use SoftDeletes;
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
}

View File

@@ -0,0 +1,45 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class LimitRepetition
*
* @package FireflyIII\Models
*/
class LimitRepetition extends Model
{
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function budgetLimit()
{
return $this->belongsTo('FireflyIII\Models\BudgetLimit');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'startdate', 'enddate'];
}
public function spentInRepetition()
{
$sum = \DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('transaction_journals.date', '>=', $this->startdate->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->enddate->format('Y-m-d'))
->where('transactions.amount', '>', 0)
->where('limit_repetitions.id', '=', $this->id)
->sum('transactions.amount');
return floatval($sum);
}
}

113
app/Models/PiggyBank.php Normal file
View File

@@ -0,0 +1,113 @@
<?php namespace FireflyIII\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class PiggyBank
*
* @package FireflyIII\Models
*/
class PiggyBank extends Model
{
use SoftDeletes;
protected $fillable = ['repeats', 'name', 'account_id','rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder',];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function account()
{
return $this->belongsTo('FireflyIII\Models\Account');
}
/**
* Grabs the PiggyBankRepetition that's currently relevant / active
*
* @returns PiggyBankRepetition
*/
public function currentRelevantRep()
{
if ($this->currentRep) {
return $this->currentRep;
}
if ($this->repeats == 0) {
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
$this->currentRep = $rep;
return $rep;
} else {
$query = $this->piggyBankRepetitions()->where(
function (EloquentBuilder $q) {
$q->where(
function (EloquentBuilder $q) {
$q->where(
function (EloquentBuilder $q) {
$today = new Carbon;
$q->whereNull('startdate');
$q->orWhere('startdate', '<=', $today->format('Y-m-d 00:00:00'));
}
)->where(
function (EloquentBuilder $q) {
$today = new Carbon;
$q->whereNull('targetdate');
$q->orWhere('targetdate', '>=', $today->format('Y-m-d 00:00:00'));
}
);
}
)->orWhere(
function (EloquentBuilder $q) {
$today = new Carbon;
$q->where('startdate', '>=', $today->format('Y-m-d 00:00:00'));
$q->where('targetdate', '>=', $today->format('Y-m-d 00:00:00'));
}
);
}
)->orderBy('startdate', 'ASC');
$result = $query->first(['piggy_bank_repetitions.*']);
$this->currentRep = $result;
return $result;
}
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBankRepetitions()
{
return $this->hasMany('FireflyIII\Models\PiggyBankRepetition');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBankEvents()
{
return $this->hasMany('FireflyIII\Models\PiggyBankEvent');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function reminders()
{
return $this->morphMany('FireflyIII\Models\Reminder', 'remindersable');
}
}

View File

@@ -0,0 +1,37 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class PiggyBankEvent
*
* @package FireflyIII\Models
*/
class PiggyBankEvent extends Model
{
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'date'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function piggyBank()
{
return $this->belongsTo('FireflyIII\Models\PiggyBank');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function transactionJournal()
{
return $this->belongsTo('FireflyIII\Models\TransactionJournal');
}
}

View File

@@ -0,0 +1,29 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class PiggyBankRepetition
*
* @package FireflyIII\Models
*/
class PiggyBankRepetition extends Model
{
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'startdate', 'targetdate'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function piggyBank()
{
return $this->belongsTo('FireflyIII\Models\PiggyBank');
}
}

49
app/Models/Preference.php Normal file
View File

@@ -0,0 +1,49 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class Preference
*
* @package FireflyIII\Models
*/
class Preference extends Model
{
protected $fillable = ['user_id', 'data', 'name'];
/**
* @param $value
*
* @return mixed
*/
public function getDataAttribute($value)
{
return json_decode($value);
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at'];
}
/**
* @param $value
*/
public function setDataAttribute($value)
{
$this->attributes['data'] = json_encode($value);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

37
app/Models/Reminder.php Normal file
View File

@@ -0,0 +1,37 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class Reminder
*
* @package FireflyIII\Models
*/
class Reminder extends Model
{
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'startdate', 'enddate'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function remindersable()
{
return $this->morphTo();
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@@ -0,0 +1,48 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
/**
* Class Transaction
*
* @package FireflyIII\Models
*/
class Transaction extends Model
{
protected $fillable = ['account_id', 'transaction_journal_id', 'description', 'amount'];
protected $rules
= [
'account_id' => 'required|exists:accounts,id',
'transaction_journal_id' => 'required|exists:transaction_journals,id',
'description' => 'between:1,255',
'amount' => 'required|numeric'
];
use SoftDeletes, ValidatingTrait;
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function account()
{
return $this->belongsTo('FireflyIII\Models\Account');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function transactionJournal()
{
return $this->belongsTo('FireflyIII\Models\TransactionJournal');
}
}

View File

@@ -0,0 +1,33 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class TransactionCurrency
*
* @package FireflyIII\Models
*/
class TransactionCurrency extends Model
{
use SoftDeletes;
protected $fillable = ['name', 'code', 'symbol'];
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactionJournals()
{
return $this->hasMany('FireflyIII\Models\TransactionJournal');
}
}

View File

@@ -0,0 +1,39 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class TransactionGroup
*
* @package FireflyIII\Models
*/
class TransactionGroup extends Model
{
use SoftDeletes;
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function transactionjournals()
{
return $this->belongsToMany('FireflyIII\Models\TransactionJournal');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@@ -0,0 +1,231 @@
<?php namespace FireflyIII\Models;
use Carbon\Carbon;
use Crypt;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
/**
* Class TransactionJournal
*
* @package FireflyIII\Models
*/
class TransactionJournal extends Model
{
use SoftDeletes, ValidatingTrait;
protected $fillable = ['user_id', 'transaction_type_id', 'bill_id', 'transaction_currency_id', 'description', 'completed', 'date', 'encrypted'];
protected $rules
= [
'user_id' => 'required|exists:users,id',
'transaction_type_id' => 'required|exists:transaction_types,id',
'bill_id' => 'exists:bills,id',
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
'description' => 'required|between:1,1024',
'completed' => 'required|boolean',
'date' => 'required|date',
'encrypted' => 'required|boolean'
];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function bill()
{
return $this->belongsTo('FireflyIII\Models\Bill');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function budgets()
{
return $this->belongsToMany('FireflyIII\Models\Budget');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function categories()
{
return $this->belongsToMany('FireflyIII\Models\Category');
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'date', 'deleted_at'];
}
/**
* @param $value
*
* @return string
*/
public function getDescriptionAttribute($value)
{
if ($this->encrypted) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBankEvents()
{
return $this->hasMany('FireflyIII\Models\PiggyBankEvent');
}
/**
* @param EloquentBuilder $query
* @param Account $account
*/
public function scopeAccountIs(EloquentBuilder $query, Account $account)
{
if (!isset($this->joinedTransactions)) {
$query->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id');
$this->joinedTransactions = true;
}
$query->where('transactions.account_id', $account->id);
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeAfter(EloquentBuilder $query, Carbon $date)
{
return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d 00:00:00'));
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeBefore(EloquentBuilder $query, Carbon $date)
{
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
}
/**
* @param EloquentBuilder $query
* @param $amount
*/
public function scopeLessThan(EloquentBuilder $query, $amount)
{
if (is_null($this->joinedTransactions)) {
$query->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
);
$this->joinedTransactions = true;
}
$query->where('transactions.amount', '<=', $amount);
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeOnDate(EloquentBuilder $query, Carbon $date)
{
return $query->where('date', '=', $date->format('Y-m-d'));
}
/**
* @param EloquentBuilder $query
* @param array $types
*/
public function scopeTransactionTypes(EloquentBuilder $query, array $types)
{
if (is_null($this->joinedTransactionTypes)) {
$query->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
);
$this->joinedTransactionTypes = true;
}
$query->whereIn('transaction_types.type', $types);
}
/**
* Automatically includes the 'with' parameters to get relevant related
* objects.
*
* @param EloquentBuilder $query
*/
public function scopeWithRelevantData(EloquentBuilder $query)
{
$query->with(
['transactions' => function (HasMany $q) {
$q->orderBy('amount', 'ASC');
}, 'transactiontype', 'transactioncurrency', 'budgets', 'categories', 'transactions.account.accounttype', 'bill', 'budgets', 'categories']
);
}
/**
* @param $value
*/
public function setDescriptionAttribute($value)
{
$this->attributes['description'] = \Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function transactionCurrency()
{
return $this->belongsTo('FireflyIII\Models\TransactionCurrency');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function transactionType()
{
return $this->belongsTo('FireflyIII\Models\TransactionType');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function transactiongroups()
{
return $this->belongsToMany('FireflyIII\Models\TransactionGroup');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactions()
{
return $this->hasMany('FireflyIII\Models\Transaction');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@@ -0,0 +1,21 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class TransactionRelation
*
* @package FireflyIII\Models
*/
class TransactionRelation extends Model
{
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at'];
}
}

View File

@@ -0,0 +1,30 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class TransactionType
*
* @package FireflyIII\Models
*/
class TransactionType extends Model
{
use SoftDeletes;
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactionJournals()
{
return $this->hasMany('FireflyIII\Models\TransactionJournal');
}
}

View File

@@ -0,0 +1,40 @@
<?php namespace FireflyIII\Providers;
use Illuminate\Support\ServiceProvider;
/**
* Class AppServiceProvider
*
* @package FireflyIII\Providers
*/
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* This service provider is a great spot to register your various container
* bindings with the application. As you can see, we are registering our
* "Registrar" implementation here. You can add your own bindings too!
*
* @return void
*/
public function register()
{
$this->app->bind(
'Illuminate\Contracts\Auth\Registrar',
'FireflyIII\Services\Registrar'
);
}
}

View File

@@ -0,0 +1,42 @@
<?php namespace FireflyIII\Providers;
use Illuminate\Bus\Dispatcher;
use Illuminate\Support\ServiceProvider;
/**
* Class BusServiceProvider
*
* @package FireflyIII\Providers
*/
class BusServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @param \Illuminate\Bus\Dispatcher $dispatcher
*
* @return void
*/
public function boot(Dispatcher $dispatcher)
{
$dispatcher->mapUsing(
function ($command) {
return Dispatcher::simpleMapping(
$command, 'FireflyIII\Commands', 'FireflyIII\Handlers\Commands'
);
}
);
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,31 @@
<?php namespace FireflyIII\Providers;
use Illuminate\Support\ServiceProvider;
/**
* Class ConfigServiceProvider
*
* @package FireflyIII\Providers
*/
class ConfigServiceProvider extends ServiceProvider
{
/**
* Overwrite any vendor / package configuration.
*
* This service provider is intended to provide a convenient location for you
* to overwrite any "vendor" or package configuration that you may want to
* modify before the application handles the incoming request / command.
*
* @return void
*/
public function register()
{
config(
[
//
]
);
}
}

View File

@@ -0,0 +1,122 @@
<?php namespace FireflyIII\Providers;
use FireflyIII\Models\Account;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Support\Facades\Navigation;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
/**
* Class EventServiceProvider
*
* @package FireflyIII\Providers
*/
class EventServiceProvider extends ServiceProvider
{
/**
* The event handler mappings for the application.
*
* @var array
*/
protected $listen
= [
'event.name' => [
'EventListener',
],
'App\Events\JournalDeleted' => [
'App\Handlers\Events\JournalDeletedHandler@handle',
],
];
/**
* Register any other events for your application.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
*
* @return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
TransactionJournal::deleted(
function (TransactionJournal $journal) {
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
$transaction->delete();
}
}
);
Account::deleted(
function (Account $account) {
/** @var Transaction $transaction */
foreach ($account->transactions()->get() as $transaction) {
$journal = $transaction->transactionJournal()->first();
$journal->delete();
}
}
);
PiggyBank::created(function(PiggyBank $piggyBank) {
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($piggyBank);
$repetition->startdate = $piggyBank->startdate;
$repetition->targetdate = $piggyBank->targetdate;
$repetition->currentamount = 0;
$repetition->save();
});
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {
$end = Navigation::addPeriod(clone $budgetLimit->startdate, $budgetLimit->repeat_freq, 0);
$end->subDay();
$set = $budgetLimit->limitrepetitions()->where('startdate', $budgetLimit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))
->get();
/*
* Create new LimitRepetition:
*/
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
\Log::error('Trying to save new LimitRepetition failed!');
\Log::error($e->getMessage());
}
} else {
if ($set->count() == 1) {
/*
* Update existing one.
*/
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
}
}
);
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace FireflyIII\Providers;
use FireflyIII\Support\Amount;
use FireflyIII\Support\ExpandedForm;
use FireflyIII\Support\Navigation;
use FireflyIII\Support\Preferences;
use FireflyIII\Support\Steam;
use FireflyIII\Validation\FireflyValidator;
use Illuminate\Support\ServiceProvider;
use Validator;
/**
* Class FireflyServiceProvider
*
* @package FireflyIII\Providers
*/
class FireflyServiceProvider extends ServiceProvider
{
public function boot()
{
Validator::resolver(
function ($translator, $data, $rules, $messages) {
return new FireflyValidator($translator, $data, $rules, $messages);
}
);
}
public function register()
{
$this->app->bind(
'preferences', function () {
return new Preferences;
}
);
$this->app->bind(
'navigation', function () {
return new Navigation;
}
);
$this->app->bind(
'amount', function () {
return new Amount;
}
);
$this->app->bind(
'steam', function () {
return new Steam;
}
);
$this->app->bind(
'expandedform', function () {
return new ExpandedForm;
}
);
$this->app->bind('FireflyIII\Repositories\Account\AccountRepositoryInterface', 'FireflyIII\Repositories\Account\AccountRepository');
$this->app->bind('FireflyIII\Repositories\Budget\BudgetRepositoryInterface', 'FireflyIII\Repositories\Budget\BudgetRepository');
$this->app->bind('FireflyIII\Repositories\Category\CategoryRepositoryInterface', 'FireflyIII\Repositories\Category\CategoryRepository');
$this->app->bind('FireflyIII\Repositories\Journal\JournalRepositoryInterface', 'FireflyIII\Repositories\Journal\JournalRepository');
$this->app->bind('FireflyIII\Repositories\Bill\BillRepositoryInterface', 'FireflyIII\Repositories\Bill\BillRepository');
$this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository');
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
$this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper');
$this->app->bind('FireflyIII\Helpers\Report\ReportQueryInterface', 'FireflyIII\Helpers\Report\ReportQuery');
}
}

View File

@@ -0,0 +1,70 @@
<?php namespace FireflyIII\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Routing\Router;
/**
* Class RouteServiceProvider
*
* @package FireflyIII\Providers
*/
class RouteServiceProvider extends ServiceProvider
{
/**
* This namespace is applied to the controller routes in your routes file.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'FireflyIII\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @param \Illuminate\Routing\Router $router
*
* @return void
*/
public function boot(Router $router)
{
parent::boot($router);
$router->before(
function (Request $request) {
// put IP in session if not already there.
$reminders = [];
if ($request->user()) {
//Filter::setSessionDateRange();
//Reminders::updateReminders();
//Steam::removeEmptyBudgetLimits();
//$reminders = Reminders::getReminders();
}
// View::share('reminders', $reminders);
}
);
}
/**
* Define the routes for the application.
*
* @param \Illuminate\Routing\Router $router
*
* @return void
*/
public function map(Router $router)
{
$router->group(
['namespace' => $this->namespace], function ($router) {
/** @noinspection PhpIncludeInspection */
require app_path('Http/routes.php');
}
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace FireflyIII\Providers;
use Illuminate\Support\ServiceProvider;
/**
* Class TestingServiceProvider
*
* @package FireflyIII\Providers
*/
class TestingServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
if ($this->app->environment() == 'testing') {
$this->app['config']['session.driver'] = 'native';
}
}
}

View File

@@ -0,0 +1,330 @@
<?php
namespace FireflyIII\Repositories\Account;
use App;
use Auth;
use Carbon\Carbon;
use Config;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Pagination\LengthAwarePaginator;
use Log;
use Session;
/**
* Class AccountRepository
*
* @package FireflyIII\Repositories\Account
*/
class AccountRepository implements AccountRepositoryInterface
{
/**
* @param Account $account
*
* @return boolean
*/
public function destroy(Account $account)
{
$account->delete();
return true;
}
/**
* @param Account $account
* @param int $page
* @param string $range
*
* @return mixed
*/
public function getJournals(Account $account, $page, $range = 'session')
{
$offset = $page * 50;
$query = Auth::user()
->transactionJournals()
->withRelevantData()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)
->orderBy('date', 'DESC');
if ($range == 'session') {
$query->before(Session::get('end', Carbon::now()->startOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth()));
}
$count = $query->count();
$set = $query->take(50)->offset($offset)->get(['transaction_journals.*']);
$paginator = new LengthAwarePaginator($set, $count, 50, $page);
return $paginator;
//return Paginator::make($items, $count, 50);
}
/**
* @param Account $account
*
* @return TransactionJournal|null
*/
public function openingBalanceTransaction(Account $account)
{
return TransactionJournal::accountIs($account)
->orderBy('transaction_journals.date', 'ASC')
->orderBy('created_at', 'ASC')
->first(['transaction_journals.*']);
}
/**
* @param array $data
*
* @return Account;
*/
public function store(array $data)
{
$newAccount = $this->_store($data);
$this->_storeMetadata($newAccount, $data);
// continue with the opposing account:
if ($data['openingBalance'] != 0) {
$type = $data['openingBalance'] < 0 ? 'expense' : 'revenue';
$opposingData = [
'user' => $data['user'],
'accountType' => $type,
'name' => $data['name'] . ' initial balance',
'active' => false,
];
$opposing = $this->_store($opposingData);
$this->_storeInitialBalance($newAccount, $opposing, $data);
}
return $newAccount;
}
/**
* @param Account $account
* @param array $data
*/
public function update(Account $account, array $data)
{
// update the account:
$account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false;
$account->save();
// update meta data:
/** @var AccountMeta $meta */
foreach ($account->accountMeta()->get() as $meta) {
if ($meta->name == 'accountRole') {
$meta->data = $data['accountRole'];
$meta->save();
}
}
$openingBalance = $this->openingBalanceTransaction($account);
// if has openingbalance?
if ($data['openingBalance'] != 0) {
// if opening balance, do an update:
if ($openingBalance) {
// update existing opening balance.
$this->_updateInitialBalance($account, $openingBalance, $data);
} else {
// create new opening balance.
$type = $data['openingBalance'] < 0 ? 'expense' : 'revenue';
$opposingData = [
'user' => $data['user'],
'accountType' => $type,
'name' => $data['name'] . ' initial balance',
'active' => false,
];
$opposing = $this->_store($opposingData);
$this->_storeInitialBalance($account, $opposing, $data);
}
} else {
// opening balance is zero, should we delete it?
if ($openingBalance) {
// delete existing opening balance.
$openingBalance->delete();
}
}
return $account;
}
/**
* @param array $data
*
* @return Account
*/
protected function _store(array $data)
{
$type = Config::get('firefly.accountTypeByIdentifier.' . $data['accountType']);
$accountType = AccountType::whereType($type)->first();
$newAccount = new Account(
[
'user_id' => $data['user'],
'account_type_id' => $accountType->id,
'name' => $data['name'],
'active' => $data['active'] === true ? true : false,
]
);
if (!$newAccount->isValid()) {
// does the account already exist?
$existingAccount = Account::where('user_id', $data['user'])->where('account_type_id', $accountType->id)->where('name', $data['name'])->first();
if (!$existingAccount) {
Log::error('Account create error: ' . $newAccount->getErrors()->toJson());
var_dump($newAccount->getErrors()->toArray());
}
$newAccount = $existingAccount;
}
$newAccount->save();
return $newAccount;
}
/**
* @param Account $account
* @param array $data
*/
protected function _storeMetadata(Account $account, array $data)
{
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => 'accountRole',
'data' => $data['accountRole']
]
);
if (!$metaData->isValid()) {
App::abort(500);
}
$metaData->save();
}
/**
* @param Account $account
* @param Account $opposing
* @param array $data
*
* @return TransactionJournal
*/
protected function _storeInitialBalance(Account $account, Account $opposing, array $data)
{
$type = $data['openingBalance'] < 0 ? 'Withdrawal' : 'Deposit';
$transactionType = TransactionType::whereType($type)->first();
$journal = new TransactionJournal(
[
'user_id' => $data['user'],
'transaction_type_id' => $transactionType->id,
'bill_id' => null,
'transaction_currency_id' => $data['openingBalanceCurrency'],
'description' => 'Initial balance for "' . $account->name . '"',
'completed' => true,
'date' => $data['openingBalanceDate'],
'encrypted' => true
]
);
if (!$journal->isValid()) {
App::abort(500);
}
$journal->save();
if ($data['openingBalance'] < 0) {
$firstAccount = $opposing;
$secondAccount = $account;
$firstAmount = $data['openingBalance'] * -1;
$secondAmount = $data['openingBalance'];
} else {
$firstAccount = $account;
$secondAccount = $opposing;
$firstAmount = $data['openingBalance'];
$secondAmount = $data['openingBalance'] * -1;
}
// first transaction: from
$one = new Transaction(
[
'account_id' => $firstAccount->id,
'transaction_journal_id' => $journal->id,
'amount' => $firstAmount
]
);
if (!$one->isValid()) {
App::abort(500);
}
$one->save();
// second transaction: to
$two = new Transaction(
[
'account_id' => $secondAccount->id,
'transaction_journal_id' => $journal->id,
'amount' => $secondAmount
]
);
if (!$two->isValid()) {
App::abort(500);
}
$two->save();
return $journal;
}
/**
* @param Account $account
* @param TransactionJournal $journal
* @param array $data
*
* @return TransactionJournal
*/
protected function _updateInitialBalance(Account $account, TransactionJournal $journal, array $data)
{
$journal->date = $data['openingBalanceDate'];
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($account->id == $transaction->account_id) {
$transaction->amount = $data['openingBalance'];
$transaction->save();
}
if ($account->id != $transaction->account_id) {
$transaction->amount = $data['openingBalance'] * -1;
$transaction->save();
}
}
return $journal;
}
/**
* @param Account $account
*
* @return float
*/
public function leftOnAccount(Account $account)
{
$balance = \Steam::balance($account);
/** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount;
}
return $balance;
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace FireflyIII\Repositories\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
/**
* Interface AccountRepositoryInterface
*
* @package FireflyIII\Repositories\Account
*/
interface AccountRepositoryInterface
{
/**
* @param Account $account
*
* @return boolean
*/
public function destroy(Account $account);
/**
* @param Account $account
* @param int $page
* @param string $range
*
* @return mixed
*/
public function getJournals(Account $account, $page, $range = 'session');
/**
* @param Account $account
*
* @return TransactionJournal|null
*/
public function openingBalanceTransaction(Account $account);
/**
* @param array $data
*
* @return Account
*/
public function store(array $data);
/**
* @param Account $account
* @param array $data
*
* @return Account
*/
public function update(Account $account, array $data);
/**
* @param Account $account
*
* @return float
*/
public function leftOnAccount(Account $account);
}

View File

@@ -0,0 +1,198 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 07:40
*/
namespace FireflyIII\Repositories\Bill;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use Navigation;
use Log;
/**
* Class BillRepository
*
* @package FireflyIII\Repositories\Bill
*/
class BillRepository implements BillRepositoryInterface
{
/**
* @param Bill $bill
*
* @return Carbon
*/
public function nextExpectedMatch(Bill $bill)
{
$finalDate = null;
if ($bill->active == 0) {
return $finalDate;
}
/*
* $today is the start of the next period, to make sure FF3 won't miss anything
* when the current period has a transaction journal.
*/
$today = Navigation::addPeriod(new Carbon, $bill->repeat_freq, 0);
$skip = $bill->skip + 1;
$start = Navigation::startOfPeriod(new Carbon, $bill->repeat_freq);
/*
* go back exactly one month/week/etc because FF3 does not care about 'next'
* bills if they're too far into the past.
*/
$counter = 0;
while ($start <= $today) {
if (($counter % $skip) == 0) {
// do something.
$end = Navigation::endOfPeriod(clone $start, $bill->repeat_freq);
$journalCount = $bill->transactionjournals()->before($end)->after($start)->count();
if ($journalCount == 0) {
$finalDate = clone $start;
break;
}
}
// add period for next round!
$start = Navigation::addPeriod($start, $bill->repeat_freq, 0);
$counter++;
}
return $finalDate;
}
/**
* @param Bill $bill
* @param TransactionJournal $journal
*
* @return bool
*/
public function scan(Bill $bill, TransactionJournal $journal)
{
/*
* Match words.
*/
$wordMatch = false;
$matches = explode(',', $bill->match);
$description = strtolower($journal->description);
Log::debug('Now scanning ' . $description);
/*
* Attach expense account to description for more narrow matching.
*/
if (count($journal->transactions) < 2) {
$transactions = $journal->transactions()->get();
} else {
$transactions = $journal->transactions;
}
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
/** @var Account $account */
$account = $transaction->account()->first();
/** @var AccountType $type */
$type = $account->accountType()->first();
if ($type->type == 'Expense account' || $type->type == 'Beneficiary account') {
$description .= ' ' . strtolower($account->name);
}
}
Log::debug('Final description: ' . $description);
Log::debug('Matches searched: ' . join(':', $matches));
$count = 0;
foreach ($matches as $word) {
if (!(strpos($description, strtolower($word)) === false)) {
$count++;
}
}
if ($count >= count($matches)) {
$wordMatch = true;
Log::debug('word match is true');
} else {
Log::debug('Count: ' . $count.', count(matches): ' . count($matches));
}
/*
* Match amount.
*/
$amountMatch = false;
if (count($transactions) > 1) {
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));
$min = floatval($bill->amount_min);
$max = floatval($bill->amount_max);
if ($amount >= $min && $amount <= $max) {
$amountMatch = true;
Log::debug('Amount match is true!');
}
}
/*
* If both, update!
*/
if ($wordMatch && $amountMatch) {
Log::debug('TOTAL match is true!');
$journal->bill()->associate($bill);
$journal->save();
}
}
/**
* @param array $data
*
* @return Bill
*/
public function store(array $data)
{
$bill = Bill::create(
[
'name' => $data['name'],
'match' => $data['match'],
'amount_min' => $data['amount_min'],
'user_id' => $data['user'],
'amount_max' => $data['amount_max'],
'date' => $data['date'],
'repeat_freq' => $data['repeat_freq'],
'skip' => $data['skip'],
'automatch' => $data['automatch'],
'active' => $data['active'],
]
);
return $bill;
}
/**
* @param Bill $bill
* @param array $data
*
* @return Bill|static
*/
public function update(Bill $bill, array $data)
{
$bill->name = $data['name'];
$bill->match = $data['match'];
$bill->amount_min = $data['amount_min'];
$bill->amount_max = $data['amount_max'];
$bill->date = $data['date'];
$bill->repeat_freq = $data['repeat_freq'];
$bill->skip = $data['skip'];
$bill->automatch = $data['automatch'];
$bill->active = $data['active'];
$bill->save();
return $bill;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 07:40
*/
namespace FireflyIII\Repositories\Bill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
/**
* Interface BillRepositoryInterface
*
* @package FireflyIII\Repositories\Bill
*/
interface BillRepositoryInterface {
/**
* @param Bill $bill
*
* @return Carbon|null
*/
public function nextExpectedMatch(Bill $bill);
/**
* @param array $data
*
* @return Bill
*/
public function store(array $data);
/**
* @param Bill $bill
* @param array $data
*
* @return mixed
*/
public function update(Bill $bill, array $data);
/**
* @param Bill $bill
* @param TransactionJournal $journal
*
* @return bool
*/
public function scan(Bill $bill, TransactionJournal $journal);
}

View File

@@ -0,0 +1,142 @@
<?php
namespace FireflyIII\Repositories\Budget;
use Carbon\Carbon;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* Class BudgetRepository
*
* @package FireflyIII\Repositories\Budget
*/
class BudgetRepository implements BudgetRepositoryInterface
{
/**
* @param Budget $budget
*
* @return boolean
*/
public function destroy(Budget $budget)
{
$budget->delete();
return true;
}
/**
* Returns all the transaction journals for a limit, possibly limited by a limit repetition.
*
* @param Budget $budget
* @param LimitRepetition $repetition
* @param int $take
*
* @return \Illuminate\Pagination\Paginator
*/
public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50)
{
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $take : 0;
$setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset)->orderBy('date', 'DESC');
$countQuery = $budget->transactionJournals();
if (!is_null($repetition->id)) {
$setQuery->after($repetition->startdate)->before($repetition->enddate);
$countQuery->after($repetition->startdate)->before($repetition->enddate);
}
$set = $setQuery->get(['transaction_journals.*']);
$count = $countQuery->count();
return new LengthAwarePaginator($set, $count, $take, $offset);
}
/**
* @param Budget $budget
* @param Carbon $date
*
* @return float
*/
public function spentInMonth(Budget $budget, Carbon $date)
{
$end = clone $date;
$date->startOfMonth();
$end->endOfMonth();
$sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1;
return $sum;
}
/**
* @param array $data
*
* @return Budget
*/
public function store(array $data)
{
$newBudget = new Budget(
[
'user_id' => $data['user'],
'name' => $data['name'],
]
);
$newBudget->save();
return $newBudget;
}
/**
* @param Budget $budget
* @param array $data
*
* @return Budget
*/
public function update(Budget $budget, array $data)
{
// update the account:
$budget->name = $data['name'];
$budget->save();
return $budget;
}
/**
* @param Budget $budget
* @param Carbon $date
* @param $amount
*
* @return LimitRepetition|null
*/
public function updateLimitAmount(Budget $budget, Carbon $date, $amount)
{
/** @var BudgetLimit $limit */
$limit = $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']);
if (!$limit) {
// create one!
$limit = new BudgetLimit;
$limit->budget()->associate($budget);
$limit->startdate = $date;
$limit->amount = $amount;
$limit->repeat_freq = 'monthly';
$limit->repeats = 0;
$limit->save();
} else {
if ($amount > 0) {
$limit->amount = $amount;
$limit->save();
} else {
$limit->delete();
}
}
return $limit;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace FireflyIII\Repositories\Budget;
use Carbon\Carbon;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
/**
* Interface BudgetRepositoryInterface
*
* @package FireflyIII\Repositories\Budget
*/
interface BudgetRepositoryInterface
{
/**
* @param Budget $budget
*
* @return boolean
*/
public function destroy(Budget $budget);
/**
* @param Budget $budget
* @param Carbon $date
*
* @return float
*/
public function spentInMonth(Budget $budget, Carbon $date);
/**
* @param Budget $budget
* @param Carbon $date
* @param $amount
*
* @return mixed
*/
public function updateLimitAmount(Budget $budget, Carbon $date, $amount);
/**
* @param array $data
*
* @return Budget
*/
public function store(array $data);
/**
* @param Budget $budget
* @param array $data
*
* @return Budget
*/
public function update(Budget $budget, array $data);
/**
* Returns all the transaction journals for a limit, possibly limited by a limit repetition.
*
* @param Budget $budget
* @param LimitRepetition $repetition
* @param int $take
*
* @return \Illuminate\Pagination\Paginator
*/
public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50);
}

View File

@@ -0,0 +1,61 @@
<?php
namespace FireflyIII\Repositories\Category;
use FireflyIII\Models\Category;
/**
* Class CategoryRepository
*
* @package FireflyIII\Repositories\Category
*/
class CategoryRepository implements CategoryRepositoryInterface
{
/**
* @param Category $category
*
* @return boolean
*/
public function destroy(Category $category)
{
$category->delete();
return true;
}
/**
* @param array $data
*
* @return Category
*/
public function store(array $data)
{
$newCategory = new Category(
[
'user_id' => $data['user'],
'name' => $data['name'],
]
);
$newCategory->save();
return $newCategory;
}
/**
* @param Category $category
* @param array $data
*
* @return Category
*/
public function update(Category $category, array $data)
{
// update the account:
$category->name = $data['name'];
$category->save();
return $category;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace FireflyIII\Repositories\Category;
use FireflyIII\Models\Category;
/**
* Interface CategoryRepositoryInterface
*
* @package FireflyIII\Repositories\Category
*/
interface CategoryRepositoryInterface
{
/**
* @param Category $category
*
* @return boolean
*/
public function destroy(Category $category);
/**
* @param array $data
*
* @return Category
*/
public function store(array $data);
/**
* @param Category $category
* @param array $data
*
* @return Category
*/
public function update(Category $category, array $data);
}

View File

@@ -0,0 +1,287 @@
<?php
namespace FireflyIII\Repositories\Journal;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
/**
* Class JournalRepository
*
* @package FireflyIII\Repositories\Journal
*/
class JournalRepository implements JournalRepositoryInterface
{
/**
*
* Get the account_id, which is the asset account that paid for the transaction.
*
* @param TransactionJournal $journal
*
* @return mixed
*/
public function getAssetAccount(TransactionJournal $journal)
{
$positive = true; // the asset account is in the transaction with the positive amount.
switch ($journal->transactionType->type) {
case 'Withdrawal':
$positive = false;
break;
}
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if (floatval($transaction->amount) > 0 && $positive === true) {
return $transaction->account_id;
}
if (floatval($transaction->amount) < 0 && $positive === false) {
return $transaction->account_id;
}
}
return $journal->transactions()->first()->account_id;
}
/**
* @param string $query
* @param TransactionJournal $journal
*
* @return Collection
*/
public function searchRelated($query, TransactionJournal $journal)
{
$start = clone $journal->date;
$end = clone $journal->date;
$start->startOfMonth();
$end->endOfMonth();
// get already related transactions:
$exclude = [$journal->id];
foreach ($journal->transactiongroups()->get() as $group) {
foreach ($group->transactionjournals()->get() as $current) {
$exclude[] = $current->id;
}
}
$exclude = array_unique($exclude);
/** @var Collection $collection */
$collection = Auth::user()->transactionjournals()
->withRelevantData()
->before($end)->after($start)->where('encrypted', 0)
->whereNotIn('id', $exclude)
->where('description', 'LIKE', '%' . $query . '%')
->get();
// manually search encrypted entries:
/** @var Collection $encryptedCollection */
$encryptedCollection = Auth::user()->transactionjournals()
->withRelevantData()
->before($end)->after($start)
->where('encrypted', 1)
->whereNotIn('id', $exclude)
->get();
$encrypted = $encryptedCollection->filter(
function (TransactionJournal $journal) use ($query) {
$strPos = strpos(strtolower($journal->description), strtolower($query));
if ($strPos !== false) {
return $journal;
}
return null;
}
);
return $collection->merge($encrypted);
}
/**
* @param array $data
*
* @return TransactionJournal
*/
public function store(array $data)
{
// find transaction type.
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
// store actual journal.
$journal = new TransactionJournal(
[
'user_id' => $data['user'],
'transaction_type_id' => $transactionType->id,
'transaction_currency_id' => $data['amount_currency_id'],
'description' => $data['description'],
'completed' => 0,
'date' => $data['date'],
]
);
$journal->save();
// store or get category
if (strlen($data['category']) > 0) {
$category = Category::firstOrCreate(['name' => $data['category'], 'user_id' => $data['user']]);
$journal->categories()->save($category);
}
// store or get budget
if (intval($data['budget_id']) > 0) {
$budget = Budget::find($data['budget_id']);
$journal->budgets()->save($budget);
}
// store accounts (depends on type)
switch ($transactionType->type) {
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
// store accompanying transactions.
Transaction::create( // first transaction.
[
'account_id' => $from->id,
'transaction_journal_id' => $journal->id,
'amount' => $data['amount'] * -1
]
);
Transaction::create( // second transaction.
[
'account_id' => $to->id,
'transaction_journal_id' => $journal->id,
'amount' => $data['amount']
]
);
$journal->completed = 1;
$journal->save();
return $journal;
}
/**
* @param TransactionJournal $journal
* @param array $data
*
* @return mixed
*/
public function update(TransactionJournal $journal, array $data)
{
// update actual journal.
$journal->transaction_currency_id = $data['amount_currency_id'];
$journal->description = $data['description'];
$journal->date = $data['date'];
// unlink all categories, recreate them:
$journal->categories()->detach();
if (strlen($data['category']) > 0) {
$category = Category::firstOrCreate(['name' => $data['category'], 'user_id' => $data['user']]);
$journal->categories()->save($category);
}
// unlink all budgets and recreate them:
$journal->budgets()->detach();
if (intval($data['budget_id']) > 0) {
$budget = Budget::find($data['budget_id']);
$journal->budgets()->save($budget);
}
// store accounts (depends on type)
switch ($journal->transactionType->type) {
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
// update the from and to transaction.
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id === $from->id) {
// this is the from transaction, negative amount:
$transaction->amount = $data['amount'] * -1;
$transaction->save();
}
if ($transaction->account_id === $to->id) {
$transaction->amount = $data['amount'];
$transaction->save();
}
}
$journal->save();
return $journal;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FireflyIII\Repositories\Journal;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* Interface JournalRepositoryInterface
*
* @package FireflyIII\Repositories\Journal
*/
interface JournalRepositoryInterface
{
/**
*
* Get the account_id, which is the asset account that paid for the transaction.
*
* @param TransactionJournal $journal
*
* @return int
*/
public function getAssetAccount(TransactionJournal $journal);
/**
* @param string $query
* @param TransactionJournal $journal
*
* @return Collection
*/
public function searchRelated($query, TransactionJournal $journal);
/**
* @param array $data
*
* @return TransactionJournal
*/
public function store(array $data);
/**
* @param TransactionJournal $journal
* @param array $data
*
* @return mixed
*/
public function update(TransactionJournal $journal, array $data);
}

View File

@@ -0,0 +1,129 @@
<?php
namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use Illuminate\Support\Collection;
use Navigation;
/**
* Class PiggyBankRepository
*
* @package FireflyIII\Repositories\PiggyBank
*/
class PiggyBankRepository implements PiggyBankRepositoryInterface
{
/**
* @param array $data
*
* @return PiggyBank
*/
public function store(array $data)
{
$piggyBank = PiggyBank::create($data);
return $piggyBank;
}
/**
* @param PiggyBank $account
* @param array $data
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data)
{
/**
'rep_length' => $request->get('rep_length'),
'rep_every' => intval($request->get('rep_every')),
'rep_times' => intval($request->get('rep_times')),
'remind_me' => intval($request->get('remind_me')) == 1 ? true : false ,
'reminder' => $request->get('reminder'),
*/
$piggyBank->name = $data['name'];
$piggyBank->account_id = intval($data['account_id']);
$piggyBank->targetamount = floatval($data['targetamount']);
$piggyBank->targetdate = $data['targetdate'];
$piggyBank->reminder = $data['reminder'];
$piggyBank->rep_length = isset($data['rep_length']) ? $data['rep_length'] : null;
$piggyBank->rep_every =isset($data['rep_every']) ? $data['rep_every'] : null;
$piggyBank->rep_times = isset($data['rep_times']) ? $data['rep_times'] : null;
$piggyBank->remind_me = isset($data['remind_me']) ? $data['remind_me'] : null;
$piggyBank->save();
return $piggyBank;
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition)
{
/** @var PiggyBank $piggyBank */
$piggyBank = $repetition->piggyBank()->first();
$bars = new Collection;
$currentStart = clone $repetition->startdate;
if (is_null($piggyBank->reminder)) {
$entry = ['repetition' => $repetition, 'amountPerBar' => floatval($piggyBank->targetamount),
'currentAmount' => floatval($repetition->currentamount), 'cumulativeAmount' => floatval($piggyBank->targetamount),
'startDate' => clone $repetition->startdate, 'targetDate' => clone $repetition->targetdate];
$bars->push($this->createPiggyBankPart($entry));
return $bars;
}
while ($currentStart < $repetition->targetdate) {
$currentTarget = Navigation::endOfX($currentStart, $piggyBank->reminder, $repetition->targetdate);
$entry = ['repetition' => $repetition, 'amountPerBar' => null, 'currentAmount' => floatval($repetition->currentamount),
'cumulativeAmount' => null, 'startDate' => $currentStart, 'targetDate' => $currentTarget];
$bars->push($this->createPiggyBankPart($entry));
$currentStart = clone $currentTarget;
$currentStart->addDay();
}
$amountPerBar = floatval($piggyBank->targetamount) / $bars->count();
$cumulative = $amountPerBar;
/** @var PiggyBankPart $bar */
foreach ($bars as $index => $bar) {
$bar->setAmountPerBar($amountPerBar);
$bar->setCumulativeAmount($cumulative);
if ($bars->count() - 1 == $index) {
$bar->setCumulativeAmount($piggyBank->targetamount);
}
$cumulative += $amountPerBar;
}
return $bars;
}
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data)
{
$part = new PiggyBankPart;
$part->setRepetition($data['repetition']);
$part->setAmountPerBar($data['amountPerBar']);
$part->setCurrentamount($data['currentAmount']);
$part->setCumulativeAmount($data['cumulativeAmount']);
$part->setStartdate($data['startDate']);
$part->setTargetdate($data['targetDate']);
return $part;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use Illuminate\Support\Collection;
/**
* Interface PiggyBankRepositoryInterface
*
* @package FireflyIII\Repositories\PiggyBank
*/
interface PiggyBankRepositoryInterface {
/**
* @param array $data
*
* @return PiggyBank
*/
public function store(array $data);
/**
* @param PiggyBank $account
* @param array $data
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data);
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition);
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data);
}

View File

@@ -0,0 +1,181 @@
<?php
namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\PiggyBankRepetition;
use Carbon\Carbon;
/**
* Class PiggyBankPart
*
* @package FireflyIII\Collection
*/
class PiggyBankPart
{
/** @var float */
public $amountPerBar;
/** @var float */
public $cumulativeAmount;
/** @var float */
public $currentamount;
/** @var Reminder */
public $reminder;
/** @var PiggyBankRepetition */
public $repetition;
/** @var Carbon */
public $startdate;
/** @var Carbon */
public $targetdate;
/**
* @return Reminder
*/
public function getReminder()
{
if (is_null($this->reminder)) {
$this->reminder = $this->repetition->piggyBank->reminders()->where('startdate', $this->getStartdate()->format('Y-m-d'))->where(
'enddate', $this->getTargetdate()->format('Y-m-d')
)->first();
}
return $this->reminder;
}
/**
* @param Reminder $reminder
*/
public function setReminder($reminder)
{
$this->reminder = $reminder;
}
/**
* @return Carbon
*/
public function getStartdate()
{
return $this->startdate;
}
/**
* @param Carbon $startdate
*/
public function setStartdate($startdate)
{
$this->startdate = $startdate;
}
/**
* @return Carbon
*/
public function getTargetdate()
{
return $this->targetdate;
}
/**
* @param Carbon $targetdate
*/
public function setTargetdate($targetdate)
{
$this->targetdate = $targetdate;
}
/**
* @return PiggyBankRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param PiggyBankRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return bool
*/
public function hasReminder()
{
return !is_null($this->reminder);
}
/**
* @return float|int
*/
public function percentage()
{
if ($this->getCurrentamount() < $this->getCumulativeAmount()) {
$pct = 0;
// calculate halfway point?
if ($this->getCumulativeAmount() - $this->getCurrentamount() < $this->getAmountPerBar()) {
$left = $this->getCurrentamount() % $this->getAmountPerBar();
$pct = round($left / $this->getAmountPerBar() * 100);
}
return $pct;
} else {
return 100;
}
}
/**
* @return float
*/
public function getCurrentamount()
{
return $this->currentamount;
}
/**
* @param float $currentamount
*/
public function setCurrentamount($currentamount)
{
$this->currentamount = $currentamount;
}
/**
* @return float
*/
public function getCumulativeAmount()
{
return $this->cumulativeAmount;
}
/**
* @param float $cumulativeAmount
*/
public function setCumulativeAmount($cumulativeAmount)
{
$this->cumulativeAmount = $cumulativeAmount;
}
/**
* @return float
*/
public function getAmountPerBar()
{
return $this->amountPerBar;
}
/**
* @param float $amountPerBar
*/
public function setAmountPerBar($amountPerBar)
{
$this->amountPerBar = $amountPerBar;
}
}

View File

@@ -0,0 +1,49 @@
<?php namespace FireflyIII\Services;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Registrar as RegistrarContract;
use Validator;
/**
* Class Registrar
*
* @package FireflyIII\Services
*/
class Registrar implements RegistrarContract
{
/**
* Create a new user instance after a valid registration.
*
* @param array $data
*
* @return User
*/
public function create(array $data)
{
return User::create(
[
'email' => $data['email'],
'password' => $data['password'],
]
);
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
*
* @return \Illuminate\Contracts\Validation\Validator
*/
public function validator(array $data)
{
return Validator::make(
$data, [
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]
);
}
}

133
app/Support/Amount.php Normal file
View File

@@ -0,0 +1,133 @@
<?php
namespace FireflyIII\Support;
use Cache;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use Preferences as Prefs;
/**
* Class Amount
*
* @package FireflyIII\Support
*/
class Amount
{
/**
* @param $amount
* @param bool $coloured
*
* @return string
*/
public function format($amount, $coloured = true)
{
$currencySymbol = $this->getCurrencySymbol();
return $this->formatWithSymbol($currencySymbol, $amount, $coloured);
}
/**
* @param Transaction|\Transaction $transaction
* @param bool $coloured
*
* @return string
*/
public function formatTransaction(Transaction $transaction, $coloured = true)
{
$symbol = $transaction->transactionJournal->transactionCurrency->symbol;
$amount = floatval($transaction->amount);
return $this->formatWithSymbol($symbol, $amount, $coloured);
}
/**
* @param string $symbol
* @param float $amount
* @param bool $coloured
*
* @return string
*/
public function formatWithSymbol($symbol, $amount, $coloured = true)
{
$amount = floatval($amount);
$amount = round($amount, 2);
$string = number_format($amount, 2, ',', '.');
if ($coloured === true) {
if ($amount === 0.0) {
return '<span style="color:#999">' . $symbol . ' ' . $string . '</span>';
}
if ($amount > 0) {
return '<span class="text-success">' . $symbol . ' ' . $string . '</span>';
}
return '<span class="text-danger">' . $symbol . ' ' . $string . '</span>';
}
// &#8364;
return $symbol . ' ' . $string;
}
/**
* @return string
*/
public function getCurrencySymbol()
{
if (defined('FFCURRENCYSYMBOL')) {
return FFCURRENCYSYMBOL;
}
if (\Cache::has('FFCURRENCYSYMBOL')) {
define('FFCURRENCYSYMBOL', \Cache::get('FFCURRENCYSYMBOL'));
return FFCURRENCYSYMBOL;
}
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
\Cache::forever('FFCURRENCYSYMBOL', $currency->symbol);
define('FFCURRENCYSYMBOL', $currency->symbol);
return $currency->symbol;
}
/**
* @return string
*/
public function getCurrencyCode()
{
if (defined('FFCURRENCYCODE')) {
return FFCURRENCYCODE;
}
if (Cache::has('FFCURRENCYCODE')) {
define('FFCURRENCYCODE', Cache::get('FFCURRENCYCODE'));
return FFCURRENCYCODE;
}
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
\Cache::forever('FFCURRENCYCODE', $currency->code);
define('FFCURRENCYCODE', $currency->code);
return $currency->code;
}
public function getDefaultCurrency()
{
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
return $currency;
}
}

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