Compare commits

...

199 Commits
3.2.0 ... 3.2.3

Author SHA1 Message Date
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
440 changed files with 17666 additions and 20600 deletions

View File

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

14
.gitignore vendored
View File

@@ -1,10 +1,8 @@
/bootstrap/compiled.php
/vendor
composer.phar
composer.lock
.env.*.php
.env.php
.DS_Store
Thumbs.db
.idea/
tests/_output/*
@@ -14,8 +12,16 @@ index.html*
app/storage/firefly-export*
.vagrant
firefly-iii-import-*.json
tests/_output/*
testing.sqlite
c3.php
_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

View File

@@ -3,13 +3,14 @@ language: php
php:
- 5.5
- 5.6
- hhvm
install:
- composer install
script:
- php vendor/bin/codecept run
- ./tests/_data/db.sh
- php vendor/bin/codecept build
- php vendor/bin/codecept run --coverage --coverage-xml
after_script:
- php vendor/bin/coveralls
- php vendor/bin/coveralls

View File

@@ -1,8 +1,10 @@
Firefly III
===========
[![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)
[![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.png?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=master)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
[![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)
@@ -17,13 +19,15 @@ 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:
@@ -31,21 +35,20 @@ Everything is organised:
- Easy navigation through your records;
- Browse back and forth to see previous months or even years;
- 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;
- Lots of help text in case you don't get it;
- 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:
@@ -64,13 +67,9 @@ Some stuff has been removed:
![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)!

View File

@@ -0,0 +1,606 @@
# ************************************************************
# Sequel Pro SQL dump
# Version 4096
#
# http://www.sequelpro.com/
# http://code.google.com/p/sequel-pro/
#
# Host: 127.0.0.1 (MySQL 5.6.19-0ubuntu0.14.04.1)
# Database: homestead
# Generation Time: 2015-01-02 19:01:30 +0000
# ************************************************************
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
# Dump of table account_meta
# ------------------------------------------------------------
DROP TABLE IF EXISTS `account_meta`;
CREATE TABLE `account_meta` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`account_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`data` text COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `account_meta_account_id_name_unique` (`account_id`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table account_types
# ------------------------------------------------------------
DROP TABLE IF EXISTS `account_types`;
CREATE TABLE `account_types` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`type` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
`editable` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `account_types_type_unique` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `account_types` WRITE;
/*!40000 ALTER TABLE `account_types` DISABLE KEYS */;
INSERT INTO `account_types` (`id`, `created_at`, `updated_at`, `type`, `editable`)
VALUES
(1,'2015-01-02 19:00:13','2015-01-02 19:00:13','Default account',1),
(2,'2015-01-02 19:00:13','2015-01-02 19:00:13','Cash account',0),
(3,'2015-01-02 19:00:13','2015-01-02 19:00:13','Asset account',1),
(4,'2015-01-02 19:00:13','2015-01-02 19:00:13','Expense account',1),
(5,'2015-01-02 19:00:13','2015-01-02 19:00:13','Revenue account',1),
(6,'2015-01-02 19:00:13','2015-01-02 19:00:13','Initial balance account',0),
(7,'2015-01-02 19:00:13','2015-01-02 19:00:13','Beneficiary account',1),
(8,'2015-01-02 19:00:13','2015-01-02 19:00:13','Import account',0);
/*!40000 ALTER TABLE `account_types` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table accounts
# ------------------------------------------------------------
DROP TABLE IF EXISTS `accounts`;
CREATE TABLE `accounts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
`account_type_id` int(10) unsigned NOT NULL,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`active` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `accounts_user_id_account_type_id_name_unique` (`user_id`,`account_type_id`,`name`),
KEY `accounts_account_type_id_foreign` (`account_type_id`),
CONSTRAINT `accounts_account_type_id_foreign` FOREIGN KEY (`account_type_id`) REFERENCES `account_types` (`id`) ON DELETE CASCADE,
CONSTRAINT `accounts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table bills
# ------------------------------------------------------------
DROP TABLE IF EXISTS `bills`;
CREATE TABLE `bills` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_id` int(10) unsigned NOT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`match` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`amount_min` decimal(10,2) NOT NULL,
`amount_max` decimal(10,2) NOT NULL,
`date` date NOT NULL,
`active` tinyint(1) NOT NULL,
`automatch` tinyint(1) NOT NULL,
`repeat_freq` enum('daily','weekly','monthly','quarterly','half-year','yearly') COLLATE utf8_unicode_ci NOT NULL,
`skip` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uid_name_unique` (`user_id`,`name`),
CONSTRAINT `bills_uid_for` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table budget_limits
# ------------------------------------------------------------
DROP TABLE IF EXISTS `budget_limits`;
CREATE TABLE `budget_limits` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`budget_id` int(10) unsigned DEFAULT NULL,
`startdate` date NOT NULL,
`amount` decimal(10,2) NOT NULL,
`repeats` tinyint(1) NOT NULL,
`repeat_freq` enum('daily','weekly','monthly','quarterly','half-year','yearly') COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_ci_combi` (`startdate`,`repeat_freq`),
UNIQUE KEY `unique_bl_combi` (`budget_id`,`startdate`,`repeat_freq`),
CONSTRAINT `bid_foreign` FOREIGN KEY (`budget_id`) REFERENCES `budgets` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table budget_transaction_journal
# ------------------------------------------------------------
DROP TABLE IF EXISTS `budget_transaction_journal`;
CREATE TABLE `budget_transaction_journal` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`budget_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `budid_tjid_unique` (`budget_id`,`transaction_journal_id`),
KEY `budget_transaction_journal_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `budget_transaction_journal_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE,
CONSTRAINT `budget_transaction_journal_budget_id_foreign` FOREIGN KEY (`budget_id`) REFERENCES `budgets` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table budgets
# ------------------------------------------------------------
DROP TABLE IF EXISTS `budgets`;
CREATE TABLE `budgets` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `budgets_user_id_name_unique` (`user_id`,`name`),
CONSTRAINT `budgets_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table categories
# ------------------------------------------------------------
DROP TABLE IF EXISTS `categories`;
CREATE TABLE `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `categories_user_id_name_unique` (`user_id`,`name`),
CONSTRAINT `categories_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table category_transaction_journal
# ------------------------------------------------------------
DROP TABLE IF EXISTS `category_transaction_journal`;
CREATE TABLE `category_transaction_journal` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`category_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `catid_tjid_unique` (`category_id`,`transaction_journal_id`),
KEY `category_transaction_journal_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `category_transaction_journal_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE,
CONSTRAINT `category_transaction_journal_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table components
# ------------------------------------------------------------
DROP TABLE IF EXISTS `components`;
CREATE TABLE `components` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`class` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `components_user_id_class_name_unique` (`user_id`,`class`,`name`),
CONSTRAINT `components_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table limit_repetitions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `limit_repetitions`;
CREATE TABLE `limit_repetitions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`budget_limit_id` int(10) unsigned NOT NULL,
`startdate` date NOT NULL,
`enddate` date NOT NULL,
`amount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `limit_repetitions_limit_id_startdate_enddate_unique` (`budget_limit_id`,`startdate`,`enddate`),
CONSTRAINT `limit_repetitions_limit_id_foreign` FOREIGN KEY (`budget_limit_id`) REFERENCES `budget_limits` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table migrations
# ------------------------------------------------------------
DROP TABLE IF EXISTS `migrations`;
CREATE TABLE `migrations` (
`migration` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`batch` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `migrations` WRITE;
/*!40000 ALTER TABLE `migrations` DISABLE KEYS */;
INSERT INTO `migrations` (`migration`, `batch`)
VALUES
('2014_06_27_163032_create_users_table',1),
('2014_06_27_163145_create_account_types_table',1),
('2014_06_27_163259_create_accounts_table',1),
('2014_06_27_163817_create_components_table',1),
('2014_06_27_163818_create_piggybanks_table',1),
('2014_06_27_164042_create_transaction_currencies_table',1),
('2014_06_27_164512_create_transaction_types_table',1),
('2014_06_27_164619_create_recurring_transactions_table',1),
('2014_06_27_164620_create_transaction_journals_table',1),
('2014_06_27_164836_create_transactions_table',1),
('2014_06_27_165344_create_component_transaction_table',1),
('2014_07_05_171326_create_component_transaction_journal_table',1),
('2014_07_06_123842_create_preferences_table',1),
('2014_07_09_204843_create_session_table',1),
('2014_07_17_183717_create_limits_table',1),
('2014_07_19_055011_create_limit_repeat_table',1),
('2014_08_06_044416_create_component_recurring_transaction_table',1),
('2014_08_12_173919_create_piggybank_repetitions_table',1),
('2014_08_18_100330_create_piggybank_events_table',1),
('2014_08_23_113221_create_reminders_table',1),
('2014_11_10_172053_create_account_meta_table',1),
('2014_11_29_135749_create_transaction_groups_table',1),
('2014_11_29_140217_create_transaction_group_transaction_journal_table',1),
('2014_12_13_190730_changes_for_v321',1),
('2014_12_24_191544_changes_for_v322',1);
/*!40000 ALTER TABLE `migrations` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table piggy_bank_events
# ------------------------------------------------------------
DROP TABLE IF EXISTS `piggy_bank_events`;
CREATE TABLE `piggy_bank_events` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`piggy_bank_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned DEFAULT NULL,
`date` date NOT NULL,
`amount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
KEY `piggybank_events_piggybank_id_foreign` (`piggy_bank_id`),
KEY `piggybank_events_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `piggybank_events_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE SET NULL,
CONSTRAINT `piggybank_events_piggybank_id_foreign` FOREIGN KEY (`piggy_bank_id`) REFERENCES `piggy_banks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table piggy_bank_repetitions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `piggy_bank_repetitions`;
CREATE TABLE `piggy_bank_repetitions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`piggy_bank_id` int(10) unsigned NOT NULL,
`startdate` date DEFAULT NULL,
`targetdate` date DEFAULT NULL,
`currentamount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `piggybank_repetitions_piggybank_id_startdate_targetdate_unique` (`piggy_bank_id`,`startdate`,`targetdate`),
CONSTRAINT `piggybank_repetitions_piggybank_id_foreign` FOREIGN KEY (`piggy_bank_id`) REFERENCES `piggy_banks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table piggy_banks
# ------------------------------------------------------------
DROP TABLE IF EXISTS `piggy_banks`;
CREATE TABLE `piggy_banks` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`account_id` int(10) unsigned NOT NULL,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`targetamount` decimal(10,2) NOT NULL,
`startdate` date DEFAULT NULL,
`targetdate` date DEFAULT NULL,
`repeats` tinyint(1) NOT NULL,
`rep_length` enum('day','week','quarter','month','year') COLLATE utf8_unicode_ci DEFAULT NULL,
`rep_every` smallint(5) unsigned NOT NULL,
`rep_times` smallint(5) unsigned DEFAULT NULL,
`reminder` enum('day','week','quarter','month','year') COLLATE utf8_unicode_ci DEFAULT NULL,
`reminder_skip` smallint(5) unsigned NOT NULL,
`remind_me` tinyint(1) NOT NULL,
`order` int(10) unsigned NOT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `piggybanks_account_id_name_unique` (`account_id`,`name`),
CONSTRAINT `piggybanks_account_id_foreign` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table preferences
# ------------------------------------------------------------
DROP TABLE IF EXISTS `preferences`;
CREATE TABLE `preferences` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`data` text COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `preferences_user_id_name_unique` (`user_id`,`name`),
CONSTRAINT `preferences_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table reminders
# ------------------------------------------------------------
DROP TABLE IF EXISTS `reminders`;
CREATE TABLE `reminders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_id` int(10) unsigned NOT NULL,
`startdate` date NOT NULL,
`enddate` date DEFAULT NULL,
`active` tinyint(1) NOT NULL,
`notnow` tinyint(1) NOT NULL DEFAULT '0',
`remindersable_id` int(10) unsigned DEFAULT NULL,
`remindersable_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `reminders_user_id_foreign` (`user_id`),
CONSTRAINT `reminders_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table sessions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `sessions`;
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`payload` text COLLATE utf8_unicode_ci NOT NULL,
`last_activity` int(11) NOT NULL,
UNIQUE KEY `sessions_id_unique` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_currencies
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_currencies`;
CREATE TABLE `transaction_currencies` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`code` varchar(3) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(48) COLLATE utf8_unicode_ci DEFAULT NULL,
`symbol` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `transaction_currencies_code_unique` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `transaction_currencies` WRITE;
/*!40000 ALTER TABLE `transaction_currencies` DISABLE KEYS */;
INSERT INTO `transaction_currencies` (`id`, `created_at`, `updated_at`, `deleted_at`, `code`, `name`, `symbol`)
VALUES
(1,'2015-01-02 19:00:13','2015-01-02 19:00:13',NULL,'EUR','Euro','€'),
(2,'2015-01-02 19:00:13','2015-01-02 19:00:13',NULL,'USD','US Dollar','$'),
(3,'2015-01-02 19:00:13','2015-01-02 19:00:13',NULL,'HUF','Hungarian forint','Ft');
/*!40000 ALTER TABLE `transaction_currencies` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table transaction_group_transaction_journal
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_group_transaction_journal`;
CREATE TABLE `transaction_group_transaction_journal` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`transaction_group_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tt_joined` (`transaction_group_id`,`transaction_journal_id`),
KEY `tr_trj_id` (`transaction_journal_id`),
CONSTRAINT `tr_trj_id` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE,
CONSTRAINT `tr_grp_id` FOREIGN KEY (`transaction_group_id`) REFERENCES `transaction_groups` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_groups
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_groups`;
CREATE TABLE `transaction_groups` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
`relation` enum('balance') COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `transaction_groups_user_id_foreign` (`user_id`),
CONSTRAINT `transaction_groups_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_journals
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_journals`;
CREATE TABLE `transaction_journals` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
`transaction_type_id` int(10) unsigned NOT NULL,
`bill_id` int(10) unsigned DEFAULT NULL,
`transaction_currency_id` int(10) unsigned NOT NULL,
`description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`completed` tinyint(1) NOT NULL,
`date` date NOT NULL,
PRIMARY KEY (`id`),
KEY `transaction_journals_user_id_foreign` (`user_id`),
KEY `transaction_journals_transaction_type_id_foreign` (`transaction_type_id`),
KEY `transaction_journals_transaction_currency_id_foreign` (`transaction_currency_id`),
KEY `bill_id_foreign` (`bill_id`),
CONSTRAINT `bill_id_foreign` FOREIGN KEY (`bill_id`) REFERENCES `bills` (`id`) ON DELETE SET NULL,
CONSTRAINT `transaction_journals_transaction_currency_id_foreign` FOREIGN KEY (`transaction_currency_id`) REFERENCES `transaction_currencies` (`id`) ON DELETE CASCADE,
CONSTRAINT `transaction_journals_transaction_type_id_foreign` FOREIGN KEY (`transaction_type_id`) REFERENCES `transaction_types` (`id`) ON DELETE CASCADE,
CONSTRAINT `transaction_journals_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_types
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_types`;
CREATE TABLE `transaction_types` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`type` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `transaction_types_type_unique` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `transaction_types` WRITE;
/*!40000 ALTER TABLE `transaction_types` DISABLE KEYS */;
INSERT INTO `transaction_types` (`id`, `created_at`, `updated_at`, `deleted_at`, `type`)
VALUES
(1,'2015-01-02 19:00:13','2015-01-02 19:00:13',NULL,'Withdrawal'),
(2,'2015-01-02 19:00:13','2015-01-02 19:00:13',NULL,'Deposit'),
(3,'2015-01-02 19:00:13','2015-01-02 19:00:13',NULL,'Transfer'),
(4,'2015-01-02 19:00:13','2015-01-02 19:00:13',NULL,'Opening balance');
/*!40000 ALTER TABLE `transaction_types` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table transactions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transactions`;
CREATE TABLE `transactions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`account_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
`description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`amount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
KEY `transactions_account_id_foreign` (`account_id`),
KEY `transactions_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `transactions_account_id_foreign` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE,
CONSTRAINT `transactions_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table users
# ------------------------------------------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`email` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
`reset` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
`remember_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

View File

@@ -17,347 +17,353 @@ Breadcrumbs::register(
// accounts
Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
$breadcrumbs->push(ucfirst($what) . ' accounts', route('accounts.index', $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 'Expense account':
case 'Beneficiary account':
$what = 'expense';
break;
case 'Revenue account':
$what = 'revenue';
break;
}
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push($account->name, route('accounts.show', $account->id));
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 ' . $account->name, route('accounts.delete', $account->id));
}
$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 ' . $account->name, route('accounts.edit', $account->id));
}
$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->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->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 ' . $budget->name, route('budgets.edit', $budget->id));
}
$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 ' . $budget->name, route('budgets.delete', $budget->id));
}
$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($budget->name, route('budgets.show', $budget->id));
if (!is_null($repetition)) {
$breadcrumbs->push(
DateKit::periodShow($repetition->startdate, $repetition->limit->repeat_freq), route('budgets.show', $budget->id, $repetition->id)
);
}
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push(e($budget->name), route('budgets.show', $budget->id));
if (!is_null($repetition)) {
$breadcrumbs->push(
DateKit::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->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->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 ' . $category->name, route('categories.edit', $category->id));
}
$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 ' . $category->name, route('categories.delete', $category->id));
}
$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($category->name, route('categories.show', $category->id));
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name), route('categories.show', $category->id));
}
}
);
// piggy banks
Breadcrumbs::register(
'piggybanks.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Piggy banks', route('piggybanks.index'));
}
'piggyBanks.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Piggy banks', route('piggyBanks.index'));
}
);
Breadcrumbs::register(
'piggybanks.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('piggybanks.index');
$breadcrumbs->push('Create new piggy bank', route('piggybanks.create'));
}
'piggyBanks.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('piggyBanks.index');
$breadcrumbs->push('Create new piggy bank', route('piggyBanks.create'));
}
);
Breadcrumbs::register(
'piggybanks.edit', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('piggybanks.show', $piggybank);
$breadcrumbs->push('Edit ' . $piggybank->name, route('piggybanks.edit', $piggybank->id));
}
'piggyBanks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggyBanks.show', $piggyBank);
$breadcrumbs->push('Edit ' . e($piggyBank->name), route('piggyBanks.edit', $piggyBank->id));
}
);
Breadcrumbs::register(
'piggybanks.delete', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('piggybanks.show', $piggybank);
$breadcrumbs->push('Delete ' . $piggybank->name, route('piggybanks.delete', $piggybank->id));
}
'piggyBanks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggyBanks.show', $piggyBank);
$breadcrumbs->push('Delete ' . e($piggyBank->name), route('piggyBanks.delete', $piggyBank->id));
}
);
Breadcrumbs::register(
'piggybanks.show', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('piggybanks.index');
$breadcrumbs->push($piggybank->name, route('piggybanks.show', $piggybank->id));
'piggyBanks.show', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggyBanks.index');
$breadcrumbs->push(e($piggyBank->name), route('piggyBanks.show', $piggyBank->id));
}
}
);
// preferences
Breadcrumbs::register(
'preferences', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Preferences', route('preferences'));
$breadcrumbs->parent('home');
$breadcrumbs->push('Preferences', route('preferences'));
}
}
);
// profile
Breadcrumbs::register(
'profile', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Profile', route('profile'));
$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'));
$breadcrumbs->parent('profile');
$breadcrumbs->push('Change your password', route('change-password'));
}
}
);
// recurring transactions
// bills
Breadcrumbs::register(
'recurring.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Recurring transactions', route('recurring.index'));
}
'bills.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Bills', route('bills.index'));
}
);
Breadcrumbs::register(
'recurring.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('recurring.index');
$breadcrumbs->push('Create new recurring transaction', route('recurring.create'));
}
'bills.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('bills.index');
$breadcrumbs->push('Create new bill', route('bills.create'));
}
);
Breadcrumbs::register(
'recurring.edit', function (Generator $breadcrumbs, RecurringTransaction $recurring) {
$breadcrumbs->parent('recurring.show', $recurring);
$breadcrumbs->push('Edit ' . $recurring->name, route('recurring.edit', $recurring->id));
}
'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(
'recurring.delete', function (Generator $breadcrumbs, RecurringTransaction $recurring) {
$breadcrumbs->parent('recurring.show', $recurring);
$breadcrumbs->push('Delete ' . $recurring->name, route('recurring.delete', $recurring->id));
}
'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(
'recurring.show', function (Generator $breadcrumbs, RecurringTransaction $recurring) {
$breadcrumbs->parent('recurring.index');
$breadcrumbs->push($recurring->name, route('recurring.show', $recurring->id));
'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));
$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->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->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 ' . $piggybank->name, route('repeated.edit', $piggybank->id));
}
'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 ' . $piggybank->name, route('repeated.delete', $piggybank->id));
}
'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($piggybank->name, route('repeated.show', $piggybank->id));
'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->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->parent('reports.index');
$breadcrumbs->push($date->format('Y'), route('reports.year', $date->format('Y')));
}
);
Breadcrumbs::register(
'reports.budgets', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push('Budgets in ' . $date->format('F Y'), route('reports.budgets', $date->format('Y')));
}
'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.unbalanced', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push('Unbalanced transactions in ' . $date->format('F Y'), route('reports.unbalanced', $date->format('Y')));
}
'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'));
}
$breadcrumbs->parent('home');
$breadcrumbs->push('Search for "' . e($query) . '"', route('search'));
}
);
// transactions
Breadcrumbs::register(
'transactions.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
$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));
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 ' . $what, route('transactions.create', $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 ' . $journal->description, route('transactions.edit', $journal ->id));
}
$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 ' . $journal->description, route('transactions.delete', $journal->id));
}
$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($journal->description, route('transactions.show', $journal->id));
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
$breadcrumbs->push(e($journal->description), route('transactions.show', $journal->id));
}
);
}
);

View File

@@ -1,9 +1,10 @@
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* Class Cleanup
*/
class Cleanup extends Command
{
@@ -21,9 +22,7 @@ class Cleanup extends Command
protected $name = 'firefly:cleanup';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
@@ -42,7 +41,7 @@ class Cleanup extends Command
$this->info('Cleared compiled...');
Artisan::call('ide-helper:generate');
$this->info('IDE helper, done...');
Artisan::call('ide-helper:models', ['write']);
Artisan::call('ide-helper:models');
$this->info('IDE models, done...');
Artisan::call('optimize');
$this->info('Optimized...');

View File

@@ -1,4 +1,4 @@
local/
laptop/
vagrant/
production/
production/

View File

@@ -1,3 +1,3 @@
<?php
return [];
return [];

View File

@@ -1,4 +1,4 @@
<?php
return [
'import' => ''
];
];

View File

@@ -3,7 +3,7 @@
return [
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'piggybank_periods' => [
'piggy_bank_periods' => [
'week' => 'Week',
'month' => 'Month',
'quarter' => 'Quarter',
@@ -46,4 +46,4 @@ return [
'6M' => 'half-year',
'custom' => 'monthly'
],
];
];

View File

@@ -12,4 +12,4 @@ return [
*/
'debug' => true,
'log_level' => 'debug',
];
];

View File

@@ -2,22 +2,7 @@
return [
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'default' => 'mysql',
'connections' => [
'mysql' => [
@@ -30,6 +15,11 @@ return [
'collation' => 'utf8_unicode_ci',
'prefix' => '',
],
'sqlite' => [
'driver' => 'sqlite',
'database' => realpath(__DIR__.'/../../../tests/_data/testing.sqlite'),
'prefix' => ''
],
'pgsql' => [
'driver' => 'pgsql',
@@ -44,4 +34,4 @@ return [
],
];
];

View File

@@ -1,134 +0,0 @@
<?php
/**
* This file is part of the TwigBridge package.
*
* @copyright Robert Crowe <hello@vivalacrowe.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Configuration options for the built-in extensions.
*/
return [
/*
|--------------------------------------------------------------------------
| Extensions
|--------------------------------------------------------------------------
|
| Enabled extensions.
|
| `Twig_Extension_Debug` is enabled automatically if twig.debug is TRUE.
|
*/
'enabled' => [
'TwigBridge\Extension\Loader\Facades',
'TwigBridge\Extension\Loader\Filters',
'TwigBridge\Extension\Loader\Functions',
'TwigBridge\Extension\Laravel\Auth',
'TwigBridge\Extension\Laravel\Config',
'TwigBridge\Extension\Laravel\Form',
'TwigBridge\Extension\Laravel\Html',
'TwigBridge\Extension\Laravel\Input',
'TwigBridge\Extension\Laravel\Session',
'TwigBridge\Extension\Laravel\String',
'TwigBridge\Extension\Laravel\Translator',
'TwigBridge\Extension\Laravel\Url',
// 'TwigBridge\Extension\Laravel\Legacy\Facades',
],
/*
|--------------------------------------------------------------------------
| Facades
|--------------------------------------------------------------------------
|
| Available facades. Access like `{{ Config.get('foo.bar') }}`.
|
| Each facade can take an optional array of options. To mark the whole facade
| as safe you can set the option `'is_safe' => true`. Setting the facade as
| safe means that any HTML returned will not be escaped.
|
| It is advisable to not set the whole facade as safe and instead mark the
| each appropriate method as safe for security reasons. You can do that with
| the following syntax:
|
| <code>
| 'Form' => [
| 'is_safe' => [
| 'open'
| ]
| ]
| </code>
|
| The values of the `is_safe` array must match the called method on the facade
| in order to be marked as safe.
|
*/
'facades' => [],
/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| Available functions. Access like `{{ secure_url(...) }}`.
|
| Each function can take an optional array of options. These options are
| passed directly to `Twig_SimpleFunction`.
|
| So for example, to mark a function as safe you can do the following:
|
| <code>
| 'link_to' => [
| 'is_safe' => ['html']
| ]
| </code>
|
| The options array also takes a `callback` that allows you to name the
| function differently in your Twig templates than what it's actually called.
|
| <code>
| 'link' => [
| 'callback' => 'link_to'
| ]
| </code>
|
*/
'functions' => [],
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Available filters. Access like `{{ variable|filter }}`.
|
| Each filter can take an optional array of options. These options are
| passed directly to `Twig_SimpleFilter`.
|
| So for example, to mark a filter as safe you can do the following:
|
| <code>
| 'studly_case' => [
| 'is_safe' => ['html']
| ]
| </code>
|
| The options array also takes a `callback` that allows you to name the
| filter differently in your Twig templates than what is actually called.
|
| <code>
| 'snake' => [
| 'callback' => 'snake_case'
| ]
| </code>
|
*/
'filters' => [],
];

View File

@@ -1,88 +0,0 @@
<?php
/**
* This file is part of the TwigBridge package.
*
* @copyright Robert Crowe <hello@vivalacrowe.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Support\Facades\Config;
/**
* Configuration options for Twig.
*/
return [
/*
|--------------------------------------------------------------------------
| Extension
|--------------------------------------------------------------------------
|
| File extension for Twig view files.
|
*/
'extension' => 'twig',
/*
|--------------------------------------------------------------------------
| Accepts all Twig environment configuration options
|--------------------------------------------------------------------------
|
| http://twig.sensiolabs.org/doc/api.html#environment-options
|
*/
'environment' => [
// When set to true, the generated templates have a __toString() method
// that you can use to display the generated nodes.
// default: false
'debug' => Config::get('app.debug', false),
// The charset used by the templates.
// default: utf-8
'charset' => 'utf-8',
// The base template class to use for generated templates.
// default: TwigBridge\Twig\Template
'base_template_class' => 'TwigBridge\Twig\Template',
// An absolute path where to store the compiled templates, or false to disable caching. If null
// then the cache file path is used.
// default: cache file storage path
'cache' => null,
// When developing with Twig, it's useful to recompile the template
// whenever the source code changes. If you don't provide a value
// for the auto_reload option, it will be determined automatically based on the debug value.
'auto_reload' => true,
// If set to false, Twig will silently ignore invalid variables
// (variables and or attributes/methods that do not exist) and
// replace them with a null value. When set to true, Twig throws an exception instead.
// default: false
'strict_variables' => false,
// If set to true, auto-escaping will be enabled by default for all templates.
// default: true
'autoescape' => true,
// A flag that indicates which optimizations to apply
// (default to -1 -- all optimizations are enabled; set it to 0 to disable)
'optimizations' => -1,
],
/*
|--------------------------------------------------------------------------
| Global variables
|--------------------------------------------------------------------------
|
| These will always be passed in and can be accessed as Twig variables.
| NOTE: these will be overwritten if you pass data into the view with the same key.
|
*/
'globals' => [],
];

0
app/config/queue.php Executable file → Normal file
View File

View File

@@ -1,2 +1,2 @@
<?php
return ['log_level' => 'debug',];
return ['log_level' => 'debug',];

View File

@@ -1,20 +1,3 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Cache Driver
|--------------------------------------------------------------------------
|
| This option controls the default cache "driver" that will be used when
| using the Caching library. Of course, you may use other drivers any
| time you wish. This is the default when another is not specified.
|
| Supported: "file", "database", "apc", "memcached", "redis", "array"
|
*/
'driver' => 'array',
];
return ['driver' => 'array',];

View File

@@ -4,9 +4,9 @@ return [
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => 'tests/_data/testing.sqlite',
'database' => realpath(__DIR__.'/../../../tests/_data/db.sqlite'),
'prefix' => ''
]
]
];
];

View File

@@ -1,21 +1,3 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "native", "cookie", "database", "apc",
| "memcached", "redis", "array"
|
*/
'driver' => 'array',
];
return ['driver' => 'array',];

View File

@@ -0,0 +1,2 @@
<?php
return ['log_level' => 'debug',];

View File

@@ -0,0 +1,8 @@
<?php
return [
'verify_mail' => false,
'verify_reset' => true,
'allow_register' => true
];

View File

@@ -0,0 +1,3 @@
<?php
return ['driver' => 'array',];

View File

@@ -0,0 +1,12 @@
<?php
return [
'default' => 'sqlite',
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => ''
]
]
];

View File

@@ -0,0 +1,13 @@
<?php
return [
'driver' => 'smtp',
'host' => '',
'port' => 587,
'from' => ['address' => '', 'name' => 'Firefly III'],
'encryption' => 'tls',
'username' => '',
'password' => '',
'sendmail' => '/usr/sbin/sendmail -bs',
'pretend' => true,
];

View File

@@ -0,0 +1,3 @@
<?php
return ['driver' => 'array',];

View File

@@ -1,6 +1,6 @@
<?php
use FireflyIII\Database\Account as AccountRepository;
use FireflyIII\Database\Account\Account as AccountRepository;
use FireflyIII\Exception\FireflyException;
/**
@@ -28,6 +28,7 @@ class AccountController extends BaseController
'Expense account' => 'expense',
'Beneficiary account' => 'expense',
'Revenue account' => 'revenue',
'Cash account' => 'cash',
];
/** @var array */
@@ -36,6 +37,7 @@ class AccountController extends BaseController
'asset' => 'fa-money',
'Asset account' => 'fa-money',
'Default account' => 'fa-money',
'Cash account' => 'fa-money',
'expense' => 'fa-shopping-cart',
'Expense account' => 'fa-shopping-cart',
'Beneficiary account' => 'fa-shopping-cart',
@@ -68,7 +70,7 @@ class AccountController extends BaseController
public function create($what)
{
$subTitleIcon = $this->_subIconsByIdentifier[$what];
$subTitle = 'Create a new ' . $what . ' account';
$subTitle = 'Create a new ' . e($what) . ' account';
return View::make('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
}
@@ -80,7 +82,7 @@ class AccountController extends BaseController
*/
public function delete(Account $account)
{
$subTitle = 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"';
$subTitle = 'Delete ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
return View::make('accounts.delete', compact('account', 'subTitle'));
}
@@ -99,9 +101,9 @@ class AccountController extends BaseController
$this->_repository->destroy($account);
Session::flash('success', 'The ' . $typeName . ' account "' . e($name) . '" was deleted.');
Session::flash('success', 'The ' . e($typeName) . ' account "' . e($name) . '" was deleted.');
return Redirect::route('accounts.index', $type);
return Redirect::route('accounts.index', $typeName);
}
/**
@@ -114,7 +116,7 @@ class AccountController extends BaseController
$openingBalance = $this->_repository->openingBalanceTransaction($account);
$subTitleIcon = $this->_subIconsByIdentifier[$account->accountType->type];
$subTitle = 'Edit ' . strtolower($account->accountType->type) . ' "' . $account->name . '"';
$subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
// pre fill some useful values.
$preFilled = [
@@ -155,7 +157,7 @@ class AccountController extends BaseController
$subTitleIcon = $this->_subIconsByIdentifier[$account->accountType->type];
$what = $this->_shortNamesByFullName[$account->accountType->type];
$journals = $this->_repository->getTransactionJournals($account, 50, $range);
$subTitle = 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"';
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
return View::make('accounts.show', compact('account', 'what', 'range', 'subTitleIcon', 'journals', 'subTitle'));
}
@@ -182,21 +184,17 @@ class AccountController extends BaseController
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('accounts.create', $data['what'])->withInput();
return Redirect::route('accounts.create', e($data['what']))->withInput();
}
// store:
// store
$this->_repository->store($data);
Session::flash('success', 'Account "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('accounts.index', $data['what']);
}
// create another.
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('accounts.create', $data['what'])->withInput();
return Redirect::route('accounts.index', e($data['what']));
}
return Redirect::route('accounts.index', $data['what']);
return Redirect::route('accounts.create', e($data['what']))->withInput();
}
/**
@@ -210,6 +208,7 @@ class AccountController extends BaseController
$data = Input::except('_token');
$data['what'] = $this->_shortNamesByFullName[$account->accountType->type];
// always validate:
$messages = $this->_repository->validate($data);
@@ -232,13 +231,10 @@ class AccountController extends BaseController
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('accounts.index', $data['what']);
}
// go back to update screen.
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('accounts.edit', $account->id);
return Redirect::route('accounts.index', e($data['what']));
}
return Redirect::route('accounts.index', $data['what']);
// go back to update screen.
return Redirect::route('accounts.edit', $account->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}
}

View File

@@ -0,0 +1,212 @@
<?php
use FireflyIII\Database\Bill\Bill as Repository;
use FireflyIII\Exception\FireflyException;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("NPathComplexity")
* Class BillController
*
*/
class BillController extends BaseController
{
/** @var Repository */
protected $_repository;
/**
* @param Repository $repository
*/
public function __construct(Repository $repository)
{
$this->_repository = $repository;
View::share('title', 'Bills');
View::share('mainTitleIcon', 'fa-calendar-o');
}
/**
* @return $this
*/
public function create()
{
$periods = \Config::get('firefly.periods_to_text');
return View::make('bills.create')->with('periods', $periods)->with('subTitle', 'Create new');
}
/**
* @param Bill $bill
*
* @return $this
*/
public function delete(Bill $bill)
{
return View::make('bills.delete')->with('bill', $bill)->with(
'subTitle', 'Delete "' . e($bill->name) . '"'
);
}
/**
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Bill $bill)
{
$this->_repository->destroy($bill);
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::make('bills.edit')->with('periods', $periods)->with('bill', $bill)->with(
'subTitle', 'Edit "' . e($bill->name) . '"'
);
}
/**
* @return $this
*/
public function index()
{
$bills = $this->_repository->get();
$bills->each(
function (Bill $bill) {
$bill->nextExpectedMatch = $this->_repository->nextExpectedMatch($bill);
$bill->lastFoundMatch = $this->_repository->lastFoundMatch($bill);
}
);
return View::make('bills.index', compact('bills'));
}
/**
* @param Bill $bill
*
* @return mixed
*/
public function rescan(Bill $bill)
{
if (intval($bill->active) == 0) {
Session::flash('warning', 'Inactive bills cannot be scanned.');
return Redirect::intended('/');
}
$this->_repository->scanEverything($bill);
Session::flash('success', 'Rescanned everything.');
return Redirect::intended('/');
}
/**
* @param Bill $bill
*
* @return mixed
*/
public function show(Bill $bill)
{
$journals = $bill->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get();
$bill->nextExpectedMatch = $this->_repository->nextExpectedMatch($bill);
$hideBill = true;
return View::make('bills.show', compact('journals', 'hideBill', 'bill'))->with(
'subTitle', e($bill->name)
);
}
/**
* @return $this
* @throws FireflyException
*/
public function store()
{
$data = Input::all();
$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 store bill: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('bills.create')->withInput();
}
// store
$this->_repository->store($data);
Session::flash('success', 'Bill "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('bills.index');
}
return Redirect::route('bills.create')->withInput();
}
/**
* @param Bill $bill
*
* @return $this
* @throws FireflyException
*/
public function update(Bill $bill)
{
$data = Input::except('_token');
$data['active'] = isset($data['active']) ? 1 : 0;
$data['automatch'] = isset($data['automatch']) ? 1 : 0;
$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 to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
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

@@ -1,24 +1,34 @@
<?php
use FireflyIII\Database\Budget as BudgetRepository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
use Carbon\Carbon;
use FireflyIII\Database\Budget\Budget as BudgetRepository;
use FireflyIII\Shared\Preferences\PreferencesInterface as Pref;
/**
* Class BudgetController
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
*/
class BudgetController extends BaseController
{
/** @var Pref */
protected $_preferences;
/** @var BudgetRepository */
protected $_repository;
/**
* @param BudgetRepository $repository
* @param Pref $preferences
*/
public function __construct(BudgetRepository $repository)
public function __construct(BudgetRepository $repository, Pref $preferences)
{
$this->_repository = $repository;
$this->_repository = $repository;
$this->_preferences = $preferences;
View::share('title', 'Budgets');
View::share('mainTitleIcon', 'fa-tasks');
}
@@ -31,17 +41,11 @@ class BudgetController extends BaseController
*/
public function amount(Budget $budget)
{
$amount = intval(Input::get('amount'));
$date = Session::get('start');
$limit = $this->_repository->updateLimitAmount($budget, $date, $amount);
$amount = intval(Input::get('amount'));
$date = Session::get('start', Carbon::now()->startOfMonth());
$limitRepetition = $this->_repository->updateLimitAmount($budget, $date, $amount);
// try to find the limit repetition for this limit:
$repetition = $limit->limitrepetitions()->first();
if ($repetition) {
return Response::json(['name' => $budget->name, 'repetition' => $repetition->id]);
} else {
return Response::json(['name' => $budget->name, 'repetition' => null]);
}
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition->id]);
}
@@ -72,8 +76,9 @@ class BudgetController extends BaseController
*/
public function destroy(Budget $budget)
{
Session::flash('success', 'Budget "' . e($budget->name) . '" was deleted.');
$this->_repository->destroy($budget);
Session::flash('success', 'The budget was deleted.');
return Redirect::route('budgets.index');
@@ -86,67 +91,50 @@ class BudgetController extends BaseController
*/
public function edit(Budget $budget)
{
$subTitle = 'Edit budget "' . $budget->name . '"';
$subTitle = 'Edit budget "' . e($budget->name) . '"';
return View::make('budgets.edit', compact('budget', 'subTitle'));
}
/**
* The index of the budget controller contains all budgets and the current relevant limit repetition.
*
* @return $this
*/
public function index()
{
$budgets = $this->_repository->get();
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$budgets = $repos->get();
// get the limits for the current month.
$date = \Session::get('start');
$spent = 0;
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$budget->spent = $repos->spentInMonth($budget, $date);
$spent += $budget->spent;
$budget->pct = 0;
$budget->limit = 0;
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $repetition */
foreach ($limit->limitrepetitions as $repetition) {
if ($repetition->startdate == $date) {
$budget->currentRep = $repetition;
$budget->limit = floatval($repetition->amount);
if ($budget->limit > $budget->spent) {
// not overspent:
$budget->pct = 30;
} else {
$budget->pct = 50;
}
}
}
// loop the budgets:
$budgets->each(
function (Budget $budget) {
$budget->spent = $this->_repository->spentInMonth($budget, \Session::get('start', Carbon::now()->startOfMonth()));
$budget->currentRep = $this->_repository->getRepetitionByDate($budget, \Session::get('start', Carbon::now()->startOfMonth()));
}
}
);
$budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000);
$amount = floatval($budgetAmount->data);
$overspent = $spent > $amount;
if ($overspent) {
// overspent on total amount
$spentPCT = ceil($amount / $spent * 100);
} else {
// not overspent on total amount.
$spentPCT = ceil($spent / $amount * 100);
}
$spent = $budgets->sum('spent');
$amount = $this->_preferences->get('budgetIncomeTotal' . \Session::get('start', Carbon::now()->startOfMonth())->format('FY'), 1000)->data;
$overspent = $spent > $amount;
$spentPCT = $overspent ? ceil($amount / $spent * 100) : ceil($spent / $amount * 100);
$budgetMax = $this->_preferences->get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data;
return View::make('budgets.index', compact('budgets', 'spent', 'spentPCT', 'overspent'))->with('budgetAmount', $budgetAmount);
return View::make('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 = $this->_repository->journalsNoBudget($start, $end);
$subTitle = 'Transactions without a budget in ' . $start->format('F Y');
return View::make('budgets.noBudget', compact('list', 'subTitle'));
}
/**
@@ -154,12 +142,7 @@ class BudgetController extends BaseController
*/
public function postUpdateIncome()
{
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
$date = Session::get('start');
$value = intval(Input::get('amount'));
$preferences->set('budgetIncomeTotal' . $date->format('FY'), $value);
$this->_preferences->set('budgetIncomeTotal' . Session::get('start', Carbon::now()->startOfMonth())->format('FY'), intval(Input::get('amount')));
return Redirect::route('budgets.index');
}
@@ -172,124 +155,90 @@ class BudgetController extends BaseController
*/
public function show(Budget $budget, LimitRepetition $repetition = null)
{
if (!is_null($repetition) && $repetition->limit->budget->id != $budget->id) {
App::abort(500);
if (!is_null($repetition) && $repetition->budgetLimit->budget->id != $budget->id) {
return View::make('error')->with('message', 'Invalid selection.');
}
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
if (is_null($repetition)) {
// get all other repetitions:
$limits = $budget->limits()->orderBy('startdate', 'DESC')->get();
// get all transaction journals for this budget.
$journals = $repos->getTransactionJournals($budget, 50);
$subTitle = $budget->name;
} else {
// get nothing? i dunno
$limits = [$repetition->limit];
// get all transaction journals for this budget and limit repetition.
$subTitle = $budget->name . ' in ' . $repetition->startdate->format('F Y');
$journals = $repos->getTransactionJournalsInRepetition($budget, $repetition, 50);
}
$hideBudget = true;
$hideBudget = true; // used in transaction list.
$journals = $this->_repository->getJournals($budget, $repetition);
$limits = $repetition ? [$repetition->budgetLimit] : $budget->budgetLimits()->orderBy('startdate', 'DESC')->get();
$subTitle = $repetition ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name);
return View::make('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget'));
}
/**
* @return $this
* @throws FireflyException
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store()
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save budget: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('budgets.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New budget stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('budgets.create');
} else {
return Redirect::route('budgets.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.create')->withInput();
break;
// 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 validate budget: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('budgets.create')->withInput();
}
// store
$this->_repository->store($data);
Session::flash('success', 'Budget "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('budgets.index');
}
// create another.
return Redirect::route('budgets.create')->withInput();
}
/**
* @param Budget $budget
*
* @return $this
* @throws FireflyException
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(Budget $budget)
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'return_to_edit':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save budget: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('budgets.edit', $budget->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($budget, $data);
Session::flash('success', 'Budget updated!');
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('budgets.edit', $budget->id);
} else {
return Redirect::route('budgets.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.edit', $budget->id)->withInput();
break;
// 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 budget: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('budgets.edit', $budget->id)->withInput();
}
// update
$this->_repository->update($budget, $data);
Session::flash('success', 'Budget "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('budgets.index');
}
return Redirect::route('budgets.edit', $budget->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
/**
@@ -297,11 +246,8 @@ class BudgetController extends BaseController
*/
public function updateIncome()
{
$date = Session::get('start');
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
$budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000);
$budgetAmount = $this->_preferences->get('budgetIncomeTotal' . Session::get('start', Carbon::now()->startOfMonth())->format('FY'), 1000);
return View::make('budgets.income')->with('amount', $budgetAmount)->with('date', $date);
return View::make('budgets.income')->with('amount', $budgetAmount);
}
}
}

View File

@@ -1,19 +1,30 @@
<?php
use Carbon\Carbon;
use FireflyIII\Database\Category\Category as CategoryRepository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class CategoryController
*/
class CategoryController extends BaseController
{
/** @var CategoryRepository */
protected $_repository;
/**
*
* @param CategoryRepository $repository
*/
public function __construct()
public function __construct(CategoryRepository $repository)
{
View::share('title', 'Categories');
View::share('mainTitleIcon', 'fa-bar-chart');
$this->_repository = $repository;
}
/**
@@ -24,6 +35,19 @@ class CategoryController extends BaseController
return View::make('categories.create')->with('subTitle', 'Create a new category');
}
/**
* @return \Illuminate\View\View
*/
public function noCategory()
{
$start = \Session::get('start', Carbon::now()->startOfMonth());
$end = \Session::get('end', Carbon::now()->startOfMonth());
$list = $this->_repository->journalsNoCategory($start, $end);
$subTitle = 'Transactions without a category in ' . $start->format('F Y');
return View::make('categories.noCategory', compact('list', 'subTitle'));
}
/**
* @param Category $category
*
@@ -31,7 +55,7 @@ class CategoryController extends BaseController
*/
public function delete(Category $category)
{
return View::make('categories.delete')->with('category', $category)->with('subTitle', 'Delete category "' . $category->name . '"');
return View::make('categories.delete')->with('category', $category)->with('subTitle', 'Delete category "' . e($category->name) . '"');
}
/**
@@ -41,11 +65,9 @@ class CategoryController extends BaseController
*/
public function destroy(Category $category)
{
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
Session::flash('success', 'Category "' . e($category->name) . '" was deleted.');
$this->_repository->destroy($category);
$repos->destroy($category);
Session::flash('success', 'The category was deleted.');
return Redirect::route('categories.index');
}
@@ -57,7 +79,7 @@ class CategoryController extends BaseController
*/
public function edit(Category $category)
{
return View::make('categories.edit')->with('category', $category)->with('subTitle', 'Edit category "' . $category->name . '"');
return View::make('categories.edit')->with('category', $category)->with('subTitle', 'Edit category "' . e($category->name) . '"');
}
/**
@@ -65,9 +87,7 @@ class CategoryController extends BaseController
*/
public function index()
{
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$categories = $repos->get();
$categories = $this->_repository->get();
return View::make('categories.index', compact('categories'));
}
@@ -79,12 +99,8 @@ class CategoryController extends BaseController
*/
public function show(Category $category)
{
$hideCategory = true;
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$journals = $repos->getTransactionJournals($category, 50);
$hideCategory = true; // used in list.
$journals = $this->_repository->getTransactionJournals($category, 50);
return View::make('categories.show', compact('category', 'journals', 'hideCategory'));
}
@@ -95,44 +111,33 @@ class CategoryController extends BaseController
*/
public function store()
{
$data = Input::all();
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save category: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('categories.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New category stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('categories.create')->withInput();
} else {
return Redirect::route('categories.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('categories.create')->withInput();
break;
// 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 store category: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('categories.create')->withInput();
}
// store
$this->_repository->store($data);
Session::flash('success', 'Category "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('categories.index');
}
return Redirect::route('categories.create')->withInput();
}
/**
@@ -143,46 +148,39 @@ class CategoryController extends BaseController
*/
public function update(Category $category)
{
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$data = Input::except('_token');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'return_to_edit':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save category: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('categories.edit', $category->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($category, $data);
Session::flash('success', 'Category updated!');
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('categories.edit', $category->id);
} else {
return Redirect::route('categories.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('categories.edit', $category->id)->withInput();
break;
// 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 category: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('categories.edit', $category->id)->withInput();
}
// update
$this->_repository->update($category, $data);
Session::flash('success', 'Category "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('categories.index');
}
// go back to update screen.
return Redirect::route('categories.edit', $category->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}
}

View File

@@ -0,0 +1,197 @@
<?php
use FireflyIII\Database\TransactionCurrency\TransactionCurrency as Repository;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class CurrencyController
*/
class CurrencyController extends BaseController
{
/** @var Repository */
protected $_repository;
/**
* @param Repository $repository
*/
public function __construct(Repository $repository)
{
$this->_repository = $repository;
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::make('currency.create', compact('subTitleIcon', 'subTitle'));
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse
*/
public function defaultCurrency(TransactionCurrency $currency)
{
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$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::make('currency.delete', compact('currency'));
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(TransactionCurrency $currency)
{
Session::flash('success', 'Currency "' . e($currency->name) . '" deleted');
$this->_repository->destroy($currency);
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::make('currency.edit', compact('currency', 'subTitle', 'subTitleIcon'));
}
/**
* @return \Illuminate\View\View
*/
public function index()
{
$currencies = $this->_repository->get();
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$currencyPreference = $preferences->get('currencyPreference', 'EUR');
$defaultCurrency = $this->_repository->findByCode($currencyPreference->data);
return View::make('currency.index', compact('currencies', 'defaultCurrency'));
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store()
{
$data = Input::except('_token');
// 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 store currency: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('currency.create')->withInput();
}
// store
$this->_repository->store($data);
Session::flash('success', 'Currency "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('currency.index');
}
return Redirect::route('currency.create')->withInput();
}
/**
* @param TransactionCurrency $currency
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(TransactionCurrency $currency)
{
$data = Input::except('_token');
// 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 currency: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('currency.edit', $currency->id)->withInput();
}
// update
$this->_repository->update($currency, $data);
Session::flash('success', 'Currency "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('currency.index');
}
return Redirect::route('currency.edit', $currency->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}

View File

@@ -1,12 +1,41 @@
<?php
use Carbon\Carbon;
use FireflyIII\Chart\ChartInterface;
use Grumpydictator\Gchart\GChart as GChart;
/**
* Class GoogleChartController
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("MethodLength") // There is one with 45 lines and im gonna move it.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*/
class GoogleChartController extends BaseController
{
/** @var GChart */
protected $_chart;
/** @var Carbon */
protected $_end;
/** @var ChartInterface */
protected $_repository;
/** @var Carbon */
protected $_start;
/**
* @param GChart $chart
* @param ChartInterface $repository
*/
public function __construct(GChart $chart, ChartInterface $repository)
{
$this->_chart = $chart;
$this->_repository = $repository;
$this->_start = Session::get('start', Carbon::now()->startOfMonth());
$this->_end = Session::get('end', Carbon::now()->endOfMonth());
}
/**
* @param Account $account
* @param string $view
@@ -15,199 +44,35 @@ class GoogleChartController extends BaseController
*/
public function accountBalanceChart(Account $account, $view = 'session')
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$this->_chart->addColumn('Day of month', 'date');
$this->_chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
$start = $this->_start;
$end = $this->_end;
$count = $account->transactions()->count();
/*
* Loop the date, then loop the accounts, then add balance.
*/
switch ($view) {
default:
case 'session':
$start = Session::get('start');
$end = Session::get('end');
break;
case 'all':
$first = $account->transactionjournals()->orderBy('date', 'DESC')->first();
$last = $account->transactionjournals()->orderBy('date', 'ASC')->first();
if (is_null($first)) {
$start = Session::get('start');
} else {
$start = clone $first->date;
}
if (is_null($last)) {
$end = Session::get('end');
} else {
$end = clone $last->date;
}
break;
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) {
$row = [clone $current];
if ($current > Carbon::now()) {
$row[] = null;
} else {
$row[] = Steam::balance($account, $current);
}
$chart->addRowArray($row);
$this->_chart->addRow(clone $current, Steam::balance($account, $current));
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
* @param string $view
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountSankeyInChart(Account $account, $view = 'session')
{
// collect all relevant entries.
$set = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('From', 'string');
$chart->addColumn('To', 'string', 'domain');
$chart->addColumn('Weight', 'number');
switch ($view) {
default:
case 'session':
$start = Session::get('start');
$end = Session::get('end');
break;
case 'all':
$first = $account->transactionjournals()->orderBy('date', 'DESC')->first();
$last = $account->transactionjournals()->orderBy('date', 'ASC')->first();
if (is_null($first)) {
$start = Session::get('start');
} else {
$start = clone $first->date;
}
if (is_null($last)) {
$end = Session::get('end');
} else {
$end = clone $last->date;
}
break;
}
$transactions = $account->transactions()->with(
['transactionjournal', 'transactionjournal.transactions' => function ($q) {
$q->where('amount', '<', 0);
}, 'transactionjournal.budgets', 'transactionjournal.transactiontype', 'transactionjournal.categories']
)->before($end)->after($start)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$amount = floatval($transaction->amount);
$type = $transaction->transactionJournal->transactionType->type;
if ($amount > 0 && $type != 'Transfer') {
$otherAccount = $transaction->transactionJournal->transactions[0]->account->name;
$categoryName = isset($transaction->transactionJournal->categories[0]) ? $transaction->transactionJournal->categories[0]->name : '(no cat)';
$set[] = [$otherAccount, $categoryName, $amount];
$set[] = [$categoryName, $account->name, $amount];
}
}
// loop the set, group everything together:
$grouped = [];
foreach ($set as $entry) {
$key = $entry[0] . $entry[1];
if (isset($grouped[$key])) {
$grouped[$key][2] += $entry[2];
} else {
$grouped[$key] = $entry;
}
}
// add rows to the chart:
foreach ($grouped as $entry) {
$chart->addRow($entry[0], $entry[1], $entry[2]);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
* @param string $view
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountSankeyOutChart(Account $account, $view = 'session')
{
// collect all relevant entries.
$set = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('From', 'string');
$chart->addColumn('To', 'string', 'domain');
$chart->addColumn('Weight', 'number');
$transactions = $account->transactions()->with(
['transactionjournal', 'transactionjournal.transactions', 'transactionjournal.budgets', 'transactionjournal.transactiontype',
'transactionjournal.categories']
)->before(Session::get('end'))->after(
Session::get('start')
)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$amount = floatval($transaction->amount);
$type = $transaction->transactionJournal->transactionType->type;
if ($amount < 0 && $type != 'Transfer') {
// from account to a budget (if present).
$budgetName = isset($transaction->transactionJournal->budgets[0]) ? $transaction->transactionJournal->budgets[0]->name : '(no budget)';
$set[] = [$account->name, $budgetName, $amount * -1];
// from budget to category.
$categoryName = isset($transaction->transactionJournal->categories[0]) ? ' ' . $transaction->transactionJournal->categories[0]->name
: '(no cat)';
$set[] = [$budgetName, $categoryName, $amount * -1];
}
}
// loop the set, group everything together:
$grouped = [];
foreach ($set as $entry) {
$key = $entry[0] . $entry[1];
if (isset($grouped[$key])) {
$grouped[$key][2] += $entry[2];
} else {
$grouped[$key] = $entry;
}
}
// add rows to the chart:
foreach ($grouped as $entry) {
$chart->addRow($entry[0], $entry[1], $entry[2]);
}
$chart->generate();
return Response::json($chart->getData());
$this->_chart->generate();
return Response::json($this->_chart->getData());
}
/**
@@ -215,51 +80,35 @@ class GoogleChartController extends BaseController
*/
public function allAccountsBalanceChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day of the month', 'date');
$this->_chart->addColumn('Day of the month', 'date');
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$pref = $preferences->get('frontpageAccounts', []);
$pref = $preferences->get('frontPageAccounts', []);
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
if (count($pref->data) > 0) {
$accounts = $acct->getByIds($pref->data);
} else {
$accounts = $acct->getAssetAccounts();
}
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$accounts = count($pref->data) > 0 ? $acct->getByIds($pref->data) : $acct->getAssetAccounts();
/*
* Add a column for each account.
*/
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
$this->_chart->addColumn('Balance for ' . $account->name, 'number');
}
/*
* Loop the date, then loop the accounts, then add balance.
*/
$start = Session::get('start');
$end = Session::get('end');
$current = clone $start;
$current = clone $this->_start;
$current->subDay();
while ($end >= $current) {
while ($this->_end >= $current) {
$row = [clone $current];
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
}
$chart->addRowArray($row);
$this->_chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
@@ -268,68 +117,50 @@ class GoogleChartController extends BaseController
*/
public function allBudgetsHomeChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Budget', 'string');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$this->_chart->addColumn('Budget', 'string');
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
Log::debug('Now in allBudgetsHomeChart()');
/** @var \FireflyIII\Database\Budget\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget\Budget');
$budgets = $bdt->get();
/*
* Loop budgets:
*/
/** @var Budget $budget */
foreach ($budgets as $budget) {
/*
* Is there a repetition starting on this particular date? We can use that.
*/
Log::debug('Now working budget #' . $budget->id . ', ' . $budget->name);
/** @var \LimitRepetition $repetition */
$repetition = $bdt->repetitionOnStartingOnDate($budget, Session::get('start'));
/*
* If there is, use it. Otherwise, forget it.
*/
$repetition = $bdt->repetitionOnStartingOnDate($budget, $this->_start);
if (is_null($repetition)) {
\Log::debug('Budget #' . $budget->id . ' has no repetition on ' . $this->_start->format('Y-m-d'));
// use the session start and end for our search query
$searchStart = Session::get('start');
$searchEnd = Session::get('end');
// the limit is zero:
$limit = 0;
$searchStart = $this->_start;
$searchEnd = $this->_end;
$limit = 0; // the limit is zero:
} else {
\Log::debug('Budget #' . $budget->id . ' has a repetition on ' . $this->_start->format('Y-m-d') . '!');
// use the limit's start and end for our search query
$searchStart = $repetition->startdate;
$searchEnd = $repetition->enddate;
// the limit is the repetitions limit:
$limit = floatval($repetition->amount);
$limit = floatval($repetition->amount); // the limit is the repetitions limit:
}
/*
* No matter the result of the search for the repetition, get all the transactions associated
* with the budget, and sum up the expenses made.
*/
$expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
\Log::debug('Expenses in budget ' . $budget->name . ' before ' . $searchEnd->format('Y-m-d') . ' and after ' . $searchStart . ' are: ' . $expenses);
if ($expenses > 0) {
$chart->addRow($budget->name, $limit, $expenses);
$this->_chart->addRow($budget->name, $limit, $expenses);
}
}
/*
* Finally, get all transactions WITHOUT a budget and add those as well.
* (yes this method is oddly specific).
*/
$noBudgetSet = $bdt->transactionsWithoutBudgetInDateRange(Session::get('start'), Session::get('end'));
$noBudgetSet = $bdt->expenseNoBudget($this->_start, $this->_end);
$sum = $noBudgetSet->sum('amount') * -1;
$chart->addRow('No budget', 0, $sum);
$this->_chart->addRow('No budget', 0, $sum);
$this->_chart->generate();
$chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
@@ -337,48 +168,94 @@ class GoogleChartController extends BaseController
*/
public function allCategoriesHomeChart()
{
$data = [];
$this->_chart->addColumn('Category', 'string');
$this->_chart->addColumn('Spent', 'number');
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
// query!
$set = $this->_repository->getCategorySummary($this->_start, $this->_end);
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/*
* Get the journals:
*/
$journals = $tj->getInDateRange(Session::get('start'), Session::get('end'));
/** @var \TransactionJournal $journal */
foreach ($journals as $journal) {
if ($journal->transactionType->type == 'Withdrawal') {
$amount = $journal->getAmount();
$category = $journal->categories()->first();
if (!is_null($category)) {
if (isset($data[$category->name])) {
$data[$category->name] += $amount;
} else {
$data[$category->name] = $amount;
}
}
}
}
arsort($data);
foreach ($data as $key => $entry) {
$chart->addRow($key, $entry);
foreach ($set as $entry) {
$entry->name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$this->_chart->addRow($entry->name, floatval($entry->sum));
}
$this->_chart->generate();
$chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* @param Bill $bill
*
* @return \Illuminate\Http\JsonResponse
*/
public function billOverview(Bill $bill)
{
$this->_chart->addColumn('Date', 'date');
$this->_chart->addColumn('Max amount', 'number');
$this->_chart->addColumn('Min amount', 'number');
$this->_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) {
$amount = $result->getAmount();
} else {
$amount = 0;
}
unset($result);
$this->_chart->addRow(clone $start, $bill->amount_max, $bill->amount_min, $amount);
$start = DateKit::addPeriod($start, $bill->repeat_freq, 0);
}
$this->_chart->generate();
return Response::json($this->_chart->getData());
}
/**
*
* @return \Illuminate\Http\JsonResponse
* @throws \FireflyIII\Exception\FireflyException
*/
public function billsOverview()
{
$paid = ['items' => [], 'amount' => 0];
$unpaid = ['items' => [], 'amount' => 0];
$this->_chart->addColumn('Name', 'string');
$this->_chart->addColumn('Amount', 'number');
$set = $this->_repository->getBillsSummary($this->_start, $this->_end);
foreach ($set as $entry) {
if (intval($entry->journalId) == 0) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += floatval($entry->averageAmount);
} else {
$paid['items'][] = $entry->description;
$paid['amount'] += floatval($entry->actualAmount);
}
}
$this->_chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$this->_chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$this->_chart->generate();
return Response::json($this->_chart->getData());
}
/**
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
@@ -389,10 +266,8 @@ class GoogleChartController extends BaseController
$start = clone $repetition->startdate;
$end = $repetition->enddate;
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day', 'date');
$chart->addColumn('Left', 'number');
$this->_chart->addColumn('Day', 'date');
$this->_chart->addColumn('Left', 'number');
$amount = $repetition->amount;
@@ -403,273 +278,132 @@ class GoogleChartController extends BaseController
*/
$sum = floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($start)->sum('amount'));
$amount += $sum;
$chart->addRow(clone $start, $amount);
$this->_chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetsReportChart($year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
$budgets = $bdt->get();
$chart->addColumn('Month', 'date');
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$chart->addColumn('No budget', 'number');
/*
* Loop budgets this year.
*/
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$row[] = $bdt->spentInMonth($budget, $start);
}
/*
* Without a budget:
*/
$endOfMonth = clone $start;
$endOfMonth->endOfMonth();
$set = $bdt->transactionsWithoutBudgetInDateRange($start, $endOfMonth);
$row[] = floatval($set->sum('amount')) * -1;
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Component $component
* @param Budget $budget
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function componentsAndSpending(Component $component, $year)
public function budgetsAndSpending(Budget $budget, $year)
{
try {
$start = new Carbon('01-01-' . $year);
new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid year.');
}
if ($component->class == 'Budget') {
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
} else {
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
}
/** @var \FireflyIII\Database\Budget\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget\Budget');
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$this->_chart->addColumn('Month', 'date');
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
$end = clone $start;
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$spent = $budgetRepository->spentInMonth($budget, $start);
$repetition = $budgetRepository->repetitionOnStartingOnDate($budget, $start);
$spent = $repos->spentInMonth($component, $start);
$repetition = $repos->repetitionOnStartingOnDate($component, $start);
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);
$this->_chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* @param Piggybank $piggybank
*
* @param Category $component
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function piggyBankHistory(\Piggybank $piggybank)
public function categoriesAndSpending(Category $component, $year)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number');
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return View::make('error')->with('message', 'Invalid year.');
}
$set = \DB::table('piggybank_events')->where('piggybank_id', $piggybank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]);
/** @var \FireflyIII\Database\Category\Category $categoryRepository */
$categoryRepository = App::make('FireflyIII\Database\Category\Category');
$this->_chart->addColumn('Month', 'date');
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$spent = $categoryRepository->spentInMonth($component, $start);
$budgeted = null;
$this->_chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$this->_chart->generate();
return Response::json($this->_chart->getData());
}
/**
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\JsonResponse
*/
public function piggyBankHistory(\PiggyBank $piggyBank)
{
$this->_chart->addColumn('Date', 'date');
$this->_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));
$this->_chart->addRow(new Carbon($entry->date), floatval($entry->sum));
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* @param RecurringTransaction $recurring
*
* @return \Illuminate\Http\JsonResponse
*/
public function recurringOverview(RecurringTransaction $recurring)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$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 = $recurring->transactionjournals()->orderBy('date', 'ASC')->first();
if ($first) {
$start = $first->date;
} else {
$start = new Carbon;
}
$end = new Carbon;
while ($start <= $end) {
$result = $recurring->transactionjournals()->before($end)->after($start)->first();
if ($result) {
$amount = $result->getAmount();
} else {
$amount = 0;
}
unset($result);
$chart->addRow(clone $start, $recurring->amount_max, $recurring->amount_min, $amount);
$start = DateKit::addPeriod($start, $recurring->repeat_freq, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
* @throws \FireflyIII\Exception\FireflyException
*/
public function recurringTransactionsOverview()
{
/*
* Set of paid transaction journals.
* Set of unpaid recurring transactions.
*/
$paid = ['items' => [], 'amount' => 0];
$unpaid = ['items' => [], 'amount' => 0];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
/** @var \FireflyIII\Database\Recurring $rcr */
$rcr = App::make('FireflyIII\Database\Recurring');
$recurring = $rcr->get();
/** @var \RecurringTransaction $entry */
foreach ($recurring as $entry) {
/*
* Start another loop starting at the $date.
*/
$start = clone $entry->date;
$end = Carbon::now();
/*
* The jump we make depends on the $repeat_freq
*/
$current = clone $start;
while ($current <= $end) {
/*
* Get end of period for $current:
*/
$currentEnd = DateKit::endOfPeriod($current, $entry->repeat_freq);
/*
* In the current session range?
*/
if (\Session::get('end') >= $current and $currentEnd >= \Session::get('start')) {
/*
* Lets see if we've already spent money on this recurring transaction (it hath recurred).
*/
/** @var TransactionJournal $set */
$journal = $rcr->getJournalForRecurringInRange($entry, $current, $currentEnd);
if (is_null($journal)) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += (($entry->amount_max + $entry->amount_min) / 2);
} else {
$amount = $journal->getAmount();
$paid['items'][] = $journal->description;
$paid['amount'] += $amount;
}
}
/*
* Add some time for the next loop!
*/
$current = DateKit::addPeriod($current, $entry->repeat_freq, intval($entry->skip));
}
}
/** @var \RecurringTransaction $entry */
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param $year
*
* @return \Illuminate\Http\JsonResponse
@@ -679,37 +413,36 @@ class GoogleChartController extends BaseController
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid year.');
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$this->_chart->addColumn('Month', 'date');
$this->_chart->addColumn('Income', 'number');
$this->_chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
// total income:
$income = $tj->getSumOfIncomesByMonth($start);
$expense = $tj->getSumOfExpensesByMonth($start);
$income = $repository->getSumOfIncomesByMonth($start);
$expense = $repository->getSumOfExpensesByMonth($start);
$chart->addRow(clone $start, $income, $expense);
$this->_chart->addRow(clone $start, $income, $expense);
$start->addMonth();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
*
* @param $year
*
* @return \Illuminate\Http\JsonResponse
@@ -719,16 +452,14 @@ class GoogleChartController extends BaseController
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid year.');
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$this->_chart->addColumn('Summary', 'string');
$this->_chart->addColumn('Income', 'number');
$this->_chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$end = clone $start;
$end->endOfYear();
@@ -738,20 +469,20 @@ class GoogleChartController extends BaseController
while ($start < $end) {
// total income:
$income += $tj->getSumOfIncomesByMonth($start);
$expense += $tj->getSumOfExpensesByMonth($start);
$income += $repository->getSumOfIncomesByMonth($start);
$expense += $repository->getSumOfExpensesByMonth($start);
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$this->_chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$this->_chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
}
}

View File

@@ -1,6 +1,8 @@
<?php
/**
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class HelpController
*/
class HelpController extends BaseController
@@ -12,15 +14,13 @@ class HelpController extends BaseController
*/
public function show($route)
{
// no valid route
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
if (!Route::has($route)) {
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
\Log::error('No such route: ' . $route);
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
// content in cache
if (Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text')) {
$helpText = Cache::get('help.' . $route . '.text');
$helpTitle = Cache::get('help.' . $route . '.title');
@@ -28,27 +28,27 @@ class HelpController extends BaseController
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
// get the help-content from Github:
$URL = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
\Log::debug('URL is: ' . $uri);
try {
$content = file_get_contents($URL);
$helpText = file_get_contents($uri);
} catch (ErrorException $e) {
$content = '<p>There is no help for this route.</p>';
\Log::error(trim($e->getMessage()));
}
if (strlen($content) > 0) {
$helpText = \Michelf\Markdown::defaultTransform($content);
$helpTitle = $route;
Cache::put('help.' . $route . '.text', $helpText, 10080); // a week.
Cache::put('help.' . $route . '.title', $helpTitle, 10080);
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
\Log::debug('Found help for ' . $route);
\Log::debug('Help text length for route ' . $route . ' is ' . strlen($helpText));
\Log::debug('Help text IS: "' . $helpText . '".');
if (strlen(trim($helpText)) == 0) {
$helpText = '<p>There is no help for this route.</p>';
}
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
$helpText = \Michelf\Markdown::defaultTransform($helpText);
$helpTitle = $route;
Cache::put('help.' . $route . '.text', $helpText, 10080); // a week.
Cache::put('help.' . $route . '.title', $helpTitle, 10080);
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
use Carbon\Carbon;
/**
* Class HomeController
@@ -6,6 +7,7 @@
*/
class HomeController extends BaseController
{
/**
* @return \Illuminate\Http\RedirectResponse
*/
@@ -22,32 +24,32 @@ class HomeController extends BaseController
public function index()
{
// count, maybe Firefly needs some introducing text to show:
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
/** @var \FireflyIII\Database\TransactionJournal $jrnls */
$jrnls = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $journalRepository */
$journalRepository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
$count = $acct->countAssetAccounts();
$start = Session::get('start');
$end = Session::get('end');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
// get the preference for the home accounts to show:
$frontpage = $preferences->get('frontpageAccounts', []);
if ($frontpage->data == []) {
$frontPage = $preferences->get('frontPageAccounts', []);
if ($frontPage->data == []) {
$accounts = $acct->getAssetAccounts();
} else {
$accounts = $acct->getByIds($frontpage->data);
$accounts = $acct->getByIds($frontPage->data);
}
$transactions = [];
foreach ($accounts as $account) {
$set = $jrnls->getInDateRangeAccount($account, 10, $start, $end);
$set = $journalRepository->getInDateRangeAccount($account, $start, $end, 10);
if (count($set) > 0) {
$transactions[] = [$set, $account];
}
@@ -76,7 +78,7 @@ class HomeController extends BaseController
Session::forget('range');
}
return Redirect::back();
return Redirect::intended('/');
}
/**
@@ -86,7 +88,7 @@ class HomeController extends BaseController
{
Navigation::next();
return Redirect::back();
return Redirect::intended('/');
}
/**
@@ -96,6 +98,6 @@ class HomeController extends BaseController
{
Navigation::prev();
return Redirect::back();
return Redirect::intended('/');
}
}
}

View File

@@ -14,8 +14,8 @@ class JsonController extends BaseController
*/
public function categories()
{
/** @var \FireflyIII\Database\Category $categories */
$categories = App::make('FireflyIII\Database\Category');
/** @var \FireflyIII\Database\Category\Category $categories */
$categories = App::make('FireflyIII\Database\Category\Category');
$list = $categories->get();
$return = [];
foreach ($list as $entry) {
@@ -34,8 +34,8 @@ class JsonController extends BaseController
*/
public function expenseAccounts()
{
/** @var \FireflyIII\Database\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account\Account');
$list = $accounts->getExpenseAccounts();
$return = [];
foreach ($list as $entry) {
@@ -51,8 +51,8 @@ class JsonController extends BaseController
*/
public function revenueAccounts()
{
/** @var \FireflyIII\Database\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account\Account');
$list = $accounts->getRevenueAccounts();
$return = [];
foreach ($list as $entry) {
@@ -62,4 +62,4 @@ class JsonController extends BaseController
return Response::json($return);
}
}
}

View File

@@ -1,42 +1,55 @@
<?php
use Carbon\Carbon;
use FireflyIII\Database\PiggyBank\PiggyBank as Repository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
* Class PiggybankController
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
*
* Class PiggyBankController
*
*/
class PiggybankController extends BaseController
class PiggyBankController extends BaseController
{
/** @var Repository */
protected $_repository;
/**
*
* @param Repository $repository
*/
public function __construct()
public function __construct(Repository $repository)
{
$this->_repository = $repository;
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
/**
* Add money to piggy bank
*
* @param Piggybank $piggybank
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function add(Piggybank $piggybank)
public function add(PiggyBank $piggyBank)
{
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$leftToSave = $piggybank->targetamount - $savedSoFar;
$amount = min($leftOnAccount, $leftToSave);
$leftOnAccount = $this->_repository->leftOnAccount($piggyBank->account);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave);
return View::make('piggybanks.add', compact('piggybank'))->with('maxAmount', $amount);
\Log::debug('Now going to view for piggy bank #' . $piggyBank->id . ' (' . $piggyBank->name . ')');
return View::make('piggy_banks.add', compact('piggyBank', 'maxAmount'));
}
/**
@@ -45,76 +58,78 @@ class PiggybankController extends BaseController
public function create()
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggybank_periods');
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus';
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
return View::make('piggybanks.create', compact('accounts', 'periods'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc')->with(
'subTitle', 'Create new piggy bank'
)->with('subTitleIcon', 'fa-plus');
return View::make('piggy_banks.create', compact('accounts', 'periods', 'subTitle', 'subTitleIcon'));
}
/**
* @param Piggybank $piggybank
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function delete(Piggybank $piggybank)
public function delete(PiggyBank $piggyBank)
{
return View::make('piggybanks.delete')->with('piggybank', $piggybank)->with('subTitle', 'Delete "' . $piggybank->name . '"')->with(
'title', 'Piggy banks'
)->with('mainTitleIcon', 'fa-sort-amount-asc');
$subTitle = 'Delete "' . e($piggyBank->name) . '"';
return View::make('piggy_banks.delete', compact('piggyBank', 'subTitle'));
}
/**
* @param Piggybank $piggyBank
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Piggybank $piggyBank)
public function destroy(PiggyBank $piggyBank)
{
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
$repos->destroy($piggyBank);
Session::flash('success', 'Piggy bank deleted.');
return Redirect::route('piggybanks.index');
Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$this->_repository->destroy($piggyBank);
return Redirect::route('piggy_banks.index');
}
/**
* @param Piggybank $piggybank
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function edit(Piggybank $piggybank)
public function edit(PiggyBank $piggyBank)
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggybank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$subTitle = 'Edit piggy bank "' . e($piggyBank->name) . '"';
$subTitleIcon = 'fa-pencil';
/*
* Flash some data to fill the form.
*/
$preFilled = ['name' => $piggybank->name,
'account_id' => $piggybank->account_id,
'targetamount' => $piggybank->targetamount,
'targetdate' => !is_null($piggybank->targetdate) ? $piggybank->targetdate->format('Y-m-d') : null,
'reminder' => $piggybank->reminder,
'remind_me' => intval($piggybank->remind_me) == 1 || !is_null($piggybank->reminder) ? true : false
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::make('piggybanks.edit', compact('piggybank', 'accounts', 'periods', 'preFilled'))->with('title', 'Piggybanks')->with(
'mainTitleIcon', 'fa-sort-amount-asc'
)->with('subTitle', 'Edit piggy bank "' . e($piggybank->name) . '"')->with('subTitleIcon', 'fa-pencil');
return View::make('piggy_banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'accounts', 'periods', 'preFilled'));
}
/**
@@ -122,135 +137,134 @@ class PiggybankController extends BaseController
*/
public function index()
{
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
/** @var Collection $piggybanks */
$piggybanks = $repos->get();
/** @var Collection $piggyBanks */
$piggyBanks = $this->_repository->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;
/** @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;
$account = $piggyBank->account;
if (!isset($accounts[$account->id])) {
$accounts[$account->id] = ['name' => $account->name, 'balance' => Steam::balance($account),
'leftForPiggybanks' => $repos->leftOnAccount($account), 'sumOfSaved' => $piggybank->savedSoFar,
'sumOfTargets' => floatval($piggybank->targetamount), 'leftToSave' => $piggybank->leftToSave];
$accounts[$account->id] = [
'name' => $account->name,
'balance' => Steam::balance($account),
'leftForPiggyBanks' => $this->_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;
$accounts[$account->id]['sumOfSaved'] += $piggyBank->savedSoFar;
$accounts[$account->id]['sumOfTargets'] += floatval($piggyBank->targetamount);
$accounts[$account->id]['leftToSave'] += $piggyBank->leftToSave;
}
}
return View::make('piggybanks.index', compact('piggybanks', 'accounts'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggy_banks.index', compact('piggyBanks', 'accounts'));
}
/**
* POST add money to piggy bank
*
* @param Piggybank $piggybank
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postAdd(Piggybank $piggybank)
public function postAdd(PiggyBank $piggyBank)
{
$amount = round(floatval(Input::get('amount')), 2);
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
/** @var \FireflyIII\Database\PiggyBank\PiggyBank $acct */
$piggyRepository = App::make('FireflyIII\Database\PiggyBank\PiggyBank');
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$leftToSave = $piggybank->targetamount - $savedSoFar;
$leftOnAccount = $piggyRepository->leftOnAccount($piggyBank->account);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);
if ($amount <= $maxAmount) {
$repetition = $piggybank->currentRelevantRep();
$repetition = $piggyBank->currentRelevantRep();
$repetition->currentamount += $amount;
$repetition->save();
/*
* Create event!
*/
Event::fire('piggybank.addMoney', [$piggybank, $amount]); // new and used.
Event::fire('piggy_bank.addMoney', [$piggyBank, $amount]); // new and used.
Session::flash('success', 'Added ' . mf($amount, false) . ' to "' . e($piggybank->name) . '".');
Session::flash('success', 'Added ' . Amount::format($amount, false) . ' to "' . e($piggyBank->name) . '".');
} else {
Session::flash('error', 'Could not add ' . mf($amount, false) . ' to "' . e($piggybank->name) . '".');
Session::flash('error', 'Could not add ' . Amount::format($amount, false) . ' to "' . e($piggyBank->name) . '".');
}
return Redirect::route('piggybanks.index');
return Redirect::route('piggy_banks.index');
}
/**
* @param Piggybank $piggybank
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postRemove(Piggybank $piggybank)
public function postRemove(PiggyBank $piggyBank)
{
$amount = floatval(Input::get('amount'));
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
if ($amount <= $savedSoFar) {
$repetition = $piggybank->currentRelevantRep();
$repetition = $piggyBank->currentRelevantRep();
$repetition->currentamount -= $amount;
$repetition->save();
/*
* Create event!
*/
Event::fire('piggybank.removeMoney', [$piggybank, $amount]); // new and used.
Event::fire('piggy_bank.removeMoney', [$piggyBank, $amount]); // new and used.
Session::flash('success', 'Removed ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".');
Session::flash('success', 'Removed ' . Amount::format($amount, false) . ' from "' . e($piggyBank->name) . '".');
} else {
Session::flash('error', 'Could not remove ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".');
Session::flash('error', 'Could not remove ' . Amount::format($amount, false) . ' from "' . e($piggyBank->name) . '".');
}
return Redirect::route('piggybanks.index');
return Redirect::route('piggy_banks.index');
}
/**
* @param Piggybank $piggybank
* @param PiggyBank $piggyBank
*
* @SuppressWarnings("Unused")
*
* @return \Illuminate\View\View
*/
public function remove(Piggybank $piggybank)
public function remove(PiggyBank $piggyBank)
{
return View::make('piggybanks.remove', compact('piggybank'));
return View::make('piggy_banks.remove', compact('piggyBank'));
}
/**
* @param Piggybank $piggybank
* @param PiggyBank $piggyBank
*
* @return $this
*/
public function show(Piggybank $piggybank)
public function show(PiggyBank $piggyBank)
{
$events = $piggybank->piggybankevents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get();
$events = $piggyBank->piggyBankEvents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get();
/*
* Number of reminders:
*/
$amountPerReminder = $piggybank->amountPerReminder();
$remindersCount = $piggybank->countFutureReminders();
$subTitle = e($piggyBank->name);
return View::make('piggybanks.show', compact('amountPerReminder', 'remindersCount', 'piggybank', 'events'))->with('title', 'Piggy banks')->with(
'mainTitleIcon', 'fa-sort-amount-asc'
)->with(
'subTitle', $piggybank->name
);
return View::make('piggy_banks.show', compact('piggyBank', 'events', 'subTitle'));
}
@@ -259,101 +273,86 @@ class PiggybankController extends BaseController
*/
public function store()
{
$data = Input::all();
$data['repeats'] = 0;
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$data = Input::all();
$data['repeats'] = 0;
$data['user_id'] = Auth::user()->id;
$data['rep_every'] = 0;
$data['reminder_skip'] = 0;
$data['remind_me'] = intval(Input::get('remind_me'));
$data['order'] = 0;
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save piggy bank: ' . $messages['errors']->first());
return Redirect::route('piggybanks.create')->withInput()->withErrors($messages['errors']);
}
// store!
$piggyBank = $repos->store($data);
// always validate:
$messages = $this->_repository->validate($data);
/*
* Create the relevant repetition per Event.
*/
Event::fire('piggybank.store', [$piggyBank]); // new and used.
Session::flash('success', 'New piggy bank stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('piggybanks.create')->withInput();
} else {
return Redirect::route('piggybanks.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('piggybanks.create')->withInput();
break;
// 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 store piggy bank: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('piggy_banks.create')->withInput();
}
// store
$piggyBank = $this->_repository->store($data);
Event::fire('piggy_bank.store', [$piggyBank]); // new and used.
Session::flash('success', 'Piggy bank "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('piggy_banks.index');
}
return Redirect::route('piggy_banks.create')->withInput();
}
/**
* @param Piggybank $piggyBank
* @param PiggyBank $piggyBank
*
* @return $this
* @throws FireflyException
*/
public function update(Piggybank $piggyBank)
public function update(PiggyBank $piggyBank)
{
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$data = Input::except('_token');
$data = Input::except('_token');
$data['rep_every'] = 0;
$data['reminder_skip'] = 0;
$data['order'] = 0;
$data['remind_me'] = isset($data['remind_me']) ? 1 : 0;
$data['user_id'] = Auth::user()->id;
$data['repeats'] = 0;
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'return_to_edit':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save piggy bank: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($piggyBank, $data);
Event::fire('piggybank.update', [$piggyBank]); // new and used.
Session::flash('success', 'Piggy bank updated!');
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('piggybanks.edit', $piggyBank->id);
} else {
return Redirect::route('piggybanks.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput();
break;
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 piggy bank: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('piggy_banks.edit', $piggyBank->id)->withInput();
}
// update
$this->_repository->update($piggyBank, $data);
Session::flash('success', 'Piggy bank "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('piggy_banks.index');
}
// go back to update screen.
return Redirect::route('piggy_banks.edit', $piggyBank->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}
}

View File

@@ -3,6 +3,8 @@
/**
* Class PreferencesController
*
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
*/
class PreferencesController extends BaseController
{
@@ -21,8 +23,8 @@ class PreferencesController extends BaseController
*/
public function index()
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
@@ -30,9 +32,13 @@ class PreferencesController extends BaseController
$accounts = $acct->getAssetAccounts();
$viewRange = $preferences->get('viewRange', '1M');
$viewRangeValue = $viewRange->data;
$frontpage = $preferences->get('frontpageAccounts', []);
$frontPage = $preferences->get('frontPageAccounts', []);
$budgetMax = $preferences->get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data;
return View::make('preferences.index')->with('accounts', $accounts)->with('frontpageAccounts', $frontpage)->with('viewRange', $viewRangeValue);
return View::make('preferences.index', compact('budgetMaximum'))->with('accounts', $accounts)->with('frontPageAccounts', $frontPage)->with(
'viewRange', $viewRangeValue
);
}
/**
@@ -40,16 +46,15 @@ class PreferencesController extends BaseController
*/
public function postIndex()
{
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
// frontpage accounts
$frontpageAccounts = [];
foreach (Input::get('frontpageAccounts') as $id) {
$frontpageAccounts[] = intval($id);
// front page accounts
$frontPageAccounts = [];
foreach (Input::get('frontPageAccounts') as $id) {
$frontPageAccounts[] = intval($id);
}
$preferences->set('frontpageAccounts', $frontpageAccounts);
$preferences->set('frontPageAccounts', $frontPageAccounts);
// view range:
$preferences->set('viewRange', Input::get('viewRange'));
@@ -58,9 +63,14 @@ class PreferencesController extends BaseController
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

@@ -1,6 +1,8 @@
<?php
/**
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class ProfileController
*/
class ProfileController extends BaseController
@@ -32,7 +34,6 @@ class ProfileController extends BaseController
{
// old, new1, new2
/** @noinspection PhpUndefinedFieldInspection */
if (!Hash::check(Input::get('old'), Auth::user()->password)) {
Session::flash('error', 'Invalid current password!');
@@ -56,8 +57,8 @@ class ProfileController extends BaseController
}
// update the user with the new password.
/** @var \FireflyIII\Database\User $repository */
$repository = \App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = \App::make('FireflyIII\Database\User\User');
$repository->updatePassword(Auth::user(), Input::get('new1'));
Session::flash('success', 'Password changed!');
@@ -65,4 +66,4 @@ class ProfileController extends BaseController
return Redirect::route('profile');
}
}
}

View File

@@ -1,225 +0,0 @@
<?php
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class RecurringController
*
*/
class RecurringController extends BaseController
{
/**
*
*/
public function __construct()
{
View::share('title', 'Recurring transactions');
View::share('mainTitleIcon', 'fa-rotate-right');
}
/**
* @return $this
*/
public function create()
{
$periods = \Config::get('firefly.periods_to_text');
return View::make('recurring.create')->with('periods', $periods)->with('subTitle', 'Create new');
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return $this
*/
public function delete(RecurringTransaction $recurringTransaction)
{
return View::make('recurring.delete')->with('recurringTransaction', $recurringTransaction)->with(
'subTitle', 'Delete "' . $recurringTransaction->name . '"'
);
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(RecurringTransaction $recurringTransaction)
{
//Event::fire('recurring.destroy', [$recurringTransaction]);
/** @var \FireflyIII\Database\Recurring $repository */
$repository = App::make('FireflyIII\Database\Recurring');
$result = $repository->destroy($recurringTransaction);
if ($result === true) {
Session::flash('success', 'The recurring transaction was deleted.');
} else {
Session::flash('error', 'Could not delete the recurring transaction. Check the logs to be sure.');
}
return Redirect::route('recurring.index');
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return $this
*/
public function edit(RecurringTransaction $recurringTransaction)
{
$periods = \Config::get('firefly.periods_to_text');
return View::make('recurring.edit')->with('periods', $periods)->with('recurringTransaction', $recurringTransaction)->with(
'subTitle', 'Edit "' . $recurringTransaction->name . '"'
);
}
/**
* @return $this
*/
public function index()
{
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
$recurring = $repos->get();
return View::make('recurring.index', compact('recurring'));
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return mixed
*/
public function rescan(RecurringTransaction $recurringTransaction)
{
if (intval($recurringTransaction->active) == 0) {
Session::flash('warning', 'Inactive recurring transactions cannot be scanned.');
return Redirect::back();
}
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
$repos->scanEverything($recurringTransaction);
Session::flash('success', 'Rescanned everything.');
return Redirect::back();
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return mixed
*/
public function show(RecurringTransaction $recurringTransaction)
{
$journals = $recurringTransaction->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get();
$hideRecurring = true;
return View::make('recurring.show', compact('journals', 'hideRecurring', 'finalDate'))->with('recurring', $recurringTransaction)->with(
'subTitle', $recurringTransaction->name
);
}
/**
* @return $this
* @throws FireflyException
*/
public function store()
{
$data = Input::except('_token');
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save recurring transaction: ' . $messages['errors']->first());
return Redirect::route('recurring.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New recurring transaction stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('recurring.create')->withInput();
} else {
return Redirect::route('recurring.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('recurring.create')->withInput();
break;
}
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return $this
* @throws FireflyException
*/
public function update(RecurringTransaction $recurringTransaction)
{
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
$data = Input::except('_token');
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'create_another':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save recurring transaction: ' . $messages['errors']->first());
return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($recurringTransaction, $data);
Session::flash('success', 'Recurring transaction updated!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('recurring.edit', $recurringTransaction->id);
} else {
return Redirect::route('recurring.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput();
break;
}
}
}

View File

@@ -0,0 +1,139 @@
<?php
use FireflyIII\Helper\Related\RelatedInterface;
use Illuminate\Support\Collection;
/**
* Class RelatedController
*/
class RelatedController extends BaseController
{
protected $_repository;
public function __construct(RelatedInterface $repository)
{
$this->_repository = $repository;
}
/**
* @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 = $this->_repository->getJournalsByIds($unique);
$set->each(
function (TransactionJournal $journal) {
$journal->amount = Amount::format($journal->getAmount());
}
);
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 = $this->_repository->getUser()->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::make('related.relate', compact('journal', 'members'));
}
/**
* @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)
{
$search = e(trim(Input::get('searchValue')));
$result = $this->_repository->search($search, $journal);
$result->each(
function (TransactionJournal $j) {
$j->amount = Amount::format($j->getAmount());
}
);
return Response::json($result->toArray());
}
}

View File

@@ -26,25 +26,22 @@ class ReminderController extends BaseController
public function act(Reminder $reminder)
{
switch (get_class($reminder->remindersable)) {
default:
throw new FireflyException('Cannot act on reminder for ' . get_class($reminder->remindersable));
break;
break;
case 'Piggybank':
$amount = Reminders::amountForReminder($reminder);
$preFilled = [
'amount' => round($amount, 2),
'description' => 'Money for ' . $reminder->remindersable->name,
'piggybank_id' => $reminder->remindersable_id,
'account_to_id' => $reminder->remindersable->account_id
];
Session::flash('preFilled', $preFilled);
$class = get_class($reminder->remindersable);
return Redirect::route('transactions.create', 'transfer');
break;
if ($class == 'PiggyBank') {
$amount = Reminders::amountForReminder($reminder);
$preFilled = [
'amount' => round($amount, 2),
'description' => 'Money for ' . $reminder->remindersable->name,
'piggy_bank_id' => $reminder->remindersable_id,
'account_to_id' => $reminder->remindersable->account_id
];
Session::flash('preFilled', $preFilled);
return Redirect::route('transactions.create', 'transfer');
}
return View::make('error')->with('message', 'This reminder has an invalid class connected to it.');
}
/**
@@ -66,7 +63,7 @@ class ReminderController extends BaseController
*
* @return \Illuminate\Http\RedirectResponse
*/
public function notnow(Reminder $reminder)
public function notNow(Reminder $reminder)
{
$reminder->active = 0;
$reminder->notnow = 1;
@@ -85,7 +82,7 @@ class ReminderController extends BaseController
{
$amount = null;
if (get_class($reminder->remindersable) == 'Piggybank') {
if (get_class($reminder->remindersable) == 'PiggyBank') {
$amount = Reminders::amountForReminder($reminder);
}
@@ -93,4 +90,4 @@ class ReminderController extends BaseController
return View::make('reminders.show', compact('reminder', 'amount'));
}
}
}

View File

@@ -1,21 +1,29 @@
<?php
use Carbon\Carbon;
use FireflyIII\Database\PiggyBank\RepeatedExpense as Repository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
* Class RepeatedExpenseController
*/
class RepeatedExpenseController extends BaseController
{
/** @var Repository */
protected $_repository;
/**
*
* @param Repository $repository
*/
public function __construct()
public function __construct(Repository $repository)
{
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
$this->_repository = $repository;
}
/**
@@ -23,19 +31,73 @@ class RepeatedExpenseController extends BaseController
*/
public function create()
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$periods = Config::get('firefly.piggybank_periods');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
return View::make('repeatedexpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with(
return View::make('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::make('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.');
$this->_repository->destroy($repeatedExpense);
return Redirect::route('repeated.index');
}
/**
* @param PiggyBank $repeatedExpense
*
* @return $this
*/
public function edit(PiggyBank $repeatedExpense)
{
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$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,
'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::make('repeatedExpense.edit', compact('subTitle', 'subTitleIcon', 'repeatedExpense', 'accounts', 'periods', 'preFilled'));
}
/**
* @return \Illuminate\View\View
*/
@@ -44,92 +106,122 @@ class RepeatedExpenseController extends BaseController
$subTitle = 'Overview';
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
$expenses = $repository->get();
$expenses = $this->_repository->get();
$expenses->each(
function (Piggybank $piggyBank) use ($repository) {
function (PiggyBank $piggyBank) {
$piggyBank->currentRelevantRep();
}
);
return View::make('repeatedexpense.index', compact('expenses', 'subTitle'));
return View::make('repeatedExpense.index', compact('expenses', 'subTitle'));
}
/**
* @param Piggybank $piggyBank
* @param PiggyBank $repeatedExpense
*
* @return \Illuminate\View\View
*/
public function show(Piggybank $piggyBank)
public function show(PiggyBank $repeatedExpense)
{
$subTitle = $piggyBank->name;
$today = Carbon::now();
$subTitle = $repeatedExpense->name;
$today = Carbon::now();
$repetitions = $repeatedExpense->piggyBankRepetitions()->get();
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
$repetitions = $piggyBank->piggybankrepetitions()->get();
$repetitions->each(
function (PiggybankRepetition $repetition) use ($repository) {
$repository->calculateParts($repetition);
function (PiggyBankRepetition $repetition) {
$repetition->bars = $this->_repository->calculateParts($repetition);
}
);
return View::make('repeatedexpense.show', compact('repetitions', 'piggyBank', 'today', 'subTitle'));
return View::make('repeatedExpense.show', compact('repetitions', 'repeatedExpense', 'today', 'subTitle'));
}
/**
* @return $this
* @throws FireflyException
*
*/
public function store()
{
$data = Input::all();
$data['repeats'] = 1;
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
$data = Input::all();
$data['repeats'] = 1;
$data['user_id'] = Auth::user()->id;
$targetDate = new Carbon($data['targetdate']);
$startDate = \DateKit::subtractPeriod($targetDate, $data['rep_length']);
$data['startdate'] = $startDate->format('Y-m-d');
$data['targetdate'] = $targetDate->format('Y-m-d');
$data['reminder_skip'] = 0;
$data['remind_me'] = isset($data['remind_me']) ? 1 : 0;
$data['order'] = 0;
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repository->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save repeated expense: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('repeated.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repeated = $repository->store($data);
/*
* Create the relevant repetition per Event.
*/
Event::fire('piggybank.store', [$repeated]); // new and used.
Session::flash('success', 'New repeated expense stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('repeated.create')->withInput();
} else {
return Redirect::route('repeated.index');
}
break;
case 'validate_only':
$messageBags = $repository->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('repeated.create')->withInput();
break;
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 store repeated expense: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('repeated.create')->withInput();
}
// store
$piggyBank = $this->_repository->store($data);
Event::fire('piggy_bank.store', [$piggyBank]); // new and used.
Session::flash('success', 'Piggy bank "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('repeated.index');
}
return Redirect::route('repeated.create')->withInput();
}
}
/**
* @param PiggyBank $repeatedExpense
*
* @return $this
* @throws FireflyException
*/
public function update(PiggyBank $repeatedExpense)
{
$data = Input::except('_token');
$data['rep_every'] = 0;
$data['reminder_skip'] = 0;
$data['order'] = 0;
$data['repeats'] = 1;
$data['remind_me'] = isset($data['remind_me']) ? 1 : 0;
$data['user_id'] = Auth::user()->id;
// always validate:
$messages = $this->_repository->validate($data);
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 repeated expense: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('repeated.edit', $repeatedExpense->id)->withInput();
}
// update
$this->_repository->update($repeatedExpense, $data);
Session::flash('success', 'Repeated expense "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('repeated.index');
}
// go back to update screen.
return Redirect::route('repeated.edit', $repeatedExpense->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}

View File

@@ -1,142 +1,60 @@
<?php
use Carbon\Carbon;
use FireflyIII\Database\Account as AccountRepository;
use FireflyIII\Database\Report as ReportRepository;
use FireflyIII\Database\TransactionJournal as TransactionJournalRepository;
use FireflyIII\Report\ReportInterface as ReportHelper;
use FireflyIII\Database\TransactionJournal\TransactionJournal as TransactionJournalRepository;
use FireflyIII\Report\ReportInterface as Report;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* Class ReportController
*/
class ReportController extends BaseController
{
/** @var AccountRepository */
protected $_accounts;
/** @var \FireflyIII\Database\Budget\Budget */
protected $_budgets;
/** @var TransactionJournalRepository */
protected $_journals;
/** @var ReportHelper */
protected $_reports;
/** @var ReportRepository */
/** @var Report */
protected $_repository;
/**
* @param AccountRepository $accounts
* @param TransactionJournalRepository $journals
* @param ReportHelper $reports
* @param ReportRepository $repository
* @param Report $repository
*/
public function __construct(AccountRepository $accounts, TransactionJournalRepository $journals, ReportHelper $reports, ReportRepository $repository)
public function __construct(TransactionJournalRepository $journals, Report $repository)
{
$this->_accounts = $accounts;
$this->_journals = $journals;
$this->_reports = $reports;
$this->_repository = $repository;
/** @var \FireflyIII\Database\Budget\Budget _budgets */
$this->_budgets = App::make('FireflyIII\Database\Budget\Budget');
View::share('title', 'Reports');
View::share('mainTitleIcon', 'fa-line-chart');
}
/**
* @param $year
* @param $month
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function budgets($year, $month)
public function budget($year = '2014', $month = '1')
{
try {
$start = new Carbon($year . '-' . $month . '-01');
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid date');
}
$end = clone $start;
$title = 'Reports';
$subTitle = 'Budgets in ' . $start->format('F Y');
$mainTitleIcon = 'fa-line-chart';
$subTitleIcon = 'fa-bar-chart';
$date = new Carbon($year . '-' . $month . '-01');
$dayEarly = clone $date;
$dayEarly = $dayEarly->subDay();
$accounts = $this->_repository->getAccountListBudgetOverview($date);
$budgets = $this->_repository->getBudgetsForMonth($date);
$end->endOfMonth();
// get a list of all budgets and expenses.
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
$budgets = $budgetRepository->get();
// calculate some stuff:
$budgets->each(
function (Budget $budget) use ($start, $end, $budgetRepository) {
$limitRepetitions = $budget->limitrepetitions()->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d'))->where(
'enddate', '<=', $end->format(
'Y-m-d'
)
)->get();
$repInfo = [];
/** @var LimitRepetition $repetition */
foreach ($limitRepetitions as $repetition) {
$spent = $budgetRepository->spentInPeriod($budget, $start, $end);
if ($spent > floatval($repetition->amount)) {
// overspent!
$overspent = true;
$pct = floatval($repetition->amount) / $spent * 100;
} else {
$overspent = false;
$pct = $spent / floatval($repetition->amount) * 100;
}
$pctDisplay = $spent / floatval($repetition->amount) * 100;
$repInfo[] = [
'date' => DateKit::periodShow($repetition->startdate, $repetition->limit->repeat_freq),
'spent' => $spent,
'budgeted' => floatval($repetition->amount),
'left' => floatval($repetition->amount) - $spent,
'pct' => ceil($pct),
'pct_display' => ceil($pctDisplay),
'overspent' => $overspent,
];
}
$budget->repInfo = $repInfo;
}
);
$accounts = $accountRepository->getAssetAccounts();
$accounts->each(
function (Account $account) use ($start, $end, $accountRepository) {
$journals = $accountRepository->getTransactionJournalsInRange($account, $start, $end);
$budgets = [];
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$budgetId = isset($journal->budgets[0]) ? $journal->budgets[0]->id : 0;
$budgetName = isset($journal->budgets[0]) ? $journal->budgets[0]->name : '(no budget)';
if (!isset($budgets[$budgetId])) {
$arr = [
'budget_id' => $budgetId,
'budget_name' => $budgetName,
'spent' => floatval($journal->getAmount()),
'budgeted' => 0,
];
$budgets[$budgetId] = $arr;
} else {
$budgets[$budgetId]['spent'] += floatval($journal->getAmount());
}
}
foreach ($budgets as $budgetId => $budget) {
$budgets[$budgetId]['left'] = $budget['budgeted'] - $budget['spent'];
}
$account->budgetInfo = $budgets;
}
);
return View::make('reports.budgets', compact('start', 'end', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon', 'budgets', 'accounts'));
return View::make('reports.budget', compact('date', 'accounts', 'budgets', 'dayEarly'));
}
@@ -146,8 +64,8 @@ class ReportController extends BaseController
public function index()
{
$start = $this->_journals->firstDate();
$months = $this->_reports->listOfMonths(clone $start);
$years = $this->_reports->listOfYears(clone $start);
$months = $this->_repository->listOfMonths(clone $start);
$years = $this->_repository->listOfYears(clone $start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
@@ -155,82 +73,32 @@ class ReportController extends BaseController
}
/**
* @param $year
* @param $month
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function unbalanced($year, $month)
public function month($year = '2014', $month = '1')
{
try {
$date = new Carbon($year . '-' . $month . '-01');
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid date.');
}
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$title = 'Reports';
$subTitle = 'Unbalanced transactions in ' . $start->format('F Y');
$mainTitleIcon = 'fa-line-chart';
$subTitleIcon = 'fa-bar-chart';
$end->endOfMonth();
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report.
$income = $this->_repository->getIncomeForMonth($date);
$expenses = $this->_repository->getExpenseGroupedForMonth($date, 10);
$budgets = $this->_repository->getBudgetsForMonth($date);
$categories = $this->_repository->getCategoriesForMonth($date, 10);
$accounts = $this->_repository->getAccountsForMonth($date);
/** @var \FireflyIII\Database\TransactionJournal $journalRepository */
$journalRepository = App::make('FireflyIII\Database\TransactionJournal');
/*
* Get all journals from this month:
*/
$journals = $journalRepository->getInDateRange($start, $end);
/*
* Filter withdrawals:
*/
$withdrawals = $journals->filter(
function (TransactionJournal $journal) {
if ($journal->transactionType->type == 'Withdrawal' && count($journal->budgets) == 0) {
// count groups related to balance.
if ($journal->transactiongroups()->where('relation', 'balance')->count() == 0) {
return $journal;
}
}
return null;
}
return View::make(
'reports.month',
compact('date', 'accounts', 'categories', 'budgets', 'expenses', 'subTitle', 'displaySum', 'subTitleIcon', 'income')
);
/*
* Filter deposits.
*/
$deposits = $journals->filter(
function (TransactionJournal $journal) {
if ($journal->transactionType->type == 'Deposit' && count($journal->budgets) == 0) {
// count groups related to balance.
if ($journal->transactiongroups()->where('relation', 'balance')->count() == 0) {
return $journal;
}
}
return null;
}
);
/*
* Filter transfers (not yet used)
*/
// $transfers = $journals->filter(
// function (TransactionJournal $journal) {
// if ($journal->transactionType->type == 'Transfer') {
// return $journal;
// }
// }
// );
$journals = $withdrawals->merge($deposits);
return View::make('reports.unbalanced', compact('start', 'end', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon', 'journals'));
}
/**
@@ -243,76 +111,23 @@ class ReportController extends BaseController
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid date.');
}
$date = new Carbon('01-01-' . $year);
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$end->endOfYear();
$title = 'Reports';
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart';
$balances = $this->_reports->yearBalanceReport($date);
$groupedIncomes = $this->_reports->groupByRevenue($date, 'income');
$groupedExpenses = $this->_reports->groupByRevenue($date, 'expense');
$balances = $this->_repository->yearBalanceReport($date);
$groupedIncomes = $this->_repository->revenueGroupedByAccount($date, $end, 15);
$groupedExpenses = $this->_repository->expensesGroupedByAccount($date, $end, 15);
return View::make(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')
);
/*
* For this year, get:
* - the sum of all expenses.
* - the sum of all incomes
* - per month, the sum of all expenses
* - per month, the sum of all incomes
* - 2x for shared and not-shared alike.
*
* - balance difference for all accounts.
*/
$accounts = $accountRepository->getAssetAccounts();
// get some sums going
$summary = [];
$end = clone $date;
$end->endOfYear();
while ($date < $end) {
$month = $date->format('F');
$income = 0;
$incomeShared = 0;
$expense = 0;
$expenseShared = 0;
foreach ($accounts as $account) {
if ($account->accountRole == 'sharedExpense') {
$incomeShared += $reportRepository->getIncomeByMonth($account, $date);
$expenseShared += $reportRepository->getExpenseByMonth($account, $date);
} else {
$income += $reportRepository->getIncomeByMonth($account, $date);
$expense += $reportRepository->getExpenseByMonth($account, $date);
}
}
$summary[] = [
'month' => $month,
'income' => $income,
'expense' => $expense,
'incomeShared' => $incomeShared,
'expenseShared' => $expenseShared,
];
$date->addMonth();
}
// draw some charts etc.
return View::make('reports.year', compact('summary', 'date'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart')->with('subTitle', $year)
->with(
'subTitleIcon', 'fa-bar-chart'
)->with('year', $year);
}
}
}

View File

@@ -17,7 +17,7 @@ class SearchController extends BaseController
$subTitle = null;
$rawQuery = null;
$result = [];
if (!is_null(Input::get('q'))) {
if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) {
$rawQuery = trim(Input::get('q'));
$words = explode(' ', $rawQuery);
$subTitle = 'Results for "' . e($rawQuery) . '"';
@@ -35,4 +35,4 @@ class SearchController extends BaseController
'mainTitleIcon', 'fa-search'
)->with('query', $rawQuery)->with('result', $result);
}
}
}

View File

@@ -1,11 +1,19 @@
<?php
use FireflyIII\Database\TransactionJournal\TransactionJournal as Repository;
use FireflyIII\Exception\FireflyException;
use FireflyIII\Helper\TransactionJournal\HelperInterface as Helper;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("ExcessiveClassComplexity")
*
* Class TransactionController
*
*/
@@ -13,54 +21,25 @@ class TransactionController extends BaseController
{
/** @var Helper */
protected $_helper;
/** @var Repository */
protected $_repository;
/**
* Construct a new transaction controller with two of the most often used helpers.
*
* @param Repository $repository
* @param Helper $helper
*/
public function __construct()
public function __construct(Repository $repository, Helper $helper)
{
$this->_repository = $repository;
$this->_helper = $helper;
View::share('title', 'Transactions');
View::share('mainTitleIcon', 'fa-repeat');
}
/**
*
* TODO this needs cleaning up and thinking over.
*
* @param TransactionJournal $journal
*
* @return array|\Illuminate\Http\JsonResponse
*/
public function alreadyRelated(TransactionJournal $journal)
{
$ids = [];
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $jrnl */
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id != $journal->id) {
$ids[] = $jrnl->id;
}
}
}
$unique = array_unique($ids);
if (count($ids) > 0) {
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$set = $repository->getByIds($ids);
$set->each(
function (TransactionJournal $journal) {
$journal->amount = mf($journal->getAmount());
}
);
return Response::json($set->toArray());
} else {
return (new Collection)->toArray();
}
}
/**
* Shows the view helping the user to create a new transaction journal.
@@ -71,40 +50,18 @@ class TransactionController extends BaseController
*/
public function create($what = 'deposit')
{
/*
* The repositories we need:
*/
$accounts = FFForm::makeSelectList($this->_helper->getAssetAccounts());
$budgets = FFForm::makeSelectList($this->_helper->getBudgets());
$budgets[0] = '(no budget)';
$piggyBanks = $this->_helper->getPiggyBanks();
$repeatedExpenses = $this->_helper->getRepeatedExpenses();
$list = $piggyBanks->merge($repeatedExpenses);
$piggies = FFForm::makeSelectList($list);
$piggies[0] = '(no piggy bank)';
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id'];
$subTitle = 'Add a new ' . e($what);
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \FireflyIII\Database\Piggybank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\Piggybank');
/** @var \FireflyIII\Database\RepeatedExpense $repRepository */
$repRepository = App::make('FireflyIII\Database\RepeatedExpense');
// get asset accounts with names and id's .
$assetAccounts = FFForm::makeSelectList($accountRepository->getAssetAccounts());
// get budgets as a select list.
$budgets = FFForm::makeSelectList($budgetRepository->get());
$budgets[0] = '(no budget)';
// get the piggy banks.
$list = $piggyRepository->get()->merge($repRepository->get());
$piggies = FFForm::makeSelectList($list);
$piggies[0] = '(no piggy bank)';
asort($piggies);
/*
* respond to a possible given values in the URL.
*/
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id'];
foreach ($respondTo as $r) {
if (!is_null(Input::get($r))) {
$preFilled[$r] = Input::get($r);
@@ -112,24 +69,25 @@ class TransactionController extends BaseController
}
Session::put('preFilled', $preFilled);
return View::make('transactions.create')->with('accounts', $assetAccounts)->with('budgets', $budgets)->with('what', $what)->with('piggies', $piggies)
->with('subTitle', 'Add a new ' . $what);
asort($piggies);
return View::make('transactions.create', compact('accounts', 'budgets', 'what', 'piggies', 'subTitle'));
}
/**
* Shows the form that allows a user to delete a transaction journal.
*
* @param TransactionJournal $transactionJournal
* @param TransactionJournal $journal
*
* @return $this
*/
public function delete(TransactionJournal $transactionJournal)
public function delete(TransactionJournal $journal)
{
$type = strtolower($transactionJournal->transactionType->type);
$type = strtolower($journal->transactionType->type);
$subTitle = 'Delete ' . e($type) . ' "' . e($journal->description) . '"';
return View::make('transactions.delete')->with('journal', $transactionJournal)->with(
'subTitle', 'Delete ' . $type . ' "' . $transactionJournal->description . '"'
);
return View::make('transactions.delete', compact('journal', 'subTitle'));
}
@@ -141,14 +99,12 @@ class TransactionController extends BaseController
*/
public function destroy(TransactionJournal $transactionJournal)
{
$type = $transactionJournal->transactionType->type;
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$repository->destroy($transactionJournal);
$type = $transactionJournal->transactionType->type;
$return = 'withdrawal';
Session::flash('success', 'Transaction "' . e($transactionJournal->description) . '" destroyed.');
$this->_repository->destroy($transactionJournal);
switch ($type) {
case 'Deposit':
@@ -162,38 +118,6 @@ class TransactionController extends BaseController
return Redirect::route('transactions.index', $return);
}
/**
* TODO this needs cleaning up and thinking over.
*
* @return \Illuminate\Http\JsonResponse
*/
public function doRelate()
{
$id = intval(Input::get('id'));
$sister = intval(Input::get('relateTo'));
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$journal = $repository->find($id);
$sis = $repository->find($sister);
if ($journal && $sis) {
$group = new TransactionGroup;
$group->relation = 'balance';
$group->user_id = $repository->getUser()->id;
$group->save();
$group->transactionjournals()->save($journal);
$group->transactionjournals()->save($sis);
return Response::json(true);
}
return Response::json(false);
}
/**
* Shows the view to edit a transaction.
*
@@ -203,128 +127,42 @@ class TransactionController extends BaseController
*/
public function edit(TransactionJournal $journal)
{
/*
* All the repositories we need:
*/
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \FireflyIII\Database\Piggybank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\Piggybank');
// type is useful for display:
$what = strtolower($journal->transactiontype->type);
// get asset accounts with names and id's.
$accounts = FFForm::makeSelectList($accountRepository->getAssetAccounts());
// get budgets as a select list.
$budgets = FFForm::makeSelectList($budgetRepository->get());
$budgets[0] = '(no budget)';
/*
* Get all piggy banks plus (if any) the relevant piggy bank. Since just one
* of the transactions in the journal has this field, it should all fill in nicely.
*/
// get the piggy banks.
$piggies = FFForm::makeSelectList($piggyRepository->get());
$piggies[0] = '(no piggy bank)';
$piggyBankId = 0;
foreach ($journal->transactions as $t) {
if (!is_null($t->piggybank_id)) {
$piggyBankId = $t->piggybank_id;
}
}
/*
* Data to properly display the edit form.
*/
$preFilled = [
'date' => $journal->date->format('Y-m-d'),
'category' => '',
'budget_id' => 0,
'piggybank_id' => $piggyBankId
$what = strtolower($journal->transactiontype->type);
$subTitle = 'Edit ' . e($what) . ' "' . e($journal->description) . '"';
$budgets = FFForm::makeSelectList($this->_helper->getBudgets(), true);
$accounts = FFForm::makeSelectList($this->_helper->getAssetAccounts());
$piggies = FFForm::makeSelectList($this->_helper->getPiggyBanks(), true);
$transactions = $journal->transactions()->orderBy('amount', 'DESC')->get();
$preFilled = [
'date' => $journal->date->format('Y-m-d'),
'category' => '',
'budget_id' => 0,
'piggy_bank_id' => 0
];
/*
* Fill in the category.
*/
$category = $journal->categories()->first();
if (!is_null($category)) {
$preFilled['category'] = $category->name;
}
/*
* Switch on the type of transaction edited by the user and fill in other
* relevant fields:
*/
switch ($what) {
case 'withdrawal':
if (floatval($journal->transactions[0]->amount) < 0) {
// transactions[0] is the asset account that paid for the withdrawal.
$preFilled['account_id'] = $journal->transactions[0]->account->id;
$preFilled['expense_account'] = $journal->transactions[1]->account->name;
$preFilled['amount'] = floatval($journal->transactions[1]->amount);
} else {
// transactions[1] is the asset account that paid for the withdrawal.
$preFilled['account_id'] = $journal->transactions[1]->account->id;
$preFilled['expense_account'] = $journal->transactions[0]->account->name;
$preFilled['amount'] = floatval($journal->transactions[0]->amount);
}
$budget = $journal->budgets()->first();
if (!is_null($budget)) {
$preFilled['budget_id'] = $budget->id;
}
break;
case 'deposit':
if (floatval($journal->transactions[0]->amount) < 0) {
// transactions[0] contains the account the money came from.
$preFilled['account_id'] = $journal->transactions[1]->account->id;
$preFilled['revenue_account'] = $journal->transactions[0]->account->name;
$preFilled['amount'] = floatval($journal->transactions[1]->amount);
} else {
// transactions[1] contains the account the money came from.
$preFilled['account_id'] = $journal->transactions[0]->account->id;
$preFilled['revenue_account'] = $journal->transactions[1]->account->name;
$preFilled['amount'] = floatval($journal->transactions[0]->amount);
}
break;
case 'transfer':
if (floatval($journal->transactions[0]->amount) < 0) {
// zero = from account.
$preFilled['account_from_id'] = $journal->transactions[0]->account->id;
$preFilled['account_to_id'] = $journal->transactions[1]->account->id;
$preFilled['amount'] = floatval($journal->transactions[1]->amount);
} else {
// one = from account
$preFilled['account_from_id'] = $journal->transactions[1]->account->id;
$preFilled['account_to_id'] = $journal->transactions[0]->account->id;
$preFilled['amount'] = floatval($journal->transactions[0]->amount);
}
if ($journal->piggybankevents()->count() > 0) {
$preFilled['piggybank_id'] = $journal->piggybankevents()->first()->piggybank_id;
}
break;
$budget = $journal->budgets()->first();
if (!is_null($budget)) {
$preFilled['budget_id'] = $budget->id;
}
/*
* Show the view.
*/
if ($journal->piggyBankEvents()->count() > 0) {
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->first()->piggy_bank_id;
}
return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts)->with(
'what', $what
)->with('budgets', $budgets)->with('data', $preFilled)->with('piggies', $piggies)->with(
'subTitle', 'Edit ' . $what . ' "' . $journal->description . '"'
);
$preFilled['amount'] = $journal->getAmount();
$preFilled['account_id'] = $this->_helper->getAssetAccount($what, $transactions);
$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);
}
/**
@@ -334,28 +172,24 @@ class TransactionController extends BaseController
*/
public function index($what)
{
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses';
$journals = $repository->getWithdrawalsPaginated(50);
$journals = $this->_repository->getWithdrawalsPaginated(50);
break;
case 'revenue':
case 'deposit':
$subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits';
$journals = $repository->getDepositsPaginated(50);
$journals = $this->_repository->getDepositsPaginated(50);
break;
case 'transfer':
case 'transfers':
$subTitleIcon = 'fa-arrows-h';
$subTitle = 'Transfers';
$journals = $repository->getTransfersPaginated(50);
$journals = $this->_repository->getTransfersPaginated(50);
break;
}
@@ -363,51 +197,7 @@ class TransactionController extends BaseController
}
/**
* @param TransactionJournal $journal
*
* @return \Illuminate\View\View
*/
public function relate(TransactionJournal $journal)
{
$groups = $journal->transactiongroups()->get();
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($groups as $group) {
/** @var TransactionJournal $jrnl */
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id != $journal->id) {
$members->push($jrnl);
}
}
}
return View::make('transactions.relate', compact('journal', 'members'));
}
/**
* TODO this needs cleaning up and thinking over.
*
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
*/
public function relatedSearch(TransactionJournal $journal)
{
$search = e(trim(Input::get('searchValue')));
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$result = $repository->searchRelated($search, $journal);
$result->each(
function (TransactionJournal $j) {
$j->amount = mf($j->getAmount());
}
);
return Response::json($result->toArray());
}
/**
* @param TransactionJournal $journal
@@ -431,16 +221,16 @@ class TransactionController extends BaseController
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $jrnl */
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id != $journal->id) {
$members->push($jrnl);
/** @var TransactionJournal $loopJournal */
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id != $journal->id) {
$members->push($loopJournal);
}
}
}
return View::make('transactions.show', compact('journal', 'members'))->with(
'subTitle', $journal->transactionType->type . ' "' . $journal->description . '"'
'subTitle', e($journal->transactionType->type) . ' "' . e($journal->description) . '"'
);
}
@@ -452,156 +242,90 @@ class TransactionController extends BaseController
*/
public function store($what)
{
$data = Input::except('_token');
$data['what'] = $what;
$data['currency'] = 'EUR';
$data = Input::except('_token');
$transactionType = $this->_repository->getJournalType($what);
$transactionCurrency = $this->_repository->getJournalCurrency('EUR');
$data['transaction_type_id'] = $transactionType->id;
$data['transaction_currency_id'] = $transactionCurrency->id;
$data['completed'] = 0;
$data['what'] = $what;
$data['currency'] = 'EUR';
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
// always validate:
$messages = $this->_repository->validate($data);
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repository->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save transaction: ' . $messages['errors']->first());
return Redirect::route('transactions.create', $what)->withInput()->withErrors($messages['errors']);
}
// store!
$journal = $repository->store($data);
Session::flash('success', 'New transaction stored!');
/*
* Trigger a search for the related (if selected)
* piggy bank and store an event.
*/
$piggyID = null;
if (!is_null(Input::get('piggybank_id')) && intval(Input::get('piggybank_id')) > 0) {
$piggyID = intval(Input::get('piggybank_id'));
}
Event::fire('transactionJournal.store', [$journal, $piggyID]); // new and used.
/*
* Also trigger on both transactions.
*/
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
Event::fire('transaction.store', [$transaction]);
}
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('transactions.create', $what)->withInput();
} else {
return Redirect::route('transactions.index', $what);
}
break;
case 'validate_only':
$messageBags = $repository->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('transactions.create', $what)->withInput();
break;
}
}
/**
* TODO this needs cleaning up and thinking over.
*
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function unrelate(TransactionJournal $journal)
{
$groups = $journal->transactiongroups()->get();
$relatedTo = intval(Input::get('relation'));
/** @var TransactionGroup $group */
foreach ($groups as $group) {
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id == $relatedTo) {
// remove from group:
$group->transactionjournals()->detach($relatedTo);
}
}
if ($group->transactionjournals()->count() == 1) {
$group->delete();
}
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 store transaction: ' . $messages['errors']->first());
}
return Response::json(true);
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('transactions.create', $data['what'])->withInput();
}
// store
$journal = $this->_repository->store($data);
Event::fire('transactionJournal.store', [$journal, Input::get('piggy_bank_id')]); // new and used.
/*
* Also trigger on both transactions.
*/
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
Event::fire('transaction.store', [$transaction]);
}
Session::flash('success', 'Transaction "' . e($data['description']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('transactions.index', $data['what']);
}
return Redirect::route('transactions.create', $data['what'])->withInput();
}
/**
* @param TransactionJournal $journal
*
* @return $this
* @throws FireflyException
*/
public function update(TransactionJournal $journal)
{
/** @var \FireflyIII\Database\TransactionJournal $repos */
$repos = App::make('FireflyIII\Database\TransactionJournal');
$data = Input::except('_token');
$data['currency'] = 'EUR';
$data['what'] = strtolower($journal->transactionType->type);
$data['transaction_type_id'] = $journal->transaction_type_id;
$data['transaction_currency_id'] = $journal->transaction_currency_id;
$data['completed'] = 1;
$messages = $this->_repository->validate($data);
$data = Input::except('_token');
$data['currency'] = 'EUR';
$data['what'] = strtolower($journal->transactionType->type);
switch (Input::get('post_submit_action')) {
case 'update':
case 'return_to_edit':
$messageBag = $repos->update($journal, $data);
if ($messageBag->count() == 0) {
// has been saved, return to index:
Session::flash('success', 'Transaction updated!');
Event::fire('transactionJournal.update', [$journal]); // new and used.
/*
* Also trigger on both transactions.
*/
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
Event::fire('transaction.update', [$transaction]);
}
if (Input::get('post_submit_action') == 'return_to_edit') {
return Redirect::route('transactions.edit', $journal->id)->withInput();
} else {
return Redirect::route('transactions.index', $data['what']);
}
} else {
Session::flash('error', 'Could not update transaction: ' . $journal->getErrors()->first());
return Redirect::route('transactions.edit', $journal->id)->withInput()->withErrors(
$journal->getErrors()
);
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('transactions.edit', $journal->id)->withInput();
break;
default:
throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.');
break;
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 transaction: ' . $messages['errors']->first());
}
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('transactions.edit', $journal->id)->withInput();
}
$this->_repository->update($journal, $data);
Session::flash('success', 'Transaction "' . e($data['description']) . '" updated.');
Event::fire('transactionJournal.update', [$journal]); // new and used.
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
Event::fire('transaction.update', [$transaction]);
}
if ($data['post_submit_action'] == 'update') {
return Redirect::route('transactions.index', $data['what']);
}
// go back to update screen.
return Redirect::route('transactions.edit', $journal->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}
}

View File

@@ -33,7 +33,7 @@ class UserController extends BaseController
Auth::logout();
Session::flush();
return Redirect::route('index');
return Redirect::route('login');
}
/**
@@ -67,29 +67,19 @@ class UserController extends BaseController
*/
public function postRegister()
{
if (Config::get('auth.allow_register') !== true) {
return View::make('error')->with('message', 'Not possible');
}
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = App::make('FireflyIII\Database\User\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
$user = $repository->register(Input::all());
//$user = $this->user->register(Input::all());
if ($user) {
if (Config::get('auth.verify_mail') === true) {
$email->sendVerificationMail($user);
$email->sendVerificationMail($user);
return View::make('user.verification-pending');
}
$email->sendPasswordMail($user);
return View::make('user.registered');
return View::make('user.verification-pending');
}
@@ -102,11 +92,11 @@ class UserController extends BaseController
*
* @return \Illuminate\View\View
*/
public function postRemindme()
public function postRemindMe()
{
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = App::make('FireflyIII\Database\User\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
@@ -116,16 +106,11 @@ class UserController extends BaseController
if (!$user) {
Session::flash('error', 'No good!');
return View::make('user.remindme');
return View::make('user.remindMe');
}
if (Config::get('auth.verify_reset') === true) {
$email->sendResetVerification($user);
$email->sendResetVerification($user);
return View::make('user.verification-pending');
}
$email->sendPasswordMail($user);
return View::make('user.registered');
return View::make('user.verification-pending');
}
@@ -136,9 +121,6 @@ class UserController extends BaseController
*/
public function register()
{
if (Config::get('auth.allow_register') !== true) {
return View::make('error')->with('message', 'Not possible');
}
return View::make('user.register');
}
@@ -148,9 +130,9 @@ class UserController extends BaseController
*
* @return \Illuminate\View\View
*/
public function remindme()
public function remindMe()
{
return View::make('user.remindme');
return View::make('user.remindMe');
}
/**
@@ -163,8 +145,8 @@ class UserController extends BaseController
public function reset($reset)
{
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = App::make('FireflyIII\Database\User\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
@@ -176,7 +158,7 @@ class UserController extends BaseController
return View::make('user.registered');
}
return View::make('error')->with('message', 'Yo no hablo reset code!');
return View::make('error')->with('message', 'No reset code found!');
}
}
}

View File

@@ -5,8 +5,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateUsersTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateUsersTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateAccountTypesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateAccountTypesTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateAccountsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateAccountsTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentsTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePiggybanksTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePiggybanksTable extends Migration
{
@@ -46,7 +45,7 @@ class CreatePiggybanksTable extends Migration
$table->boolean('remind_me');
$table->integer('order')->unsigned();
// connect account to piggybank.
// connect account to piggy bank.
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
// for an account, the name must be unique.

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionCurrenciesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionCurrenciesTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionTypesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionTypesTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateRecurringTransactionsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateRecurringTransactionsTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionJournalsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionJournalsTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionsTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentTransactionTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentTransactionTable extends Migration
{
@@ -18,7 +17,7 @@ class CreateComponentTransactionTable extends Migration
*/
public function down()
{
Schema::drop('component_transaction');
Schema::dropIfExists('component_transaction');
}
/**

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentTransactionJournalTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentTransactionJournalTable extends Migration
{
@@ -30,19 +29,19 @@ class CreateComponentTransactionJournalTable extends Migration
{
Schema::create(
'component_transaction_journal', function (Blueprint $table) {
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
// link components with component_id
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link components with component_id
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link transaction journals with transaction_journal_id
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
// link transaction journals with transaction_journal_id
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
// combo must be unique:
$table->unique(['component_id', 'transaction_journal_id'],'cid_tjid_unique');
}
// combo must be unique:
$table->unique(['component_id', 'transaction_journal_id'], 'cid_tjid_unique');
}
);
}

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePreferencesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePreferencesTable extends Migration
{

View File

@@ -1,11 +1,11 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateSessionTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateSessionTable extends Migration
{
@@ -28,11 +28,11 @@ class CreateSessionTable extends Migration
public function up()
{
Schema::create(
'sessions', function ($t) {
$t->string('id')->unique();
$t->text('payload');
$t->integer('last_activity');
}
'sessions', function (Blueprint $table) {
$table->string('id')->unique();
$table->text('payload');
$table->integer('last_activity');
}
);
}

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateLimitsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateLimitsTable extends Migration
{
@@ -30,19 +29,19 @@ class CreateLimitsTable extends Migration
{
Schema::create(
'limits', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('component_id')->unsigned();
$table->date('startdate');
$table->decimal('amount', 10, 2);
$table->boolean('repeats');
$table->enum('repeat_freq', ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly']);
$table->increments('id');
$table->timestamps();
$table->integer('component_id')->unsigned();
$table->date('startdate');
$table->decimal('amount', 10, 2);
$table->boolean('repeats');
$table->enum('repeat_freq', ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly']);
$table->unique(['component_id', 'startdate', 'repeat_freq']);
$table->unique(['component_id', 'startdate', 'repeat_freq'], 'unique_ci_combi');
// connect component
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
}
// connect component
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
}
);
}

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateLimitRepeatTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateLimitRepeatTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentRecurringTransactionTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentRecurringTransactionTable extends Migration
{
@@ -18,7 +17,7 @@ class CreateComponentRecurringTransactionTable extends Migration
*/
public function down()
{
Schema::drop('component_recurring_transaction');
Schema::dropIfExists('component_recurring_transaction');
}
/**
@@ -30,21 +29,21 @@ class CreateComponentRecurringTransactionTable extends Migration
{
Schema::create(
'component_recurring_transaction', function (Blueprint $table) {
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('recurring_transaction_id')->unsigned();
$table->boolean('optional');
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('recurring_transaction_id')->unsigned();
$table->boolean('optional');
// link components with component_id
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link components with component_id
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link transaction journals with transaction_journal_id
$table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('cascade');
// link transaction journals with transaction_journal_id
$table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('cascade');
// component and recurring transaction must be unique.
$table->unique(['component_id','recurring_transaction_id'],'cid_rtid_unique');
// component and recurring transaction must be unique.
$table->unique(['component_id', 'recurring_transaction_id'], 'cid_rtid_unique');
}
}
);
}

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePiggyInstance
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePiggybankRepetitionsTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePiggybankEventsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePiggybankEventsTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateRemindersTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateRemindersTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateAccountMetaTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateAccountMetaTable extends Migration
{

View File

@@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionGroupsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionGroupsTable extends Migration
{

View File

@@ -0,0 +1,494 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Down:
* 1. Create new Components based on Budgets.
* 2. Create new Components based on Categories
* 3. Recreate component_id in limits
* 4. Update all budget_limits entries (component_id).
* 5. Add the foreign key to component_id in budget_limits
* 6. Drop column 'budget_id' in budget_limits.
* 7. Create table journal_components.
* 8. create entries for budgets in journal_components.
* 9. create entries for categories in journal_components.
* 10. drop table budget_journals
* 11. drop table category_journals
* 12. drop table budgets
* 13. drop table categories.
* 14. rename budget_limits to limits.
* 15. Rename piggy_bank_events to piggybank_events
* 16. Rename field 'budget_limit_id' to 'limit_id' in 'limit_repetitions'
* 17. Do not recreate component_recurring_transaction
* 18. Do not recreate component_transaction
* 19. Do not recreate field 'piggybank_id' in 'transactions'
* 20. Drop fields from currency table.
*
*
*
* Up:
*
* 1. Create new budget table.
* 2. Create new category table.
* 3. Create journal_budget table.
* 4. Create journal_category table.
* 5. Move budgets to new budgets table AND move journal_components to budget_components.
* 6. Move categories to categories table AND move journal_components to category_components.
* 7. Rename limits to budget_limits.
* 8. Rename piggybank_events to piggy_bank_events
* 9. Rename field 'limit_id' to 'budget_limit_id' in 'limit_repetitions'
* 10. Create field budget_id in budget_limits.
* 11. Update budget_limits with budgets (instead of components).
* 12. drop table journal_components
* 13. Drop table component_recurring_transaction
* 14. Drop table component_transaction
* 15. Drop field 'piggybank_id' from 'transactions'
* 16. Drop field 'component_id' from 'budget_limits'
* 17. Expand currency table with new fields.
*
* Class ChangesForV321
*/
class ChangesForV321 extends Migration
{
public function down()
{
$this->moveBudgetsBack(); // 1.
$this->moveCategoriesBack(); // 2.
$this->createComponentId(); // 3.
$this->updateComponentInBudgetLimits(); // 4.
$this->createComponentIdForeignKey(); // 5.
$this->dropBudgetIdColumnInBudgetLimits(); // 6.
$createComponents = new CreateComponentTransactionJournalTable; // 7.
$createComponents->up();
$this->moveBackEntriesForBudgetsInJoinedTable(); // 8.
$this->moveBackEntriesForCategoriesInJoinedTable(); // 9.
$this->dropBudgetJournalTable(); // 10.
$this->dropCategoryJournalTable(); // 11.
$this->dropBudgetTable(); // 12.
$this->dropCategoryTable(); // 13.
$this->renameBudgetLimits(); // 14.
$this->renamePiggyBankEvents(); // 15.
$this->renameBudgetLimitToBudgetInRepetitions(); // 16.
// 17, 18, 19
$this->dropFieldsFromCurrencyTable(); // 20.
}
public function moveBudgetsBack()
{
Budget::get()->each(
function (Budget $budget) {
Component::firstOrCreate(
[
'name' => $budget->name,
'user_id' => $budget->user_id,
'class' => 'Budget'
]
);
}
);
}
public function moveCategoriesBack()
{
Category::get()->each(
function (Category $category) {
Component::firstOrCreate(
[
'name' => $category->name,
'user_id' => $category->user_id,
'class' => 'Category'
]
);
}
);
}
public function createComponentId()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->integer('component_id')->unsigned();
}
);
}
public function updateComponentInBudgetLimits()
{
BudgetLimit::get()->each(
function (BudgetLimit $bl) {
$budgetId = $bl->budget_id;
$budget = Budget::find($budgetId);
if ($budget) {
$component = Component::where('class', 'Budget')->where('user_id', $budget->user_id)->where('name', $budget->name)->first();
if ($component) {
$bl->component_id = $component->id;
$bl->save();
}
}
}
);
}
public function createComponentIdForeignKey()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->foreign('component_id', 'limits_component_id_foreign')->references('id')->on('components')->onDelete('cascade');
}
);
}
public function dropBudgetIdColumnInBudgetLimits()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->dropForeign('bid_foreign');
$table->dropColumn('budget_id'); // also drop foreign key!
}
);
}
public function moveBackEntriesForBudgetsInJoinedTable()
{
$set = DB::table('budget_transaction_journal')->get();
foreach ($set as $entry) {
$budget = Budget::find($entry->budget_id);
if ($budget) {
$component = Component::where('class', 'Budget')->where('name', $budget->name)->where('user_id', $budget->user_id)->first();
if ($component) {
DB::table('component_transaction_journal')->insert(
[
'component_id' => $component->id,
'transaction_journal_id' => $entry->transaction_journal_id
]
);
}
}
}
}
public function moveBackEntriesForCategoriesInJoinedTable()
{
$set = DB::table('category_transaction_journal')->get();
foreach ($set as $entry) {
$category = Category::find($entry->category_id);
if ($category) {
$component = Component::where('class', 'Category')->where('name', $category->name)->where('user_id', $category->user_id)->first();
if ($component) {
DB::table('component_transaction_journal')->insert(
[
'component_id' => $component->id,
'transaction_journal_id' => $entry->transaction_journal_id
]
);
}
}
}
}
public function dropBudgetJournalTable()
{
Schema::dropIfExists('budget_transaction_journal');
}
public function dropCategoryJournalTable()
{
Schema::dropIfExists('category_transaction_journal');
}
public function dropBudgetTable()
{
Schema::dropIfExists('budgets');
}
public function dropCategoryTable()
{
Schema::dropIfExists('categories');
}
public function renameBudgetLimits()
{
Schema::rename('budget_limits', 'limits');
}
public function renamePiggyBankEvents()
{
Schema::rename('piggy_bank_events', 'piggybank_events');
}
public function renameBudgetLimitToBudgetInRepetitions()
{
Schema::table(
'limit_repetitions', function (Blueprint $table) {
$table->renameColumn('budget_limit_id', 'limit_id');
}
);
}
public function dropFieldsFromCurrencyTable()
{
Schema::table(
'transaction_currencies', function (Blueprint $table) {
$table->dropColumn('symbol');
$table->dropColumn('name');
}
);
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$this->createBudgetTable(); // 1.
$this->createCategoryTable(); // 2.
$this->createBudgetJournalTable(); // 3
$this->createCategoryJournalTable(); // 4.
$this->moveBudgets(); // 5.
$this->moveCategories(); // 6.
$this->correctNameForBudgetLimits(); // 7.
$this->correctNameForPiggyBankEvents(); // 8.
$this->renameBudgetToBudgetLimitInRepetitions(); // 9.
$this->addBudgetIdFieldToBudgetLimits(); // 10.
$this->moveComponentIdToBudgetId(); // 11.
$this->dropComponentJournalTable(); // 12.
$this->dropComponentRecurringTransactionTable(); // 13.
$this->dropComponentTransactionTable(); // 14.
$this->dropPiggyBankIdFromTransactions(); // 15.
$this->dropComponentIdFromBudgetLimits(); // 16.
$this->expandCurrencyTable(); // 17.
}
public function createBudgetTable()
{
Schema::create(
'budgets', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('name', 50);
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unique(['user_id', 'name']);
}
);
}
public function createCategoryTable()
{
Schema::create(
'categories', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('name', 50);
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unique(['user_id', 'name']);
}
);
}
public function createBudgetJournalTable()
{
Schema::create(
'budget_transaction_journal', function (Blueprint $table) {
$table->increments('id');
$table->integer('budget_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
$table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->unique(['budget_id', 'transaction_journal_id'], 'budid_tjid_unique');
}
);
}
public function createCategoryJournalTable()
{
Schema::create(
'category_transaction_journal', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->unique(['category_id', 'transaction_journal_id'], 'catid_tjid_unique');
}
);
}
public function moveBudgets()
{
Component::where('class', 'Budget')->get()->each(
function (Component $c) {
$entry = [
'user_id' => $c->user_id,
'name' => $c->name
];
$budget = Budget::firstOrCreate($entry);
Log::debug('Migrated budget #' . $budget->id . ': ' . $budget->name);
// create entry in budget_transaction_journal
$connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get();
foreach ($connections as $connection) {
DB::table('budget_transaction_journal')->insert(
[
'budget_id' => $budget->id,
'transaction_journal_id' => $connection->transaction_journal_id
]
);
}
}
);
}
public function moveCategories()
{
Component::where('class', 'Category')->get()->each(
function (Component $c) {
$entry = [
'user_id' => $c->user_id,
'name' => $c->name
];
$category = Category::firstOrCreate($entry);
Log::debug('Migrated category #' . $category->id . ': ' . $category->name);
// create entry in category_transaction_journal
$connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get();
foreach ($connections as $connection) {
DB::table('category_transaction_journal')->insert(
[
'category_id' => $category->id,
'transaction_journal_id' => $connection->transaction_journal_id
]
);
}
}
);
}
public function correctNameForBudgetLimits()
{
Schema::rename('limits', 'budget_limits');
}
public function correctNameForPiggyBankEvents()
{
Schema::rename('piggybank_events', 'piggy_bank_events');
}
public function renameBudgetToBudgetLimitInRepetitions()
{
Schema::table(
'limit_repetitions', function (Blueprint $table) {
$table->renameColumn('limit_id', 'budget_limit_id');
}
);
}
public function addBudgetIdFieldToBudgetLimits()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->integer('budget_id', false, true)->nullable()->after('updated_at');
$table->foreign('budget_id', 'bid_foreign')->references('id')->on('budgets')->onDelete('cascade');
}
);
}
public function moveComponentIdToBudgetId()
{
\Log::debug('Now in moveComponentIdToBudgetId()');
BudgetLimit::get()->each(
function (BudgetLimit $bl) {
\Log::debug('Now at budgetLimit #' . $bl->id . ' with component_id: ' . $bl->component_id);
$component = Component::find($bl->component_id);
if ($component) {
\Log::debug('Found component with id #' . $component->id . ' and name ' . $component->name);
$budget = Budget::whereName($component->name)->whereUserId($component->user_id)->first();
if ($budget) {
\Log::debug('Found a budget with ID #' . $budget->id . ' and name ' . $budget->name);
$bl->budget_id = $budget->id;
$bl->save();
\Log::debug('Connected budgetLimit #' . $bl->id . ' to budget_id' . $budget->id);
} else {
\Log::debug('Could not find a matching budget with name ' . $component->name);
}
} else {
\Log::debug('Could not find a component with id ' . $bl->component_id);
}
}
);
\Log::debug('Done with moveComponentIdToBudgetId()');
}
public function dropComponentJournalTable()
{
Schema::dropIfExists('component_transaction_journal');
}
public function dropComponentRecurringTransactionTable()
{
Schema::dropIfExists('component_recurring_transaction');
}
public function dropComponentTransactionTable()
{
Schema::dropIfExists('component_transaction');
}
public function dropPiggyBankIdFromTransactions()
{
Schema::table(
'transactions', function (Blueprint $table) {
if (Schema::hasColumn('transactions', 'piggybank_id')) {
$table->dropForeign('transactions_piggybank_id_foreign');
$table->dropColumn('piggybank_id');
}
}
);
}
public function dropComponentIdFromBudgetLimits()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->dropForeign('limits_component_id_foreign');
$table->dropColumn('component_id');
}
);
}
public function expandCurrencyTable()
{
Schema::table(
'transaction_currencies', function (Blueprint $table) {
$table->string('name', 48)->nullable();
$table->string('symbol', 8)->nullable();
}
);
\DB::update('UPDATE `transaction_currencies` SET `symbol` = "&#8364;", `name` = "Euro" WHERE `code` = "EUR";');
}
}

View File

@@ -0,0 +1,168 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV322
*/
class ChangesForV322 extends Migration
{
/**
*
*/
public function down()
{
// rename tables:
Schema::rename('piggy_bank_repetitions', 'piggybank_repetitions');
Schema::rename('piggy_banks', 'piggybanks');
// rename fields
Schema::table(
'piggy_bank_events', function (Blueprint $table) {
$table->renameColumn('piggy_bank_id', 'piggybank_id');
}
);
Schema::table(
'piggybank_repetitions', function (Blueprint $table) {
$table->renameColumn('piggy_bank_id', 'piggybank_id');
}
);
// remove soft delete to piggy banks
Schema::table(
'piggybanks', function (Blueprint $table) {
$table->dropSoftDeletes();
}
);
// drop keys from bills (foreign bills_uid_for and unique uid_name_unique)
Schema::table(
'bills', function (Blueprint $table) {
$table->dropForeign('bills_uid_for');
$table->dropUnique('uid_name_unique');
}
);
// drop foreign key from transaction_journals (bill_id_foreign)
Schema::table(
'transaction_journals', function (Blueprint $table) {
$table->dropForeign('bill_id_foreign');
}
);
// drop foreign key from budget_limits:
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->dropForeign('bid_foreign');
$table->dropUnique('unique_bl_combi');
}
);
// rename bills to recurring_transactions
Schema::rename('bills', 'recurring_transactions');
// recreate foreign key recurring_transactions_user_id_foreign in recurring_transactions
// recreate unique recurring_transactions_user_id_name_unique in recurring_transactions
Schema::table(
'recurring_transactions', function (Blueprint $table) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unique(['user_id', 'name']);
}
);
// rename bill_id to recurring_transaction_id
// recreate foreign transaction_journals_recurring_transaction_id_foreign in transaction_journals
Schema::table(
'transaction_journals', function (Blueprint $table) {
$table->renameColumn('bill_id', 'recurring_transaction_id');
$table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('set null');
}
);
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// rename tables:
Schema::rename('piggybank_repetitions', 'piggy_bank_repetitions');
Schema::rename('piggybanks', 'piggy_banks');
// recreate it the correct way:
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->unique(['budget_id', 'startdate', 'repeat_freq'], 'unique_bl_combi');
}
);
// rename fields
Schema::table(
'piggy_bank_events', function (Blueprint $table) {
$table->renameColumn('piggybank_id', 'piggy_bank_id');
}
);
Schema::table(
'piggy_bank_repetitions', function (Blueprint $table) {
$table->renameColumn('piggybank_id', 'piggy_bank_id');
}
);
// add soft delete to piggy banks
Schema::table(
'piggy_banks', function (Blueprint $table) {
$table->softDeletes();
}
);
// rename everything related to recurring transactions, aka bills:
Schema::table(
'transaction_journals', function (Blueprint $table) {
// drop relation
$table->dropForeign('transaction_journals_recurring_transaction_id_foreign');
// rename column
$table->renameColumn('recurring_transaction_id', 'bill_id');
}
);
Schema::table(
'recurring_transactions', function (Blueprint $table) {
$table->dropForeign('recurring_transactions_user_id_foreign');
$table->dropUnique('recurring_transactions_user_id_name_unique');
}
);
// rename table:
Schema::rename('recurring_transactions', 'bills');
// recreate foreign relation:
Schema::table(
'transaction_journals', function (Blueprint $table) {
$table->foreign('bill_id', 'bill_id_foreign')->references('id')->on('bills')->onDelete('set null');
}
);
// recreate more foreign relations.
Schema::table(
'bills', function (Blueprint $table) {
// connect user id to users
$table->foreign('user_id', 'bills_uid_for')->references('id')->on('users')->onDelete('cascade');
// for a user, the name must be unique
$table->unique(['user_id', 'name'], 'uid_name_unique');
}
);
}
}

View File

@@ -38,4 +38,4 @@ class AccountTypeSeeder extends Seeder
}
}
}

View File

@@ -8,19 +8,15 @@ class DefaultUserSeeder extends Seeder
public function run()
{
DB::table('users')->delete();
if (App::environment() == 'testing') {
if (App::environment() == 'testing' || App::environment() == 'homestead') {
User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'acceptance@example.com', 'password' => 'acceptance', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'functional@example.com', 'password' => 'functional', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'reset@example.com', 'password' => 'functional', 'reset' => 'okokokokokokokokokokokokokokokok', 'remember_token' => null]);
User::create(
['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null, 'migrated' => 0]
);
User::create(
['email' => 'acceptance@example.com', 'password' => 'acceptance', 'reset' => null, 'remember_token' => null, 'migrated' => 0]
);
User::create(
['email' => 'functional@example.com', 'password' => 'functional', 'reset' => null, 'remember_token' => null, 'migrated' => 0]
);
}
}
}
}

View File

@@ -2,168 +2,219 @@
use Carbon\Carbon;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* Class TestContentSeeder
*/
class TestContentSeeder extends Seeder
{
/** @var string */
public $eom;
/** @var string */
public $neom;
/** @var string */
public $nsom;
/** @var string */
public $som;
/** @var string */
public $today;
/** @var string */
public $yaeom;
/** @var string */
public $yasom;
/** @var Carbon */
protected $_endOfMonth;
/** @var Carbon */
protected $_nextEndOfMonth;
/** @var Carbon */
protected $_nextStartOfMonth;
/** @var Carbon */
protected $_startOfMonth;
/** @var Carbon */
protected $_today;
/** @var Carbon */
protected $_yearAgoEndOfMonth;
/** @var Carbon */
protected $_yearAgoStartOfMonth;
/**
*
*/
public function __construct()
{
$this->_startOfMonth = Carbon::now()->startOfMonth();
$this->som = $this->_startOfMonth->format('Y-m-d');
$this->_endOfMonth = Carbon::now()->endOfMonth();
$this->eom = $this->_endOfMonth->format('Y-m-d');
$this->_nextStartOfMonth = Carbon::now()->addMonth()->startOfMonth();
$this->nsom = $this->_nextStartOfMonth->format('Y-m-d');
$this->_nextEndOfMonth = Carbon::now()->addMonth()->endOfMonth();
$this->neom = $this->_nextEndOfMonth->format('Y-m-d');
$this->_yearAgoStartOfMonth = Carbon::now()->subYear()->startOfMonth();
$this->yasom = $this->_yearAgoStartOfMonth->format('Y-m-d');
$this->_yearAgoEndOfMonth = Carbon::now()->subYear()->startOfMonth();
$this->yaeom = $this->_yearAgoEndOfMonth->format('Y-m-d');
$this->_today = Carbon::now();
$this->today = $this->_today->format('Y-m-d');
}
/**
* Dates are always this month, the start of this month or earlier.
*/
public function run()
{
if (App::environment() == 'testing') {
$assetType = AccountType::whereType('Asset account')->first();
$expenseType = AccountType::whereType('Expense account')->first();
$revenueType = AccountType::whereType('Revenue account')->first();
$ibType = AccountType::whereType('Initial balance account')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$obType = TransactionType::whereType('Opening balance')->first();
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$transfer = TransactionType::whereType('Transfer')->first();
$deposit = TransactionType::whereType('Deposit')->first();
if (App::environment() == 'testing' || App::environment() == 'homestead') {
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
if ($user) {
// create two asset accounts.
$checking = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Checking account', 'active' => 1]);
$savings = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Savings account', 'active' => 1]);
// create initial accounts and various other stuff:
$this->createAssetAccounts($user);
$this->createBudgets($user);
$this->createCategories($user);
$this->createPiggyBanks($user);
$this->createReminders($user);
$this->createRecurringTransactions($user);
$this->createBills($user);
$this->createExpenseAccounts($user);
$this->createRevenueAccounts($user);
// create two budgets:
$groceriesBudget = Budget::create(['user_id' => $user->id, 'name' => 'Groceries']);
$billsBudget = Budget::create(['user_id' => $user->id, 'name' => 'Bills']);
// create two categories:
$dailyGroceries = Category::create(['user_id' => $user->id, 'name' => 'Daily groceries']);
$lunch = Category::create(['user_id' => $user->id, 'name' => 'Lunch']);
$house = Category::create(['user_id' => $user->id, 'name' => 'House']);
// create some expense accounts.
$ah = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Albert Heijn', 'active' => 1]);
$plus = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'PLUS', 'active' => 1]);
$vitens = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Vitens', 'active' => 1]);
$greenchoice = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Greenchoice', 'active' => 1]);
$portaal = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Portaal', 'active' => 1]);
$store = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Buy More', 'active' => 1]);
// create three revenue accounts.
$employer = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Employer', 'active' => 1]);
$taxes = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'IRS', 'active' => 1]);
$job = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Job', 'active' => 1]);
// put money in the two accounts (initial balance)
$ibChecking = Account::create(
['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Checking account initial balance', 'active' => 0]
);
$ibSavings = Account::create(
['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Savings account initial balance', 'active' => 0]
);
$this->createTransaction($ibChecking, $checking, 4000, $obType, 'Initial Balance for Checking account', '2014-01-01');
$this->createTransaction($ibSavings, $savings, 10000, $obType, 'Initial Balance for Savings account', '2014-01-01');
// get some objects from the database:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$landLord = Account::whereName('Land lord')->orderBy('id', 'DESC')->first();
$utilities = Account::whereName('Utilities company')->orderBy('id', 'DESC')->first();
$television = Account::whereName('TV company')->orderBy('id', 'DESC')->first();
$phone = Account::whereName('Phone agency')->orderBy('id', 'DESC')->first();
$employer = Account::whereName('Employer')->orderBy('id', 'DESC')->first();
// create some expenses and incomes and what-not (for every month):
$start = new Carbon('2014-01-01');
$end = Carbon::now()->startOfMonth()->subDay();
while ($start <= $end) {
$this->createTransaction(
$checking, $portaal, 500, $withdrawal, 'Rent for ' . $start->format('F Y'), $start->format('Y-m-') . '01', $billsBudget, $house
);
$this->createTransaction(
$checking, $vitens, 12, $withdrawal, 'Water for ' . $start->format('F Y'), $start->format('Y-m-') . '02', $billsBudget, $house
);
$this->createTransaction(
$checking, $greenchoice, 110, $withdrawal, 'Power for ' . $start->format('F Y'), $start->format('Y-m-') . '02', $billsBudget, $house
);
$bills = Budget::whereName('Bills')->orderBy('id', 'DESC')->first();
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first();
// spend on groceries
$groceriesStart = clone $start;
for ($i = 0; $i < 13; $i++) {
$amt = rand(100, 300) / 10;
$lunchAmount = rand(30, 60) / 10;
$this->createTransaction(
$checking, $plus, $lunchAmount, $withdrawal, 'Lunch', $groceriesStart->format('Y-m-d'), $groceriesBudget, $lunch
);
$groceriesStart->addDay();
if (intval($groceriesStart->format('d')) % 2 == 0) {
$this->createTransaction(
$checking, $ah, $amt, $withdrawal, 'Groceries', $groceriesStart->format('Y-m-d'), $groceriesBudget, $dailyGroceries
);
}
$groceriesStart->addDay();
}
// get income:
$this->createTransaction($employer, $checking, rand(1400, 1600), $deposit, 'Salary', $start->format('Y-m-') . '23');
// pay taxes:
$this->createTransaction($checking, $taxes, rand(50, 70), $withdrawal, 'Taxes in ' . $start->format('F Y'), $start->format('Y-m-') . '27');
// some other stuff.
$house = Category::whereName('House')->orderBy('id', 'DESC')->first();
$start->addMonth();
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$deposit = TransactionType::whereType('Deposit')->first();
$transfer = TransactionType::whereType('Transfer')->first();
}
$euro = TransactionCurrency::whereCode('EUR')->first();
// create some big expenses, move some money around.
$this->createTransaction($savings, $checking, 1259, $transfer, 'Money for new PC', $end->format('Y-m') . '-11');
$this->createTransaction($checking, $store, 1259, $withdrawal, 'New PC', $end->format('Y-m') . '-12');
$rentBill = Bill::where('name', 'Rent')->first();
// create two budgets
// create two categories
$current = clone $this->_yearAgoStartOfMonth;
while ($current <= $this->_startOfMonth) {
$cur = $current->format('Y-m-d');
$formatted = $current->format('F Y');
// create
// create expenses for rent, utilities, TV, phone on the 1st of the month.
$this->createTransaction($checking, $landLord, 800, $withdrawal, 'Rent for ' . $formatted, $cur, $euro, $bills, $house, $rentBill);
$this->createTransaction($checking, $utilities, 150, $withdrawal, 'Utilities for ' . $formatted, $cur, $euro, $bills, $house);
$this->createTransaction($checking, $television, 50, $withdrawal, 'TV for ' . $formatted, $cur, $euro, $bills, $house);
$this->createTransaction($checking, $phone, 50, $withdrawal, 'Phone bill for ' . $formatted, $cur, $euro, $bills, $house);
// two transactions. One without a budget, one without a category.
$this->createTransaction($checking, $phone, 10, $withdrawal, 'Extra charges on phone bill for ' . $formatted, $cur, $euro, null, $house);
$this->createTransaction($checking, $television, 5, $withdrawal, 'Extra charges on TV bill for ' . $formatted, $cur, $euro, $bills, null);
// income from job:
$this->createTransaction($employer, $checking, rand(3500, 4000), $deposit, 'Salary for ' . $formatted, $cur, $euro);
$this->createTransaction($checking, $savings, 2000, $transfer, 'Salary to savings account in ' . $formatted, $cur, $euro);
$this->createGroceries($current);
$this->createBigExpense(clone $current);
echo 'Created test-content for ' . $current->format('F Y') . "\n";
$current->addMonth();
}
// piggy bank event
// add money to this piggy bank
// create a piggy bank event to match:
$piggyBank = PiggyBank::whereName('New camera')->orderBy('id', 'DESC')->first();
$intoPiggy = $this->createTransaction($checking, $savings, 100, $transfer, 'Money for piggy', $this->yaeom, $euro, $groceries, $house);
PiggyBankEvent::create(
[
'piggy_bank_id' => $piggyBank->id,
'transaction_journal_id' => $intoPiggy->id,
'date' => $this->yaeom,
'amount' => 100
]
);
}
}
/**
* @param Account $from
* @param Account $to
* @param $amount
* @param TransactionType $type
* @param $description
* @param $date
* @param User $user
*/
public function createAssetAccounts(User $user)
{
$assetType = AccountType::whereType('Asset account')->first();
$ibType = AccountType::whereType('Initial balance account')->first();
$obType = TransactionType::whereType('Opening balance')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$acc_a = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Checking account', 'active' => 1]);
$acc_b = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Savings account', 'active' => 1]);
$acc_c = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Delete me', 'active' => 1]);
$acc_d = Account::create(['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Checking account initial balance', 'active' => 0]);
$acc_e = Account::create(['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Savings account initial balance', 'active' => 0]);
$acc_f = Account::create(['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Delete me initial balance', 'active' => 0]);
$this->createTransaction($acc_d, $acc_a, 4000, $obType, 'Initial Balance for Checking account', $this->yasom, $euro);
$this->createTransaction($acc_e, $acc_b, 10000, $obType, 'Initial Balance for Savings account', $this->yasom, $euro);
$this->createTransaction($acc_f, $acc_c, 100, $obType, 'Initial Balance for Delete me', $this->yasom, $euro);
}
/**
* @param Account $from
* @param Account $to
* @param $amount
* @param TransactionType $type
* @param $description
* @param $date
* @param TransactionCurrency $currency
*
* @param Budget $budget
* @param Category $category
* @param Bill $bill
*
* @return TransactionJournal
*/
public function createTransaction(
Account $from, Account $to, $amount, TransactionType $type, $description, $date, Budget $budget = null, Category $category = null
Account $from, Account $to, $amount, TransactionType $type, $description, $date, TransactionCurrency $currency, Budget $budget = null,
Category $category = null, Bill $bill = null
) {
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$billID = is_null($bill) ? null : $bill->id;
/** @var TransactionJournal $journal */
$journal = TransactionJournal::create(
[
'user_id' => $user->id,
'transaction_type_id' => $type->id,
'transaction_currency_id' => $euro->id,
'description' => $description,
'completed' => 1,
'date' => $date
'user_id' => $user->id, 'transaction_type_id' => $type->id, 'transaction_currency_id' => $currency->id, 'bill_id' => $billID,
'description' => $description, 'completed' => 1, 'date' => $date
]
);
Transaction::create(
[
'account_id' => $from->id,
'transaction_journal_id' => $journal->id,
'amount' => $amount * -1
]
Transaction::create(['account_id' => $from->id, 'transaction_journal_id' => $journal->id, 'amount' => $amount * -1]);
Transaction::create(['account_id' => $to->id, 'transaction_journal_id' => $journal->id, 'amount' => $amount]);
);
Transaction::create(
[
'account_id' => $to->id,
'transaction_journal_id' => $journal->id,
'amount' => $amount
]
);
if (!is_null($budget)) {
$journal->budgets()->save($budget);
}
@@ -173,4 +224,338 @@ class TestContentSeeder extends Seeder
return $journal;
}
}
/**
* @param User $user
*/
public function createBudgets(User $user)
{
$groceries = Budget::create(['user_id' => $user->id, 'name' => 'Groceries']);
$bills = Budget::create(['user_id' => $user->id, 'name' => 'Bills']);
$deleteMe = Budget::create(['user_id' => $user->id, 'name' => 'Delete me']);
Budget::create(['user_id' => $user->id, 'name' => 'Budget without repetition']);
$groceriesLimit = BudgetLimit::create(
['startdate' => $this->som, 'amount' => 201, 'repeats' => 0, 'repeat_freq' => 'monthly', 'budget_id' => $groceries->id]
);
$billsLimit = BudgetLimit::create(
['startdate' => $this->som, 'amount' => 202, 'repeats' => 0, 'repeat_freq' => 'monthly', 'budget_id' => $bills->id]
);
$deleteMeLimit = BudgetLimit::create(
['startdate' => $this->som, 'amount' => 203, 'repeats' => 0, 'repeat_freq' => 'monthly', 'budget_id' => $deleteMe->id]
);
// and because we have no filters, some repetitions:
LimitRepetition::create(['budget_limit_id' => $groceriesLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 201]);
LimitRepetition::create(['budget_limit_id' => $billsLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 202]);
LimitRepetition::create(['budget_limit_id' => $deleteMeLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 203]);
}
/**
* @param User $user
*/
public function createCategories(User $user)
{
Category::create(['user_id' => $user->id, 'name' => 'DailyGroceries']);
Category::create(['user_id' => $user->id, 'name' => 'Lunch']);
Category::create(['user_id' => $user->id, 'name' => 'House']);
Category::create(['user_id' => $user->id, 'name' => 'Delete me']);
}
/**
* @param User $user
*/
public function createPiggyBanks(User $user)
{
// account
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
// some dates
$endDate = clone $this->_startOfMonth;
$nextYear = clone $this->_startOfMonth;
$endDate->addMonths(4);
$nextYear->addYear()->subDay();
$next = $nextYear->format('Y-m-d');
$end = $endDate->format('Y-m-d');
// piggy bank
$newCamera = PiggyBank::create(
[
'account_id' => $savings->id,
'name' => 'New camera',
'targetamount' => 2000,
'startdate' => $this->som,
'targetdate' => null,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null,
'reminder_skip' => 0,
'remind_me' => 0,
'order' => 0,
]
);
// and some events!
PiggyBankEvent::create(['piggy_bank_id' => $newCamera->id, 'date' => $this->som, 'amount' => 100]);
PiggyBankRepetition::create(['piggy_bank_id' => $newCamera->id, 'startdate' => $this->som, 'targetdate' => null, 'currentamount' => 100]);
$newClothes = PiggyBank::create(
[
'account_id' => $savings->id,
'name' => 'New clothes',
'targetamount' => 2000,
'startdate' => $this->som,
'targetdate' => $end,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null,
'reminder_skip' => 0,
'remind_me' => 0,
'order' => 0,
]
);
PiggyBankEvent::create(['piggy_bank_id' => $newClothes->id, 'date' => $this->som, 'amount' => 100]);
PiggyBankRepetition::create(['piggy_bank_id' => $newClothes->id, 'startdate' => $this->som, 'targetdate' => $end, 'currentamount' => 100]);
// weekly reminder piggy bank
$weekly = PiggyBank::create(
[
'account_id' => $savings->id,
'name' => 'Weekly reminder for clothes',
'targetamount' => 2000,
'startdate' => $this->som,
'targetdate' => $next,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => 'week',
'reminder_skip' => 0,
'remind_me' => 1,
'order' => 0,
]
);
PiggyBankRepetition::create(['piggy_bank_id' => $weekly->id, 'startdate' => $this->som, 'targetdate' => $next, 'currentamount' => 0]);
}
/**
* @param User $user
*/
public function createReminders(User $user)
{
// for weekly piggy bank (clothes)
$nextWeek = clone $this->_startOfMonth;
$piggyBank = PiggyBank::whereName('New clothes')->orderBy('id', 'DESC')->first();
$nextWeek->addWeek();
$week = $nextWeek->format('Y-m-d');
Reminder::create(
['user_id' => $user->id, 'startdate' => $this->som, 'enddate' => $week, 'active' => 1, 'notnow' => 0,
'remindersable_id' => $piggyBank->id, 'remindersable_type' => 'PiggyBank']
);
// a fake reminder::
Reminder::create(
['user_id' => $user->id, 'startdate' => $this->som, 'enddate' => $week, 'active' => 0, 'notnow' => 0, 'remindersable_id' => 40,
'remindersable_type' => 'Transaction']
);
}
/**
* @param User $user
*/
public function createRecurringTransactions(User $user)
{
// account
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$recurring = PiggyBank::create(
[
'account_id' => $savings->id,
'name' => 'Nieuwe spullen',
'targetamount' => 1000,
'startdate' => $this->som,
'targetdate' => $this->eom,
'repeats' => 1,
'rep_length' => 'month',
'rep_every' => 0,
'rep_times' => 0,
'reminder' => 'month',
'reminder_skip' => 0,
'remind_me' => 1,
'order' => 0,
]
);
PiggyBankRepetition::create(['piggy_bank_id' => $recurring->id, 'startdate' => $this->som, 'targetdate' => $this->eom, 'currentamount' => 0]);
PiggyBankRepetition::create(
['piggy_bank_id' => $recurring->id, 'startdate' => $this->nsom, 'targetdate' => $this->neom, 'currentamount' => 0]
);
Reminder::create(
['user_id' => $user->id, 'startdate' => $this->som, 'enddate' => $this->neom, 'active' => 1, 'notnow' => 0,
'remindersable_id' => $recurring->id, 'remindersable_type' => 'PiggyBank']
);
}
/**
* @param $user
*/
public function createBills($user)
{
// bill
Bill::create(
[
'user_id' => $user->id, 'name' => 'Rent', 'match' => 'rent,landlord',
'amount_min' => 700,
'amount_max' => 900,
'date' => $this->som,
'active' => 1,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
]
);
// bill
Bill::create(
[
'user_id' => $user->id,
'name' => 'Gas licht',
'match' => 'no,match',
'amount_min' => 500,
'amount_max' => 700,
'date' => $this->som,
'active' => 1,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
]
);
// bill
Bill::create(
[
'user_id' => $user->id,
'name' => 'Something something',
'match' => 'mumble,mumble',
'amount_min' => 500,
'amount_max' => 700,
'date' => $this->som,
'active' => 0,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
]
);
}
/**
* @param $user
*/
public function createExpenseAccounts($user)
{
//// create expenses for rent, utilities, water, TV, phone on the 1st of the month.
$expenseType = AccountType::whereType('Expense account')->first();
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Land lord', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Utilities company', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Water company', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'TV company', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Phone agency', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Super savers', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Groceries House', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Lunch House', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Buy More', 'active' => 1]);
}
/**
* @param $user
*/
public function createRevenueAccounts($user)
{
$revenueType = AccountType::whereType('Revenue account')->first();
Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Employer', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'IRS', 'active' => 1]);
Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Second job employer', 'active' => 1]);
}
/**
* @param Carbon $date
*/
public function createGroceries(Carbon $date)
{
// variables we need:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$shopOne = Account::whereName('Groceries House')->orderBy('id', 'DESC')->first();
$shopTwo = Account::whereName('Super savers')->orderBy('id', 'DESC')->first();
$lunchHouse = Account::whereName('Lunch House')->orderBy('id', 'DESC')->first();
$lunch = Category::whereName('Lunch')->orderBy('id', 'DESC')->first();
$daily = Category::whereName('DailyGroceries')->orderBy('id', 'DESC')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first();
$shops = [$shopOne, $shopTwo];
// create groceries and lunch (daily, between 5 and 10 euro).
$mStart = clone $date;
$mEnd = clone $date;
$mEnd->endOfMonth();
while ($mStart <= $mEnd) {
$mFormat = $mStart->format('Y-m-d');
$shop = $shops[rand(0, 1)];
$this->createTransaction($checking, $shop, (rand(500, 1000) / 100), $withdrawal, 'Groceries', $mFormat, $euro, $groceries, $daily);
$this->createTransaction($checking, $lunchHouse, (rand(200, 600) / 100), $withdrawal, 'Lunch', $mFormat, $euro, $groceries, $lunch);
$mStart->addDay();
}
}
/**
* @param $date
*/
public function createBigExpense($date)
{
$date->addDays(12);
$dollar = TransactionCurrency::whereCode('USD')->first();
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$buyMore = Account::whereName('Buy More')->orderBy('id', 'DESC')->first();
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$transfer = TransactionType::whereType('Transfer')->first();
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
// create some big expenses, move some money around.
$amount = rand(500, 2000);
$one = $this->createTransaction(
$savings, $checking, $amount, $transfer, 'Money for big expense in ' . $date->format('F Y'), $date->format('Y-m-d'), $dollar
);
$two = $this->createTransaction(
$checking, $buyMore, $amount, $withdrawal, 'Big expense in ' . $date->format('F Y'), $date->format('Y-m-d'), $dollar
);
$group = TransactionGroup::create(
[
'user_id' => $user->id,
'relation' => 'balance'
]
);
$group->transactionjournals()->save($one);
$group->transactionjournals()->save($two);
}
}

View File

@@ -10,9 +10,9 @@ class TransactionCurrencySeeder extends Seeder
{
DB::table('transaction_currencies')->delete();
TransactionCurrency::create(
['code' => 'EUR']
);
TransactionCurrency::create(['code' => 'EUR','name' => 'Euro','symbol' => '&#8364;']);
TransactionCurrency::create(['code' => 'USD','name' => 'US Dollar','symbol' => '$']);
TransactionCurrency::create(['code' => 'HUF','name' => 'Hungarian forint','symbol' => 'Ft']);
}
}
}

View File

@@ -17,4 +17,4 @@ class TransactionTypeSeeder extends Seeder
TransactionType::create(['type' => 'Opening balance']);
}
}
}

View File

@@ -4,11 +4,15 @@
App::before(
function ($request) {
// put IP in session if not already there.
$reminders = [];
if (Auth::check()) {
Filter::setSessionDateRange();
Reminders::updateReminders();
Steam::removeEmptyBudgetLimits();
$reminders = Reminders::getReminders();
}
View::share('reminders', $reminders);
@@ -90,3 +94,11 @@ Route::filter(
}
}
);
Route::filter(
'allow-register', function () {
if (Config::get('auth.allow_register') !== true) {
return View::make('error')->with('message', 'Not possible');
}
}
);

View File

@@ -1,121 +0,0 @@
<?php
namespace Firefly\Database;
use LaravelBook\Ardent\Ardent;
/**
* Class SingleTableInheritanceEntity
*
* @package Firefly\Database
*/
abstract class SingleTableInheritanceEntity extends Ardent
{
/**
* The field that stores the subclass
*
* @var string
*/
protected $subclassField = null;
/**
* must be overridden and set to true in subclasses
*
* @var bool
*/
protected $isSubclass = false;
/**
* @param array $attributes
*
* @return \Illuminate\Database\Eloquent\Model|static
*/
public function newFromBuilder($attributes = [])
{
$instance = $this->mapData((array)$attributes)->newInstance([], true);
$instance->setRawAttributes((array)$attributes, true);
return $instance;
}
/**
* if no subclass is defined, function as normal
*
* @param array $attributes
*
* @return \Illuminate\Database\Eloquent\Model|static
*/
public function mapData(array $attributes)
{
if (!$this->subclassField) {
return $this->newInstance();
}
return new $attributes[$this->subclassField];
}
/**
*
* instead of using $this->newInstance(), call
* newInstance() on the object from mapData
*
* @param bool $excludeDeleted
*
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function newQuery($excludeDeleted = true)
{
// If using Laravel 4.0.x then use the following commented version of this command
// $builder = new Builder($this->newBaseQueryBuilder());
// newEloquentBuilder() was added in 4.1
$builder = $this->newEloquentBuilder($this->newBaseQueryBuilder());
// Once Firefly has the query builders, it will set the model instances so the
// builder can easily access any information it may need from the model
// while it is constructing and executing various queries against it.
$builder->setModel($this)->with($this->with);
if ($excludeDeleted && $this->softDelete) {
$builder->whereNull($this->getQualifiedDeletedAtColumn());
}
if ($this->subclassField && $this->isSubclass()) {
$builder->where($this->subclassField, '=', get_class($this));
}
return $builder;
}
/**
* @return bool
*/
public function isSubclass()
{
return $this->isSubclass;
}
/**
* ensure that the subclass field is assigned on save
*
* @param array $rules
* @param array $customMessages
* @param array $options
* @param callable $beforeSave
* @param callable $afterSave
*
* @return bool
*/
public function save(
array $rules = [],
array $customMessages = [],
array $options = [],
\Closure $beforeSave = null,
\Closure $afterSave = null
) {
if ($this->subclassField) {
$this->attributes[$this->subclassField] = get_class($this);
}
return parent::save($rules, $customMessages, $options, $beforeSave, $afterSave);
}
}

View File

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

View File

@@ -1,11 +0,0 @@
<?php
namespace Firefly\Exception;
/**
* Class ValidationException
*
* @package Firefly\Exception
*/
class ValidationException extends \Exception {
}

View File

@@ -1,365 +0,0 @@
<?php
namespace Firefly\Form;
use Firefly\Exception\FireflyException;
use Illuminate\Support\MessageBag;
class Form
{
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffInteger($name, $value = null, array $options = [])
{
$options['step'] = '1';
return self::ffInput('number', $name, $value, $options);
}
public static function ffCheckbox($name, $value = 1, $checked = null, $options = [])
{
$options['checked'] = $checked ? true : null;
return self::ffInput('checkbox', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffAmount($name, $value = null, array $options = [])
{
$options['step'] = 'any';
$options['min'] = '0.01';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffBalance($name, $value = null, array $options = [])
{
$options['step'] = 'any';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffDate($name, $value = null, array $options = [])
{
return self::ffInput('date', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffTags($name, $value = null, array $options = [])
{
$options['data-role'] = 'tagsinput';
return self::ffInput('text', $name, $value, $options);
}
/**
* @param $name
* @param array $list
* @param null $selected
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffSelect($name, array $list = [], $selected = null, array $options = [])
{
return self::ffInput('select', $name, $selected, $options, $list);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffText($name, $value = null, array $options = array())
{
return self::ffInput('text', $name, $value, $options);
}
/**
* @param $name
* @param $options
*
* @return string
*/
public static function label($name, $options)
{
if (isset($options['label'])) {
return $options['label'];
}
$labels = [
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account'
];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
}
/**
* Return buttons for update/validate/return.
*
* @param $type
* @param $name
*/
public static function ffOptionsList($type, $name)
{
$previousValue = \Input::old('post_submit_action');
$previousValue = is_null($previousValue) ? 'store' : $previousValue;
/*
* Store.
*/
$store = '';
switch ($type) {
case 'create':
$store = '<div class="form-group"><label for="default" class="col-sm-4 control-label">Store</label>';
$store .= '<div class="col-sm-8"><div class="radio"><label>';
$store .= \Form::radio('post_submit_action', 'store', $previousValue == 'store');
$store .= 'Store ' . $name . '</label></div></div></div>';
break;
case 'update':
$store = '<div class="form-group"><label for="default" class="col-sm-4 control-label">Store</label>';
$store .= '<div class="col-sm-8"><div class="radio"><label>';
$store .= \Form::radio('post_submit_action', 'update', $previousValue == 'store');
$store .= 'Update ' . $name . '</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store) ' . $type);
break;
}
/*
* validate is always the same:
*/
$validate = '<div class="form-group"><label for="validate_only" class="col-sm-4 control-label">Validate only';
$validate .= '</label><div class="col-sm-8"><div class="radio"><label>';
$validate .= \Form::radio('post_submit_action', 'validate_only', $previousValue == 'validate_only');
$validate .= 'Only validate, do not save</label></div></div></div>';
/*
* Store & return:
*/
switch ($type) {
case 'create':
$return = '<div class="form-group"><label for="return_to_form" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action', 'create_another', $previousValue == 'create_another');
$return .= 'After storing, return here to create another one.</label></div></div></div>';
break;
case 'update':
$return = '<div class="form-group"><label for="return_to_edit" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action', 'return_to_edit', $previousValue == 'return_to_edit');
$return .= 'After updating, return here.</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store+return) ' . $type);
break;
}
return $store . $validate . $return;
}
/**
* @param $type
* @param $name
* @param null $value
* @param array $options
* @param array $list
*
* @return string
* @throws FireflyException
*/
public static function ffInput($type, $name, $value = null, array $options = array(), $list = [])
{
/*
* add some defaults to this method:
*/
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['autocomplete'] = 'off';
$label = self::label($name, $options);
/*
* Make label and placeholder look nice.
*/
$options['placeholder'] = ucfirst($name);
/*
* Get pre filled value:
*/
if (\Session::has('prefilled')) {
$preFilled = \Session::get('preFilled');
$value = isset($prefilled[$name]) && is_null($value) ? $prefilled[$name] : $value;
}
/*
* Get the value.
*/
if (!is_null(\Input::old($name))) {
/*
* Old value overrules $value.
*/
$value = \Input::old($name);
}
/*
* Get errors, warnings and successes from session:
*/
/** @var MessageBag $errors */
$errors = \Session::get('errors');
/** @var MessageBag $warnings */
$warnings = \Session::get('warnings');
/** @var MessageBag $successes */
$successes = \Session::get('successes');
/*
* If errors, add some more classes.
*/
switch (true) {
case (!is_null($errors) && $errors->has($name)):
$classes = 'form-group has-error has-feedback';
break;
case (!is_null($warnings) && $warnings->has($name)):
$classes = 'form-group has-warning has-feedback';
break;
case (!is_null($successes) && $successes->has($name)):
$classes = 'form-group has-success has-feedback';
break;
default:
$classes = 'form-group';
break;
}
/*
* Add some HTML.
*/
$html = '<div class="' . $classes . '">';
$html .= '<label for="' . $options['id'] . '" class="col-sm-4 control-label">' . $label . '</label>';
$html .= '<div class="col-sm-8">';
/*
* Switch input type:
*/
unset($options['label']);
switch ($type) {
case 'text':
$html .= \Form::input('text', $name, $value, $options);
break;
case 'amount':
$html .= '<div class="input-group"><div class="input-group-addon">&euro;</div>';
$html .= \Form::input('number', $name, $value, $options);
$html .= '</div>';
break;
case 'number':
$html .= \Form::input('number', $name, $value, $options);
break;
case 'checkbox':
$checked = $options['checked'];
unset($options['checked'], $options['placeholder'], $options['autocomplete'], $options['class']);
$html .= '<div class="checkbox"><label>';
$html .= \Form::checkbox($name, $value, $checked, $options);
$html .= '</label></div>';
break;
case 'date':
$html .= \Form::input('date', $name, $value, $options);
break;
case 'select':
$html .= \Form::select($name, $list, $value, $options);
break;
default:
throw new FireflyException('Cannot handle type "' . $type . '" in FFFormBuilder.');
break;
}
/*
* If errors, respond to them:
*/
if (!is_null($errors)) {
if ($errors->has($name)) {
$html .= '<span class="glyphicon glyphicon-remove form-control-feedback"></span>';
$html .= '<p class="text-danger">' . e($errors->first($name)) . '</p>';
}
}
unset($errors);
/*
* If warnings, respond to them:
*/
if (!is_null($warnings)) {
if ($warnings->has($name)) {
$html .= '<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>';
$html .= '<p class="text-warning">' . e($warnings->first($name)) . '</p>';
}
}
unset($warnings);
/*
* If successes, respond to them:
*/
if (!is_null($successes)) {
if ($successes->has($name)) {
$html .= '<span class="glyphicon glyphicon-ok form-control-feedback"></span>';
$html .= '<p class="text-success">' . e($successes->first($name)) . '</p>';
}
}
unset($successes);
$html .= '</div>';
$html .= '</div>';
return $html;
}
}

View File

@@ -1,154 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
/**
* Class Account
*
* @package Firefly\Helper\Controllers
*/
class Account implements AccountInterface
{
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
return \TransactionJournal::withRelevantData()
->accountIs($account)
->leftJoin(
'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
)
->where('transaction_types.type', 'Opening balance')
->first(['transaction_journals.*']);
}
/**
* Since it is entirely possible the database is messed up somehow it might be that a transaction
* journal has only one transaction. This is mainly caused by wrong deletions and other artefacts from the past.
*
* If it is the case, Firefly removes $item and continues like nothing ever happened. This will however,
* mess up some statisics but it's decided everybody should learn to live with that.
*
* Firefly might be needing some cleanup routine in the future.
*
* For now, Firefly simply warns the user of this.
*
* @param \Account $account
* @param $perPage
*
* @return array|mixed
* @throws \Firefly\Exception\FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function show(\Account $account, $perPage)
{
$start = \Session::get('start');
$end = \Session::get('end');
$stats = [
'accounts' => []
];
$items = [];
// build a query:
$query = \TransactionJournal::withRelevantData()
->defaultSorting()
->accountIs($account)
->after($start)
->before($end);
// filter some:
switch (\Input::get('type')) {
case 'transactions':
$query->transactionTypes(['Deposit', 'Withdrawal']);
break;
case 'transfers':
$query->transactionTypes(['Transfer']);
break;
}
switch (\Input::get('show')) {
case 'expenses':
case 'out':
$query->lessThan(0);
break;
case 'income':
case 'in':
$query->moreThan(0);
break;
}
// build paginator:
$totalItems = $query->count();
$page = max(1, intval(\Input::get('page')));
$skip = ($page - 1) * $perPage;
$result = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']);
// get the relevant budgets, categories and accounts from this list:
/** @var $item \TransactionJournal */
foreach ($result as $index => $item) {
foreach ($item->components as $component) {
$stats[$component->class][$component->id] = $component;
}
if (count($item->transactions) < 2) {
\Session::flash('warning', 'Some transactions are incomplete; they will not be shown.');
unset($result[$index]);
continue;
}
$items[] = $item;
$fromAccount = $item->transactions[0]->account;
$toAccount = $item->transactions[1]->account;
$stats['accounts'][$fromAccount->id] = $fromAccount;
$stats['accounts'][$toAccount->id] = $toAccount;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
unset($result, $page, $item, $fromAccount, $toAccount);
// statistics (transactions)
$trIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trDiff = $trIn + $trOut;
// statistics (transfers)
$trfIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfDiff = $trfIn + $trfOut;
$stats['period'] = [
'in' => $trIn,
'out' => $trOut,
'diff' => $trDiff,
't_in' => $trfIn,
't_out' => $trfOut,
't_diff' => $trfDiff
];
$return = [
'journals' => $paginator,
'statistics' => $stats
];
return $return;
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection;
/**
* Interface AccountInterface
*
* @package Firefly\Helper\Controllers
*/
interface AccountInterface
{
/**
* @param \Account $account
*
* @return mixed
*/
public function openingBalanceTransaction(\Account $account);
/**
* @param \Account $account
* @param $perPage
*
* @return mixed
*/
public function show(\Account $account, $perPage);
}

View File

@@ -1,227 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection;
/**
* Class Budget
*
* @package Firefly\Helper\Controllers
*/
class Budget implements BudgetInterface
{
/**
* First, loop all budgets, all of their limits and all repetitions to get an overview per period
* and some basic information about that repetition's data.
*
*
*
* @param Collection $budgets
*
* @return mixed|void
*/
public function organizeByDate(Collection $budgets)
{
$return = [];
/** @var \Budget $budget */
foreach ($budgets as $budget) {
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $repetition */
foreach ($limit->limitrepetitions as $repetition) {
$repetition->left = $repetition->leftInRepetition();
$periodOrder = $repetition->periodOrder();
$period = $repetition->periodShow();
if (!isset($return[$periodOrder])) {
$return[$periodOrder] = [
'date' => $period,
'start' => $repetition->startdate,
'end' => $repetition->enddate,
'budget_id' => $budget->id,
'limitrepetitions' => [$repetition]
];
} else {
$return[$periodOrder]['limitrepetitions'][] = $repetition;
}
}
}
}
krsort($return);
return $return;
}
/**
* Get a repetition (complex because of user check)
* and then get the transactions in it.
* @param $repetitionId
*
* @return array
*/
public function organizeRepetition(\LimitRepetition $repetition)
{
$result = [];
// get transactions:
$set = $repetition->limit->budget
->transactionjournals()
->withRelevantData()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.*']);
$result[0] = [
'date' => $repetition->periodShow(),
'limit' => $repetition->limit,
'limitrepetition' => $repetition,
'journals' => $set,
'paginated' => false
];
return $result;
}
/**
*
*
* @param \Budget $budget
* @param bool $useSessionDates
*
* @return array|mixed
* @throws \Firefly\Exception\FireflyException
*/
public function organizeRepetitions(\Budget $budget, $useSessionDates = false)
{
$sessionStart = \Session::get('start');
$sessionEnd = \Session::get('end');
$result = [];
$inRepetition = [];
// get the limits:
if ($useSessionDates) {
$limits = $budget->limits()->where('startdate', '>=', $sessionStart->format('Y-m-d'))->where(
'startdate', '<=', $sessionEnd->format('Y-m-d')
)->get();
} else {
$limits = $budget->limits;
}
/** @var \Limit $limit */
foreach ($limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$order = $repetition->periodOrder();
$result[$order] = [
'date' => $repetition->periodShow(),
'limitrepetition' => $repetition,
'limit' => $limit,
'journals' => [],
'paginated' => false
];
$transactions = [];
$set = $budget->transactionjournals()
->withRelevantData()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.*']);
foreach ($set as $entry) {
$transactions[] = $entry;
$inRepetition[] = $entry->id;
}
$result[$order]['journals'] = $transactions;
}
}
if ($useSessionDates === false) {
$query = $budget->transactionjournals()->withRelevantData()->defaultSorting();
if (count($inRepetition) > 0) {
$query->whereNotIn('transaction_journals.id', $inRepetition);
}
// build paginator:
$perPage = 25;
$totalItems = $query->count();
$page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1;
$skip = ($page - 1) * $perPage;
$set = $query->skip($skip)->take($perPage)->get();
// stupid paginator!
$items = [];
/** @var $item \TransactionJournal */
foreach ($set as $item) {
$items[] = $item;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
$result['0000'] = [
'date' => 'Not in an envelope',
'limit' => null,
'paginated' => true,
'journals' => $paginator];
}
krsort($result);
return $result;
}
/**
* @param \Budget $budget
*
* @return mixed|void
*/
public function outsideRepetitions(\Budget $budget)
{
$inRepetitions = [];
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$set = $budget->transactionjournals()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.id']);
foreach ($set as $item) {
$inRepetitions[] = $item->id;
}
}
}
$query = $budget->transactionjournals()
->withRelevantData()
->whereNotIn('transaction_journals.id', $inRepetitions)
->defaultSorting();
// build paginator:
$perPage = 25;
$totalItems = $query->count();
$page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1;
$skip = ($page - 1) * $perPage;
$set = $query->skip($skip)->take($perPage)->get();
// stupid paginator!
$items = [];
/** @var $item \TransactionJournal */
foreach ($set as $item) {
$items[] = $item;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
$result = [0 => [
'date' => 'Not in an envelope',
'limit' => null,
'paginated' => true,
'journals' => $paginator
]];
return $result;
}
}

View File

@@ -1,44 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection;
/**
* Interface BudgetInterface
*
* @package Firefly\Helper\Controllers
*/
interface BudgetInterface
{
/**
* @param Collection $budgets
*
* @return mixed
*/
public function organizeByDate(Collection $budgets);
/**
* @param $repetitionId
*
* @return mixed
*/
public function organizeRepetition(\LimitRepetition $repetition);
/**
* @param \Budget $budget
* @param bool $useSessionDates
*
* @return mixed
*/
public function organizeRepetitions(\Budget $budget, $useSessionDates = false);
/**
* @param \Budget $budget
*
* @return mixed
*/
public function outsideRepetitions(\Budget $budget);
}

View File

@@ -1,29 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
/**
* Class Category
*
* @package Firefly\Helper\Controllers
*/
class Category implements CategoryInterface
{
/**
* @param \Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function journalsInRange(\Category $category, Carbon $start, Carbon $end)
{
return $category->transactionjournals()->with(
['transactions', 'transactions.account', 'transactiontype', 'components']
)->orderBy('date', 'DESC')->orderBy('id', 'DESC')->before($end)->after($start)->get();
}
}

View File

@@ -1,25 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
/**
* Interface CategoryInterface
*
* @package Firefly\Helper\Controllers
*/
interface CategoryInterface
{
/**
* @param \Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function journalsInRange(\Category $category, Carbon $start, Carbon $end);
}

View File

@@ -1,475 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Support\Collection;
/**
* Class Chart
*
* @package Firefly\Helper\Controllers
*/
class Chart implements ChartInterface
{
/**
* @param \Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function account(\Account $account, Carbon $start, Carbon $end)
{
$current = clone $start;
$today = new Carbon;
$return = [
'name' => $account->name,
'id' => $account->id,
'type' => 'spline',
'pointStart' => $start->timestamp * 1000,
'pointInterval' => 24 * 3600 * 1000, // one day
'data' => []
];
while ($current <= $end) {
if ($current > $today) {
$return['data'][] = $account->predict(clone $current);
} else {
$return['data'][] = $account->balance(clone $current);
}
$current->addDay();
}
return $return;
}
/**
* @param \Account $account
* @param Carbon $date
*
* @return array
*/
public function accountDailySummary(\Account $account, Carbon $date)
{
$result = [
'rows' => [],
'sum' => 0
];
if ($account) {
// get journals in range:
$journals = \Auth::user()->transactionjournals()->with(
[
'transactions',
'transactions.account',
'transactioncurrency',
'transactiontype'
]
)
->distinct()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->where('transactions.account_id', $account->id)
->where('transaction_journals.date', $date->format('Y-m-d'))
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
// loop all journals:
foreach ($journals as $journal) {
foreach ($journal->transactions as $transaction) {
$name = $transaction->account->name;
if ($transaction->account->id != $account->id) {
if (!isset($result['rows'][$name])) {
$result['rows'][$name] = [
'name' => $name,
'id' => $transaction->account->id,
'amount' => floatval($transaction->amount)
];
} else {
$result['rows'][$name]['amount'] += floatval($transaction->amount);
}
$result['sum'] += floatval($transaction->amount);
}
}
}
}
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws \Firefly\Exception\FireflyException
*/
public function categories(Carbon $start, Carbon $end)
{
$result = [];
// grab all transaction journals in this period:
$journals = \TransactionJournal::
with(
['components', 'transactions' => function ($q) {
$q->where('amount', '>', 0);
}]
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', 'Withdrawal')
->after($start)->before($end)
->where('completed', 1)
->get(['transaction_journals.*']);
foreach ($journals as $journal) {
// has to be one:
if (!isset($journal->transactions[0])) {
throw new FireflyException('Journal #' . $journal->id . ' has ' . count($journal->transactions)
. ' transactions!');
}
$transaction = $journal->transactions[0];
$amount = floatval($transaction->amount);
// get budget from journal:
$category = $journal->categories()->first();
$categoryName = is_null($category) ? '(no category)' : $category->name;
$result[$categoryName] = isset($result[$categoryName]) ? $result[$categoryName] + floatval($amount)
: $amount;
}
unset($journal, $transaction, $category, $amount);
// sort
arsort($result);
$chartData = [];
foreach ($result as $name => $value) {
$chartData[] = [$name, $value];
}
return $chartData;
}
/**
* @param \Category $category
* @param $range
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws \Firefly\Exception\FireflyException
*/
public function categoryShowChart(\Category $category, $range, Carbon $start, Carbon $end)
{
$data = ['name' => $category->name . ' per ' . $range, 'data' => []];
// go back twelve periods. Skip if empty.
$beginning = clone $start;
switch ($range) {
default:
throw new FireflyException('No beginning for range ' . $range);
break;
case '1D':
$beginning->subDays(12);
break;
case '1W':
$beginning->subWeeks(12);
break;
case '1M':
$beginning->subYear();
break;
case '3M':
$beginning->subYears(3);
break;
case '6M':
$beginning->subYears(6);
break;
case 'custom':
$diff = $start->diff($end);
$days = $diff->days;
$beginning->subDays(12 * $days);
break;
}
// loop over the periods:
while ($beginning <= $start) {
// increment currentEnd to fit beginning:
$currentEnd = clone $beginning;
// increase beginning for next round:
switch ($range) {
default:
throw new FireflyException('No currentEnd incremental for range ' . $range);
break;
case '1D':
break;
case '1W':
$currentEnd->addWeek()->subDay();
break;
case '1M':
$currentEnd->addMonth()->subDay();
break;
case '3M':
$currentEnd->addMonths(3)->subDay();
break;
case '6M':
$currentEnd->addMonths(6)->subDay();
break;
case 'custom':
$diff = $start->diff($end);
$days = $diff->days;
$days = $days == 1 ? 2 : $days;
$currentEnd->addDays($days)->subDay();
break;
}
// now format the current range:
$title = '';
switch ($range) {
default:
throw new FireflyException('No date formats for frequency "' . $range . '"!');
break;
case '1D':
$title = $beginning->format('j F Y');
break;
case '1W':
$title = $beginning->format('\W\e\e\k W, Y');
break;
case '1M':
$title = $beginning->format('F Y');
break;
case '3M':
case '6M':
$title = $beginning->format('M Y') . ' - ' . $currentEnd->format('M Y');
break;
case 'custom':
$title = $beginning->format('d-m-Y') . ' - ' . $currentEnd->format('d-m-Y');
break;
case 'yearly':
// return $this->startdate->format('Y');
break;
}
// get sum for current range:
$journals = \TransactionJournal::
with(
['transactions' => function ($q) {
$q->where('amount', '>', 0);
}]
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', 'Withdrawal')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('components.id', '=', $category->id)
//->leftJoin()
->after($beginning)->before($currentEnd)
->where('completed', 1)
->get(['transaction_journals.*']);
$currentSum = 0;
foreach ($journals as $journal) {
if (!isset($journal->transactions[0])) {
throw new FireflyException('Journal #' . $journal->id . ' has ' . count($journal->transactions)
. ' transactions!');
}
$transaction = $journal->transactions[0];
$amount = floatval($transaction->amount);
$currentSum += $amount;
}
$data['data'][] = [$title, $currentSum];
// increase beginning for next round:
switch ($range) {
default:
throw new FireflyException('No incremental for range ' . $range);
break;
case '1D':
$beginning->addDay();
break;
case '1W':
$beginning->addWeek();
break;
case '1M':
$beginning->addMonth();
break;
case '3M':
$beginning->addMonths(3);
break;
case '6M':
$beginning->addMonths(6);
break;
case 'custom':
$diff = $start->diff($end);
$days = $diff->days;
$beginning->addDays($days);
break;
}
}
return $data;
}
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return float|null
*/
public function spentOnDay(\Budget $budget, Carbon $date)
{
return floatval(
\Transaction::
leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)->where('component_transaction_journal.component_id', '=', $budget->id)->where(
'transaction_journals.date', $date->format('Y-m-d')
)->where('amount', '>', 0)->sum('amount')
);
}
/**
* @param \Budget $budget
*
* @return int[]
*/
public function allJournalsInBudgetEnvelope(\Budget $budget)
{
$inRepetitions = [];
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$set = $budget
->transactionjournals()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->get(['transaction_journals.id']);
foreach ($set as $item) {
$inRepetitions[] = $item->id;
}
}
}
return $inRepetitions;
}
/**
* @param \Budget $budget
* @param array $ids
*
* @return mixed|void
*/
public function journalsNotInSet(\Budget $budget, array $ids)
{
$query = $budget->transactionjournals()
->whereNotIn('transaction_journals.id', $ids)
->orderBy('date', 'DESC')
->orderBy('transaction_journals.id', 'DESC');
$result = $query->get(['transaction_journals.id']);
$set = [];
foreach ($result as $entry) {
$set[] = $entry->id;
}
return $set;
}
/**
* @param array $set
*
* @return mixed
*/
public function transactionsByJournals(array $set)
{
$transactions = \Transaction::whereIn('transaction_journal_id', $set)
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->groupBy('transaction_journals.date')
->where('amount', '>', 0)->get(['transaction_journals.date', \DB::Raw('SUM(`amount`) as `aggregate`')]);
return $transactions;
}
/**
* Get all limit (LimitRepetitions) for a budget falling in a certain date range.
*
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end)
{
$reps = new Collection;
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
$set = $limit->limitrepetitions()->where(
function ($q) use ($start, $end) {
// startdate is between range
$q->where(
function ($q) use ($start, $end) {
$q->where('startdate', '>=', $start->format('Y-m-d'));
$q->where('startdate', '<=', $end->format('Y-m-d'));
}
);
// or enddate is between range.
$q->orWhere(
function ($q) use ($start, $end) {
$q->where('enddate', '>=', $start->format('Y-m-d'));
$q->where('enddate', '<=', $end->format('Y-m-d'));
}
);
}
)->get();
$reps = $reps->merge($set);
}
return $reps;
}
/**
* Firefly checks how much money has been spend on the limitrepetition (aka: the current envelope) in
* the period denoted. Aka, the user has a certain amount of money in an envelope and wishes to know how
* much he has spent between the dates entered. This date range can be a partial match with the date range
* of the envelope or no match at all.
*
* @param \LimitRepetition $repetition
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end)
{
return floatval(
\Transaction::
leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)->where('component_transaction_journal.component_id', '=', $repetition->limit->budget->id)->where(
'transaction_journals.date', '>=', $start->format('Y-m-d')
)->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->where(
'amount', '>', 0
)->sum('amount')
);
}
}

View File

@@ -1,108 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
/**
* Interface ChartInterface
*
* @package Firefly\Helper\Controllers
*/
interface ChartInterface
{
/**
* @param \Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function account(\Account $account, Carbon $start, Carbon $end);
/**
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function categories(Carbon $start, Carbon $end);
/**
* @param \Account $account
* @param Carbon $date
*
* @return mixed
*/
public function accountDailySummary(\Account $account, Carbon $date);
/**
* @param \Category $category
* @param $range
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function categoryShowChart(\Category $category, $range, Carbon $start, Carbon $end);
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return float|null
*/
public function spentOnDay(\Budget $budget, Carbon $date);
/**
* @param \Budget $budget
*
* @return int[]
*/
public function allJournalsInBudgetEnvelope(\Budget $budget);
/**
* @param \Budget $budget
* @param array $ids
*
* @return mixed
*/
public function journalsNotInSet(\Budget $budget, array $ids);
/**
* @param array $set
*
* @return mixed
*/
public function transactionsByJournals(array $set);
/**
* Get all limit (LimitRepetitions) for a budget falling in a certain date range.
*
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end);
/**
* Firefly checks how much money has been spend on the limitrepetition (aka: the current envelope) in
* the period denoted. Aka, the user has a certain amount of money in an envelope and wishes to know how
* much he has spent between the dates entered. This date range can be a partial match with the date range
* of the envelope or no match at all.
*
* @param \LimitRepetition $repetition
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end);
}

View File

@@ -1,396 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
/**
* Class Json
*
* @package Firefly\Helper\Controllers
*/
class Json implements JsonInterface
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters()
{
/*
* Process all parameters!
*/
if (intval(\Input::get('length')) < 0) {
$length = 10000; // we get them all if no length is defined.
} else {
$length = intval(\Input::get('length'));
}
$parameters = [
'start' => intval(\Input::get('start')),
'length' => $length,
'draw' => intval(\Input::get('draw')),
];
/*
* Columns:
*/
if (!is_null(\Input::get('columns')) && is_array(\Input::get('columns'))) {
foreach (\Input::get('columns') as $column) {
$parameters['columns'][] = [
'data' => $column['data'],
'name' => $column['name'],
'searchable' => $column['searchable'] == 'true' ? true : false,
'orderable' => $column['orderable'] == 'true' ? true : false,
'search' => [
'value' => $column['search']['value'],
'regex' => $column['search']['regex'] == 'true' ? true : false,
]
];
}
}
/*
* Sorting.
*/
$parameters['orderOnAccount'] = false;
if (!is_null(\Input::get('order')) && is_array(\Input::get('order'))) {
foreach (\Input::get('order') as $order) {
$columnIndex = intval($order['column']);
$columnName = $parameters['columns'][$columnIndex]['name'];
$parameters['order'][] = [
'name' => $columnName,
'dir' => strtoupper($order['dir'])
];
if ($columnName == 'to' || $columnName == 'from') {
$parameters['orderOnAccount'] = true;
}
}
}
/*
* Search parameters:
*/
$parameters['search'] = [
'value' => '',
'regex' => false
];
if (!is_null(\Input::get('search')) && is_array(\Input::get('search'))) {
$search = \Input::get('search');
$parameters['search'] = [
'value' => $search['value'],
'regex' => $search['regex'] == 'true' ? true : false
];
}
return $parameters;
}
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function journalDataset(array $parameters, Builder $query)
{
/*
* Count query:
*/
$count = $query->count();
/*
* Update the selection:
*/
$query->take($parameters['length']);
if ($parameters['start'] > 0) {
$query->skip($parameters['start']);
}
/*
* Input search parameters:
*/
$filtered = $count;
if (strlen($parameters['search']['value']) > 0) {
$query->where('transaction_journals.description', 'LIKE', '%' . e($parameters['search']['value']) . '%');
$filtered = $query->count();
}
/*
* Build return array:
*/
$data = [
'draw' => $parameters['draw'],
'recordsTotal' => $count,
'recordsFiltered' => $filtered,
'data' => [],
];
/*
* Get paginated result set:
*/
if ($parameters['orderOnAccount'] === true) {
/** @var Collection $set */
$set = $query->get(
[
'transaction_journals.*',
't1.amount',
't1.account_id AS from_id',
'a1.name AS from',
't2.account_id AS to_id',
'a2.name AS to',
]
);
} else {
/** @var Collection $set */
$set = $query->get(
[
'transaction_journals.*',
'transactions.amount',
]
);
}
/*
* Loop set and create entries to return.
*/
/** @var \TransactionJournal $entry */
foreach ($set as $entry) {
$from = $entry->transactions[0]->account;
$to = $entry->transactions[1]->account;
$budget = $entry->budgets()->first();
$category = $entry->categories()->first();
$recurring = $entry->recurringTransaction()->first();
$arr = [
'date' => $entry->date->format('j F Y'),
'description' => [
'description' => $entry->description,
'url' => route('transactions.show', $entry->id)
],
'amount' => floatval($entry->amount),
'from' => ['name' => $from->name, 'url' => route('accounts.show', $from->id)],
'to' => ['name' => $to->name, 'url' => route('accounts.show', $to->id)],
'components' => [
'budget_id' => 0,
'budget_url' => '',
'budget_name' => '',
'category_id' => 0,
'category_url' => '',
'category_name' => ''
],
'id' => [
'edit' => route('transactions.edit', $entry->id),
'delete' => route('transactions.delete', $entry->id)
]
];
if ($budget) {
$arr['components']['budget_id'] = $budget->id;
$arr['components']['budget_name'] = $budget->name;
$arr['components']['budget_url'] = route('budgets.show', $budget->id);
}
if ($category) {
$arr['components']['category_id'] = $category->id;
$arr['components']['category_name'] = $category->name;
$arr['components']['category_url'] = route('categories.show', $category->id);
}
if ($recurring) {
$arr['components']['recurring_id'] = $recurring->id;
$arr['components']['recurring_name'] = e($recurring->name);
$arr['components']['recurring_url'] = route('recurring.show', $recurring->id);
}
$data['data'][] = $arr;
}
return $data;
}
/**
* Builds most of the query required to grab transaction journals from the database.
* This is useful because all three pages showing different kinds of transactions use
* the exact same query with only slight differences.
*
* @param array $parameters
*
* @return Builder
*/
public function journalQuery(array $parameters)
{
/*
* We need the following vars to fine tune the query:
*/
if ($parameters['amount'] == 'negative') {
$operator = '<';
$operatorNegated = '>';
$function = 'lessThan';
} else {
$operator = '>';
$operatorNegated = '<';
$function = 'moreThan';
}
/*
* Build query:
*/
$query = \TransactionJournal::transactionTypes($parameters['transactionTypes'])->withRelevantData();
$query->where('user_id', \Auth::user()->id);
$query->where('completed', 1);
/*
* This is complex. Join `transactions` twice, once for the "to" account and once for the
* "from" account. Then get the amount from one of these (depends on type).
*
* Only need to do this when there's a sort order for "from" or "to".
*
* Also need the table prefix for this to work.
*/
if ($parameters['orderOnAccount'] === true) {
$connection = \Config::get('database.default');
$prefix = \Config::get('database.connections.' . $connection . '.prefix');
// left join first table for "from" account:
$query->leftJoin(
'transactions AS ' . $prefix . 't1', function ($join) use ($operator) {
$join->on('t1.transaction_journal_id', '=', 'transaction_journals.id')
->on('t1.amount', $operator, \DB::Raw(0));
}
);
// left join second table for "to" account:
$query->leftJoin(
'transactions AS ' . $prefix . 't2', function ($join) use ($operatorNegated) {
$join->on('t2.transaction_journal_id', '=', 'transaction_journals.id')
->on('t2.amount', $operatorNegated, \DB::Raw(0));
}
);
// also join accounts twice to get the account's name, which we need for sorting.
$query->leftJoin('accounts as ' . $prefix . 'a1', 'a1.id', '=', 't1.account_id');
$query->leftJoin('accounts as ' . $prefix . 'a2', 'a2.id', '=', 't2.account_id');
} else {
// less complex
$query->$function(0);
}
/*
* Add sort parameters to query:
*/
if (isset($parameters['order']) && count($parameters['order']) > 0) {
foreach ($parameters['order'] as $order) {
$query->orderBy($order['name'], $order['dir']);
}
} else {
$query->defaultSorting();
}
return $query;
}
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function recurringTransactionsDataset(array $parameters, Builder $query)
{
/*
* Count query:
*/
$count = $query->count();
/*
* Update the selection:
*/
$query->take($parameters['length']);
if ($parameters['start'] > 0) {
$query->skip($parameters['start']);
}
/*
* Input search parameters:
*/
$filtered = $count;
if (strlen($parameters['search']['value']) > 0) {
$query->where('recurring_transactions.description', 'LIKE', '%' . e($parameters['search']['value']) . '%');
$filtered = $query->count();
}
/*
* Build return array:
*/
$data = [
'draw' => $parameters['draw'],
'recordsTotal' => $count,
'recordsFiltered' => $filtered,
'data' => [],
];
/*
* Get paginated result set:
*/
/** @var Collection $set */
$set = $query->get(
[
'recurring_transactions.*',
]
);
/*
* Loop set and create entries to return.
*/
foreach ($set as $entry) {
$set = [
'name' => ['name' => $entry->name, 'url' => route('recurring.show', $entry->id)],
'match' => explode(' ', $entry->match),
'amount_max' => floatval($entry->amount_max),
'amount_min' => floatval($entry->amount_min),
'date' => $entry->date->format('j F Y'),
'active' => intval($entry->active),
'automatch' => intval($entry->automatch),
'repeat_freq' => $entry->repeat_freq,
'id' => [
'edit' => route('recurring.edit', $entry->id),
'delete' => route('recurring.delete', $entry->id)
]
];
if (intval($entry->skip) > 0) {
$set['repeat_freq'] = $entry->repeat_freq . ' (skip ' . $entry->skip . ')';
}
$data['data'][] = $set;
}
return $data;
}
/**
* Create a query that will pick up all recurring transactions from the database.
*
* @param array $parameters
*
* @return Builder
*/
public function recurringTransactionsQuery(array $parameters)
{
$query = \RecurringTransaction::where('user_id', \Auth::user()->id);
if (isset($parameters['order']) && count($parameters['order']) > 0) {
foreach ($parameters['order'] as $order) {
$query->orderBy($order['name'], $order['dir']);
}
} else {
$query->orderBy('name', 'ASC');
}
return $query;
}
}

View File

@@ -1,64 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
/**
* Interface JsonInterface
*
* @package Firefly\Helper\Controllers
*/
interface JsonInterface
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters();
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function journalDataset(array $parameters, Builder $query);
/**
* Builds most of the query required to grab transaction journals from the database.
* This is useful because all three pages showing different kinds of transactions use
* the exact same query with only slight differences.
*
* @param array $parameters
*
* @return Builder
*/
public function journalQuery(array $parameters);
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function recurringTransactionsDataset(array $parameters, Builder $query);
/**
* Create a query that will pick up all recurring transactions from the database.
*
* @param array $parameters
*
* @return Builder
*/
public function recurringTransactionsQuery(array $parameters);
}

View File

@@ -1,120 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\MessageBag;
class Recurring implements RecurringInterface
{
/**
* Returns messages about the validation.
*
* @param array $data
*
* @return array
*/
public function validate(array $data)
{
$errors = new MessageBag;
$warnings = new MessageBag;
$successes = new MessageBag;
/*
* Name:
*/
if (strlen($data['name']) == 0) {
$errors->add('name', 'The name should not be this short.');
}
if (strlen($data['name']) > 250) {
$errors->add('name', 'The name should not be this long.');
}
if (! isset($data['id'])) {
$count = \Auth::user()->recurringtransactions()->whereName($data['name'])->count();
} else {
$count = \Auth::user()->recurringtransactions()->whereName($data['name'])->where('id', '!=', $data['id'])->count();
}
if ($count > 0) {
$errors->add('name', 'A recurring transaction with this name already exists.');
}
if (count($errors->get('name')) == 0) {
$successes->add('name', 'OK!');
}
/*
* Match
*/
if (count(explode(',', $data['match'])) > 10) {
$warnings->add('match', 'This many matches is pretty pointless');
}
if (strlen($data['match']) == 0) {
$errors->add('match', 'Cannot match on nothing.');
}
if (count($errors->get('match')) == 0) {
$successes->add('match', 'OK!');
}
/*
* Amount
*/
if (floatval($data['amount_max']) == 0 && floatval($data['amount_min']) == 0) {
$errors->add('amount_min', 'Amount max and min cannot both be zero.');
$errors->add('amount_max', 'Amount max and min cannot both be zero.');
}
if (floatval($data['amount_max']) < floatval($data['amount_min'])) {
$errors->add('amount_max', 'Amount max must be more than amount min.');
}
if (floatval($data['amount_min']) > floatval($data['amount_max'])) {
$errors->add('amount_max', 'Amount min must be less than amount max.');
}
if (count($errors->get('amount_min')) == 0) {
$successes->add('amount_min', 'OK!');
}
if (count($errors->get('amount_max')) == 0) {
$successes->add('amount_max', 'OK!');
}
/*
* Date
*/
try {
$date = new Carbon($data['date']);
} catch (Exception $e) {
$errors->add('date', 'The date entered was invalid');
}
if (strlen($data['date']) == 0) {
$errors->add('date', 'The date entered was invalid');
}
if (!$errors->has('date')) {
$successes->add('date', 'OK!');
}
$successes->add('active', 'OK!');
$successes->add('automatch', 'OK!');
if (intval($data['skip']) < 0) {
$errors->add('skip', 'Cannot be below zero.');
} else if (intval($data['skip']) > 31) {
$errors->add('skip', 'Cannot be above 31.');
}
if (count($errors->get('skip')) == 0) {
$successes->add('skip', 'OK!');
}
$set = \Config::get('firefly.budget_periods');
if (!in_array($data['repeat_freq'], $set)) {
$errors->add('repeat_freq', 'Invalid value.');
}
if (count($errors->get('repeat_freq')) == 0) {
$successes->add('repeat_freq', 'OK!');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
}
}

View File

@@ -1,15 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
interface RecurringInterface {
/**
* Returns messages about the validation.
*
* @param array $data
*
* @return array
*/
public function validate(array $data);
}

View File

@@ -1,101 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Support\Collection;
/**
* Class Search
*
* @package Firefly\Helper\Controllers
*/
class Search implements SearchInterface
{
/**
* @param array $words
*
* @return Collection
*/
public function searchTransactions(array $words)
{
return \Auth::user()->transactionjournals()->withRelevantData()->where(
function ($q) use ($words) {
foreach ($words as $word) {
$q->orWhere('description', 'LIKE', '%' . e($word) . '%');
}
}
)->get();
}
/**
* @param array $words
*
* @return Collection
*/
public function searchAccounts(array $words)
{
return \Auth::user()->accounts()->with('accounttype')->where(
function ($q) use ($words) {
foreach ($words as $word) {
$q->orWhere('name', 'LIKE', '%' . e($word) . '%');
}
}
)->get();
}
/**
* @param array $words
*
* @return Collection
*/
public function searchCategories(array $words)
{
/** @var Collection $set */
$set = \Auth::user()->categories()->get();
$newSet = $set->filter(
function (\Category $c) use ($words) {
$found = 0;
foreach ($words as $word) {
if (!(strpos(strtolower($c->name), strtolower($word)) === false)) {
$found++;
}
}
return $found > 0;
}
);
return $newSet;
}
/**
* @param array $words
*
* @return Collection
*/
public function searchBudgets(array $words)
{
/** @var Collection $set */
$set = \Auth::user()->budgets()->get();
$newSet = $set->filter(
function (\Budget $b) use ($words) {
$found = 0;
foreach ($words as $word) {
if (!(strpos(strtolower($b->name), strtolower($word)) === false)) {
$found++;
}
}
return $found > 0;
}
);
return $newSet;
}
/**
* @param array $words
*
* @return Collection
*/
public function searchTags(array $words)
{
return new Collection;
}
}

View File

@@ -1,36 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
/**
* Interface SearchInterface
*
* @package Firefly\Helper\Controllers
*/
interface SearchInterface
{
/**
* @param array $words
*/
public function searchTransactions(array $words);
/**
* @param array $words
*/
public function searchAccounts(array $words);
/**
* @param array $words
*/
public function searchCategories(array $words);
/**
* @param array $words
*/
public function searchBudgets(array $words);
/**
* @param array $words
*/
public function searchTags(array $words);
}

View File

@@ -1,485 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Exception;
use Firefly\Exception\FireflyException;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI;
use Firefly\Storage\Category\CategoryRepositoryInterface as CRI;
use Firefly\Storage\Piggybank\PiggybankRepositoryInterface as PRI;
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
use Illuminate\Support\MessageBag;
/**
* Class Transaction
*
* @package Firefly\Helper\Controllers
*/
class Transaction implements TransactionInterface
{
protected $_user = null;
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $_journals */
protected $_journals;
/** @var \Firefly\Storage\Category\CategoryRepositoryInterface $_categories */
protected $_categories;
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $_budgets */
protected $_budgets;
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $_piggybanks */
protected $_piggybanks;
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $_accounts */
protected $_accounts;
/**
* @param TJRI $journals
* @param CRI $categories
* @param BRI $budgets
* @param PRI $piggybanks
* @param ARI $accounts
*/
public function __construct(TJRI $journals, CRI $categories, BRI $budgets, PRI $piggybanks, ARI $accounts)
{
$this->_journals = $journals;
$this->_categories = $categories;
$this->_budgets = $budgets;
$this->_piggybanks = $piggybanks;
$this->_accounts = $accounts;
$this->overruleUser(\Auth::user());
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
$this->_journals->overruleUser($user);
$this->_categories->overruleUser($user);
$this->_budgets->overruleUser($user);
$this->_piggybanks->overruleUser($user);
$this->_accounts->overruleUser($user);
return true;
}
/**
* @param \TransactionJournal $journal
* @param array $data
*
* @return MessageBag|\TransactionJournal
*/
public function update(\TransactionJournal $journal, array $data)
{
/*
* Update the journal using the repository.
*/
$journal = $this->_journals->update($journal, $data);
/*
* If invalid, return the message bag:
*/
if (!$journal->validate()) {
return $journal->errors();
}
/*
* find budget using repository
*/
if (isset($data['budget_id'])) {
$budget = $this->_budgets->find($data['budget_id']);
}
/*
* find category using repository
*/
$category = $this->_categories->firstOrCreate($data['category']);
/*
* Find piggy bank using repository:
*/
$piggybank = null;
if (isset($data['piggybank_id'])) {
$piggybank = $this->_piggybanks->find($data['piggybank_id']);
}
/*
* save accounts using repositories
* this depends on the kind of transaction and i've yet to fix this.
*/
if (isset($data['account_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['expense_account'])) {
$to = $this->_accounts->findExpenseAccountByName($data['expense_account']);
}
if (isset($data['revenue_account'])) {
$from = $this->_accounts->findRevenueAccountByName($data['revenue_account']);
$to = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['account_from_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_from_id']);
}
if (isset($data['account_to_id'])) {
$to = $this->_accounts->findAssetAccountById($data['account_to_id']);
}
/*
* Add a custom error when they are the same.
*/
if ($to->id == $from->id) {
$bag = new MessageBag;
$bag->add('account_from_id', 'The account from cannot be the same as the account to.');
return $bag;
}
/*
* Check if the transactions need new data:
*/
$transactions = $journal->transactions()->orderBy('amount', 'ASC')->get();
/** @var \Transaction $transaction */
foreach ($transactions as $index => $transaction) {
switch (true) {
case ($index == 0): // FROM account
$transaction->account()->associate($from);
$transaction->amount = floatval($data['amount']) * -1;
break;
case ($index == 1): // TO account.
$transaction->account()->associate($to);
$transaction->amount = floatval($data['amount']);
break;
}
$transaction->save();
// either way, try to attach the piggy bank:
if (!is_null($piggybank)) {
if ($piggybank->account_id == $transaction->account_id) {
$transaction->piggybank()->associate($piggybank);
}
}
}
/*
* Connect budget and category:
*/
$budgetids = !isset($budget) || (isset($budget) && is_null($budget)) ? [] : [$budget->id];
$catids = is_null($category) ? [] : [$category->id];
$components = array_merge($budgetids,$catids);
$journal->components()->sync($components);
$journal->save();
if (isset($data['return_journal']) && $data['return_journal'] == true) {
return $journal;
}
return $journal->errors();
}
/**
* Returns messages about the validation.
*
* @param array $data
* @return array
* @throws FireflyException
*/
public function validate(array $data)
{
$errors = new MessageBag;
$warnings = new MessageBag;
$successes = new MessageBag;
/*
* Description:
*/
if (strlen($data['description']) == 0) {
$errors->add('description', 'The description should not be this short.');
}
if (strlen($data['description']) > 250) {
$errors->add('description', 'The description should not be this long.');
}
/*
* Amount
*/
if (floatval($data['amount']) <= 0) {
$errors->add('amount', 'The amount cannot be zero or less than zero.');
}
if (floatval($data['amount']) > 10000) {
$warnings->add('amount', 'OK, but that\'s a lot of money dude.');
}
/*
* Date
*/
try {
$date = new Carbon($data['date']);
} catch (Exception $e) {
$errors->add('date', 'The date entered was invalid');
}
if (strlen($data['date']) == 0) {
$errors->add('date', 'The date entered was invalid');
}
if (!$errors->has('date')) {
$successes->add('date', 'OK!');
}
/*
* Category
*/
$category = $this->_categories->findByName($data['category']);
if (strlen($data['category']) == 0) {
$warnings->add('category', 'No category will be created.');
} else {
if (is_null($category)) {
$warnings->add('category', 'Will have to be created.');
} else {
$successes->add('category', 'OK!');
}
}
switch ($data['what']) {
default:
throw new FireflyException('Cannot validate a ' . $data['what']);
break;
case 'deposit':
/*
* Tests for deposit
*/
// asset account
$accountId = isset($data['account_id']) ? intval($data['account_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_id', 'Cannot find this asset account.');
} else {
$successes->add('account_id', 'OK!');
}
// revenue account:
if (strlen($data['revenue_account']) == 0) {
$warnings->add('revenue_account', 'Revenue account will be "cash".');
} else {
$exp = $this->_accounts->findRevenueAccountByName($data['revenue_account'], false);
if (is_null($exp)) {
$warnings->add('revenue_account', 'Expense account will be created.');
} else {
$successes->add('revenue_account', 'OK!');
}
}
break;
case 'transfer':
// account from
$accountId = isset($data['account_from_id']) ? intval($data['account_from_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_from_id', 'Cannot find this asset account.');
} else {
$successes->add('account_from_id', 'OK!');
}
unset($accountId);
// account to
$accountId = isset($data['account_to_id']) ? intval($data['account_to_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_to_id', 'Cannot find this asset account.');
} else {
$successes->add('account_to_id', 'OK!');
}
unset($accountId);
// piggy bank
$piggybankId = isset($data['piggybank_id']) ? intval($data['piggybank_id']) : 0;
$piggybank = $this->_piggybanks->find($piggybankId);
if (is_null($piggybank)) {
$warnings->add('piggybank_id', 'No piggy bank will be modified.');
} else {
$successes->add('piggybank_id', 'OK!');
}
break;
case 'withdrawal':
/*
* Tests for withdrawal
*/
// asset account
$accountId = isset($data['account_id']) ? intval($data['account_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_id', 'Cannot find this asset account.');
} else {
$successes->add('account_id', 'OK!');
}
// expense account
if (strlen($data['expense_account']) == 0) {
$warnings->add('expense_account', 'Expense account will be "cash".');
} else {
$exp = $this->_accounts->findExpenseAccountByName($data['expense_account'], false);
if (is_null($exp)) {
$warnings->add('expense_account', 'Expense account will be created.');
} else {
$successes->add('expense_account', 'OK!');
}
}
// budget
if (!isset($data['budget_id']) || (isset($data['budget_id']) && intval($data['budget_id']) == 0)) {
$warnings->add('budget_id', 'No budget selected.');
} else {
$budget = $this->_budgets->find(intval($data['budget_id']));
if (is_null($budget)) {
$errors->add('budget_id', 'This budget does not exist');
} else {
$successes->add('budget_id', 'OK!');
}
}
break;
}
if (count($errors->get('description')) == 0) {
$successes->add('description', 'OK!');
}
if (count($errors->get('amount')) == 0) {
$successes->add('amount', 'OK!');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
/*
* Tests for deposit
*/
/*
* Tests for transfer
*/
}
/**
* Store a full transaction journal and associated stuff
*
* @param array $data
*
* @return MessageBag|\TransactionJournal
*
* @SuppressWarnings(PHPMD.ShortVariable)
*/
public function store(array $data)
{
/*
* save journal using repository
*/
$journal = $this->_journals->store($data);
/*
* If invalid, return the message bag:
*/
if (!$journal->validate()) {
return $journal->errors();
}
/*
* find budget using repository
*/
if (isset($data['budget_id'])) {
$budget = $this->_budgets->find($data['budget_id']);
}
/*
* find category using repository
*/
$category = $this->_categories->firstOrCreate($data['category']);
/*
* Find piggy bank using repository:
*/
$piggybank = null;
if (isset($data['piggybank_id'])) {
$piggybank = $this->_piggybanks->find($data['piggybank_id']);
}
/*
* save accounts using repositories
* this depends on the kind of transaction and i've yet to fix this.
*/
if (isset($data['account_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['expense_account'])) {
$to = $this->_accounts->findExpenseAccountByName($data['expense_account']);
}
if (isset($data['revenue_account'])) {
$from = $this->_accounts->findRevenueAccountByName($data['revenue_account']);
$to = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['account_from_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_from_id']);
}
if (isset($data['account_to_id'])) {
$to = $this->_accounts->findAssetAccountById($data['account_to_id']);
}
/*
* Add a custom error when they are the same.
*/
if ($to->id == $from->id) {
$bag = new MessageBag;
$bag->add('account_from_id', 'The account from cannot be the same as the account to.');
return $bag;
}
/*
* Save transactions using repository. We try to connect the (possibly existing)
* piggy bank to either transaction, knowing it will only work with one of them.
*/
/** @var \Transaction $one */
$one = $this->_journals->saveTransaction($journal, $from, floatval($data['amount']) * -1);
$one->connectPiggybank($piggybank);
$two = $this->_journals->saveTransaction($journal, $to, floatval($data['amount']));
$two->connectPiggybank($piggybank);
/*
* Count for $journal is zero? Then there were errors!
*/
if ($journal->transactions()->count() < 2) {
/*
* Join message bags and return them:
*/
$bag = $one->errors();
$bag->merge($two->errors());
return $bag;
}
/*
* Connect budget, category and piggy bank:
*/
if (isset($budget) && !is_null($budget)) {
$journal->budgets()->save($budget);
}
if (!is_null($category)) {
$journal->categories()->save($category);
}
$journal->completed = true;
$journal->save();
/*
* Trigger recurring transaction event.
*/
\Event::fire('journals.store',[$journal]);
if (isset($data['return_journal']) && $data['return_journal'] == true) {
return ['journal' => $journal, 'messagebag' => $journal->errors()];
}
return $journal->errors();
}
}

View File

@@ -1,48 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Support\MessageBag;
/**
* Interface TransactionInterface
*
* @package Firefly\Helper\Controllers
*/
interface TransactionInterface {
/**
* Store a full transaction journal and associated stuff
*
* @param array $data
*
* @return MessageBag|\TransactionJournal
*/
public function store(array $data);
/**
* Returns messages about the validation.
*
* @param array $data
*
* @return array
*/
public function validate(array $data);
/**
* @param \TransactionJournal $journal
* @param array $data
*
* @return MessageBag|\TransactionJournal
*/
public function update(\TransactionJournal $journal, array $data);
/**
* Overrule the user used when the class is created.
*
* @param \User $user
*
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@@ -1,77 +0,0 @@
<?php
namespace Firefly\Helper\Email;
/**
* Class EmailHelper
*
* @package Firefly\Helper\Email
*/
class EmailHelper implements EmailHelperInterface
{
/**
* @param \User $user
*
* @return mixed|void
*/
public function sendVerificationMail(\User $user)
{
$reset = \Str::random(32);
$user->reset = $reset;
$user->forceSave();
$email = $user->email;
$data = ['reset' => $reset];
\Mail::send(
['emails.user.verify-html', 'emails.user.verify-text'], $data, function ($message) use ($email) {
$message->to($email, $email)->subject('Verify your e-mail address.');
}
);
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function sendPasswordMail(\User $user)
{
$password = \Str::random(12);
$user->password = $password;
$user->reset = \Str::random(32); // new one.
$user->forceSave();
$email = $user->email;
$data = ['password' => $password];
\Mail::send(
['emails.user.register-html', 'emails.user.register-text'], $data, function ($message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly!');
}
);
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function sendResetVerification(\User $user)
{
$reset = \Str::random(32);
$user->reset = $reset;
$user->forceSave();
$email = $user->email;
$data = ['reset' => $reset];
\Mail::send(
['emails.user.remindme-html', 'emails.user.remindme-text'], $data, function ($message) use ($email) {
$message->to($email, $email)->subject('Forgot your password?');
}
);
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace Firefly\Helper\Email;
/**
* Interface EmailHelperInterface
*
* @package Firefly\Helper\Email
*/
interface EmailHelperInterface
{
/**
* @param \User $user
*
* @return mixed
*/
public function sendVerificationMail(\User $user);
/**
* @param \User $user
*
* @return mixed
*/
public function sendPasswordMail(\User $user);
/**
* @param \User $user
*
* @return mixed
*/
public function sendResetVerification(\User $user);
}

View File

@@ -1,78 +0,0 @@
<?php
namespace Firefly\Helper;
use Illuminate\Support\ServiceProvider;
/**
* Class HelperServiceProvider
*
* @package Firefly\Helper
*/
class HelperServiceProvider extends ServiceProvider
{
/**
* Triggered automatically by Laravel
*/
public function register()
{
// controllers:
$this->app->bind(
'Firefly\Helper\Controllers\AccountInterface',
'Firefly\Helper\Controllers\Account'
);
$this->app->bind(
'Firefly\Helper\Controllers\ChartInterface',
'Firefly\Helper\Controllers\Chart'
);
$this->app->bind(
'Firefly\Helper\Controllers\JsonInterface',
'Firefly\Helper\Controllers\Json'
);
$this->app->bind(
'Firefly\Helper\Controllers\RecurringInterface',
'Firefly\Helper\Controllers\Recurring'
);
$this->app->bind(
'Firefly\Helper\Controllers\SearchInterface',
'Firefly\Helper\Controllers\Search'
);
$this->app->bind(
'Firefly\Helper\Controllers\TransactionInterface',
'Firefly\Helper\Controllers\Transaction'
);
$this->app->bind(
'Firefly\Helper\Controllers\CategoryInterface',
'Firefly\Helper\Controllers\Category'
);
$this->app->bind(
'Firefly\Helper\Controllers\BudgetInterface',
'Firefly\Helper\Controllers\Budget'
);
// mail:
$this->app->bind(
'Firefly\Helper\Email\EmailHelperInterface',
'Firefly\Helper\Email\EmailHelper'
);
// settings:
$this->app->bind(
'Firefly\Helper\Preferences\PreferencesHelperInterface',
'Firefly\Helper\Preferences\PreferencesHelper'
);
// settings:
$this->app->bind(
'Firefly\Helper\Toolkit\ToolkitInterface',
'Firefly\Helper\Toolkit\Toolkit'
);
}
}

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