Compare commits

...

194 Commits

Author SHA1 Message Date
James Cole
3d9755ca8c Merge branch 'release/3.4.0.9' 2015-05-24 11:53:43 +02:00
James Cole
b5cf2d03e6 New version. 2015-05-24 11:53:34 +02:00
James Cole
e3b35b8f35 Add code climate file [skip ci] 2015-05-24 11:46:41 +02:00
James Cole
1c1fe672bd Some code cleanup [skip ci] 2015-05-24 11:41:52 +02:00
James Cole
6c71f68ed8 Should cover models. 2015-05-24 11:13:46 +02:00
James Cole
8f2f912cdf New (empty) tests for models. 2015-05-24 10:01:00 +02:00
James Cole
bf6ea16acb Fixed test. 2015-05-24 09:29:44 +02:00
James Cole
288546c2b9 Fixed some math. 2015-05-24 08:00:40 +02:00
James Cole
724db6c34c Built some new tests 2015-05-24 07:43:48 +02:00
James Cole
067c451c1d Fixed tests 2015-05-23 21:41:04 +02:00
James Cole
11e3696191 Cleanup tests. 2015-05-23 21:23:51 +02:00
James Cole
41e20664de Fixed coverage. 2015-05-23 21:18:20 +02:00
James Cole
d8de90d6f3 Ignore constructors. 2015-05-23 20:49:57 +02:00
James Cole
b01e8299d3 Fixed tests 2015-05-23 20:47:31 +02:00
James Cole
1ec11e3e2e Compartmentalised all tests. 2015-05-23 19:41:54 +02:00
James Cole
422f429725 Experimental count thing. 2015-05-23 17:38:16 +02:00
James Cole
5c55fa5fbb Remove encrypted amounts because it stinks. 2015-05-23 17:33:04 +02:00
James Cole
80d845fdf2 Save but don't use. 2015-05-23 17:18:23 +02:00
James Cole
601fe68346 Don't multiply transactions. 2015-05-23 17:14:36 +02:00
James Cole
9e050fb059 Updated some tests. 2015-05-23 17:11:16 +02:00
James Cole
99d4adf5e6 Translations [skip ci] 2015-05-23 15:57:38 +02:00
James Cole
85f8d1e8e9 Math is hard [skip ci] 2015-05-23 15:45:01 +02:00
James Cole
8334d3d99f Translation error [skip ci] 2015-05-23 15:43:48 +02:00
James Cole
cff08d19eb Slowly move away from using the raw 'transactions.amount' field. 2015-05-23 15:42:19 +02:00
James Cole
2d86390bc1 Experimental query.Code cleanup [skip ci] 2015-05-23 09:11:59 +02:00
James Cole
7a20835571 Code cleanup [skip ci] 2015-05-23 09:09:25 +02:00
James Cole
ff3c9676b5 Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 09:05:08 +02:00
James Cole
055f97dab1 Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 09:03:52 +02:00
James Cole
8a867e71a1 Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 09:02:27 +02:00
James Cole
b8275b4734 Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 09:01:38 +02:00
James Cole
36b951b146 Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 09:01:07 +02:00
James Cole
c5a5f17643 Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 09:00:11 +02:00
James Cole
16b909c4df Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 08:59:23 +02:00
James Cole
92b7648e03 Encrypt more stuff in the cleanup routine [skip ci] 2015-05-23 08:58:21 +02:00
James Cole
ca46ebe3b2 Encrypt bill amounts [skip ci] 2015-05-23 08:57:02 +02:00
James Cole
676e48254a Whoops [skip ci] 2015-05-23 08:55:16 +02:00
James Cole
b15b55227d Add another cleanup routine. [skip ci] 2015-05-23 08:54:25 +02:00
James Cole
3c3b723913 Some code cleanup [skip ci] 2015-05-23 08:51:24 +02:00
James Cole
f05002c729 Updated models for encryption. 2015-05-23 08:46:46 +02:00
James Cole
1c2cbd5b40 New stuff for encrypted amounts. 2015-05-23 07:47:36 +02:00
James Cole
54c6ca9f45 Expanded some tests. 2015-05-22 20:25:12 +02:00
James Cole
c10efbb170 Also encrypt meta data for reminders. 2015-05-22 19:19:18 +02:00
James Cole
a496ad5814 Simple routine to encrypt entries which were not encrypted yet. 2015-05-22 19:18:00 +02:00
James Cole
50cf7f6a3b Merge branch 'release/3.4.0.8' 2015-05-22 18:55:13 +02:00
James Cole
f946f10afd Merge branch 'release/3.4.0.8' into develop 2015-05-22 18:55:13 +02:00
James Cole
eecb4db34c Built chart tests. 2015-05-22 18:31:57 +02:00
James Cole
1f865d3ea4 Improved coverage. 2015-05-22 15:05:32 +02:00
James Cole
623bb4b350 New tests. 2015-05-22 10:17:20 +02:00
James Cole
dc8ad673a6 New test to cover some reminder stuff. 2015-05-22 08:52:30 +02:00
James Cole
4914ad821e Fixed coverage for transaction controller. 2015-05-22 08:03:34 +02:00
James Cole
f099cbadc3 Version increment. 2015-05-22 07:16:36 +02:00
James Cole
42cda384c8 Fix help routes 2015-05-22 07:15:40 +02:00
James Cole
23c91b9990 Some translations, playing around with popups. [skip ci] 2015-05-21 23:05:31 +02:00
James Cole
ff0379182e Sort chart [skip ci] 2015-05-21 18:57:14 +02:00
James Cole
e08a23948f Fixed chart. [skip ci] 2015-05-21 18:50:23 +02:00
James Cole
bd56de6d36 Fix other translations [skip ci] 2015-05-21 17:47:10 +02:00
James Cole
42970aea80 Fix some translations. 2015-05-21 17:45:01 +02:00
James Cole
003a05ee8d Fixed the category chart. 2015-05-21 17:27:48 +02:00
James Cole
ffb11b01a6 Some new translations. 2015-05-21 07:44:44 +02:00
James Cole
e426f5d5da Some new translations. 2015-05-21 07:30:38 +02:00
James Cole
6989f61e1b Extra translations [skip ci] 2015-05-20 20:10:01 +02:00
James Cole
0e6677ccb3 Remove cash accounts from list [skip ci] 2015-05-20 20:04:27 +02:00
James Cole
8f104d555a Added newlines to files. 2015-05-20 19:56:14 +02:00
James Cole
b1d3158db1 Code cleanup. 2015-05-20 19:55:53 +02:00
James Cole
7645005d5a Return of account id. 2015-05-20 18:18:05 +02:00
James Cole
411f77fd29 Fixed some other displays of money 2015-05-20 18:09:44 +02:00
James Cole
568ab26db1 Spell check [skip ci] 2015-05-20 17:06:47 +02:00
James Cole
29652108f0 Fixed tests. 2015-05-20 17:04:53 +02:00
James Cole
f07e4dc711 Fix sort. [skip ci] 2015-05-20 07:21:40 +02:00
James Cole
8a2ac457c2 Name fix [skip ci] 2015-05-20 07:21:01 +02:00
James Cole
9e54eecfaa Fixed some views. 2015-05-20 07:20:02 +02:00
James Cole
95ef691077 Fixed some more reports and charts. [skip ci] 2015-05-20 07:07:46 +02:00
James Cole
7a0ad5a587 Fixed a chart [skip ci] 2015-05-20 06:50:24 +02:00
James Cole
42b49d0e4b Added some correcting methods. [skip ci] 2015-05-20 06:50:15 +02:00
James Cole
9217c2f003 Corrected some charts [skip ci] 2015-05-20 06:49:51 +02:00
James Cole
fbdf66998d Added a forgotten chart, corrected some others. [skip ci] 2015-05-20 06:49:22 +02:00
James Cole
deda9d3c54 Add corrected expense routes [skip ci] 2015-05-20 06:49:03 +02:00
James Cole
a5d78f20ae Add corrected income and expense methods [skip ci] 2015-05-20 06:48:52 +02:00
James Cole
5ed09e3f38 Call the corrected spent function [skip ci] 2015-05-20 06:47:53 +02:00
James Cole
3e9774cd66 Merge branch 'release/3.4.0.7' 2015-05-18 18:23:07 +02:00
James Cole
54387c8fdf Merge branch 'release/3.4.0.7' into develop 2015-05-18 18:23:07 +02:00
James Cole
7eec949a13 Push new release. 2015-05-18 18:23:01 +02:00
James Cole
4113c4ff40 Fixed the tests. 2015-05-18 17:57:44 +02:00
James Cole
1bf0968bfe Show correct icon. [skip ci] 2015-05-18 17:05:22 +02:00
James Cole
374b90fb00 Show correct amount [skip ci] 2015-05-18 17:04:10 +02:00
James Cole
064e60e9d5 Show tag amount better [skip ci] 2015-05-18 17:02:18 +02:00
James Cole
b637455970 New translations [skip ci] 2015-05-18 16:59:01 +02:00
James Cole
68158937d1 Fixed translation [skip ci] 2015-05-18 06:56:59 +02:00
James Cole
adb1356b7a Small bug fix in display of income vs. expenses [skip ci] 2015-05-18 06:55:36 +02:00
James Cole
d880ccb8e0 Small bug fix in display of income vs. expenses [skip ci] 2015-05-18 06:54:46 +02:00
James Cole
050fb1d1ef Some translations [skip ci] 2015-05-17 18:06:00 +02:00
James Cole
6580752bde Now includes chart! [skip ci] 2015-05-17 18:03:16 +02:00
James Cole
c9df265c9b Included bills in month report. [skip ci] 2015-05-17 17:54:13 +02:00
James Cole
098e5bc162 Small translation fix [skip ci] 2015-05-17 17:27:56 +02:00
James Cole
4b2dcc74d4 Expanded the amount thing. 2015-05-17 16:12:00 +02:00
James Cole
a9254c5c9a Some new translations, better tag view [skip ci] 2015-05-17 15:55:24 +02:00
James Cole
7ce57e6ccb Fix a view, fix tag format. 2015-05-17 15:24:30 +02:00
James Cole
0fcb32a66f Fix update error. 2015-05-17 15:17:06 +02:00
James Cole
9946535f01 Some new bread crumbs [skip ci] 2015-05-17 15:12:45 +02:00
James Cole
2b17396d6b Fix some translations. 2015-05-17 13:44:58 +02:00
James Cole
b01d5bc237 Translation fixes [skip ci] 2015-05-17 12:53:32 +02:00
James Cole
b123860304 More stuff. 2015-05-17 12:49:09 +02:00
James Cole
033f5b67db Code cleanup 2015-05-17 11:28:58 +02:00
James Cole
6280448dfb Fixed more tests. 2015-05-17 10:47:12 +02:00
James Cole
01cd3333e4 Fixing tests. 2015-05-17 10:36:11 +02:00
James Cole
63050907b9 Code cleanup. 2015-05-17 10:30:18 +02:00
James Cole
beedf7d780 Cleaning up 2015-05-17 10:10:58 +02:00
James Cole
6b8194261f Clean up and simplify code. 2015-05-17 10:01:47 +02:00
James Cole
dbb1c4d534 Fixed some reports. 2015-05-17 09:35:49 +02:00
James Cole
e6263f9ff5 Some cleaning up 2015-05-17 09:18:44 +02:00
James Cole
6ca119c4db Minor changes [skip ci] 2015-05-17 09:04:55 +02:00
James Cole
c483a1ab3a Ignore simple classes. 2015-05-16 19:08:54 +02:00
James Cole
2e7edd033c Some expanded things. 2015-05-16 16:47:52 +02:00
James Cole
c576902501 Fixed tests. 2015-05-16 16:04:51 +02:00
James Cole
66c2951594 Cleanup 2015-05-16 15:52:09 +02:00
James Cole
b812881cdb More hip report stuff. 2015-05-16 15:43:58 +02:00
James Cole
cdeac2c6db Should simplify reports. 2015-05-16 14:51:23 +02:00
James Cole
bca2ddd529 Budget report thing. 2015-05-16 14:14:22 +02:00
James Cole
e7285c6499 Some overhauls. 2015-05-16 13:53:08 +02:00
James Cole
bdff275672 Simplified report code. 2015-05-16 13:06:38 +02:00
James Cole
bec58a1ee6 Experimental test coverage. 2015-05-16 10:34:38 +02:00
James Cole
f64616748c Expenses only [skip ci] 2015-05-16 10:18:32 +02:00
James Cole
512ce15973 Small tweaks in charts. 2015-05-16 10:13:41 +02:00
James Cole
ed8b301574 Routes and JS cleanup. 2015-05-16 10:05:22 +02:00
James Cole
d22a6c019c Fixed lots of chart references. 2015-05-16 09:57:31 +02:00
James Cole
a0cb1b9d9e Clean up and split chart controllers 2015-05-16 09:41:14 +02:00
James Cole
a5294c62ea Renamed a method. 2015-05-16 09:26:54 +02:00
James Cole
e155d3311c Fixed tests 2015-05-16 09:25:14 +02:00
James Cole
0a372b0daf Cleanup tests 2015-05-16 09:16:32 +02:00
James Cole
69143399d1 Moved some stuff around. 2015-05-16 09:09:52 +02:00
James Cole
3270d3bf96 Cleanup. 2015-05-16 08:14:26 +02:00
James Cole
3896a66122 Fixed tests. 2015-05-16 08:09:15 +02:00
James Cole
b94781aef1 Some optimisations. 2015-05-16 08:05:04 +02:00
James Cole
bed1adc367 Fixed budget. 2015-05-16 07:56:15 +02:00
James Cole
ae54497efa Differ between shared and normal transactions (not build yet). 2015-05-16 07:49:02 +02:00
James Cole
06b747c221 Some cleanup and translations. 2015-05-16 07:28:58 +02:00
James Cole
f159beee0d Fixed preferences tests. 2015-05-15 23:24:55 +02:00
James Cole
49d7dea086 Fixed some coverage. 2015-05-15 23:02:42 +02:00
James Cole
3e65733dc5 Small fix in report view [skip ci] 2015-05-15 22:12:31 +02:00
James Cole
cc375d58bb Fixed tests. 2015-05-15 22:06:52 +02:00
James Cole
911c7c662a Expanded reports. 2015-05-15 22:00:00 +02:00
James Cole
aae003be33 Some more cleaning up and fixing 2015-05-15 21:01:24 +02:00
James Cole
aede03d8b2 Some more layout changes. 2015-05-15 20:43:50 +02:00
James Cole
f0f5ada7de More changes to reports. 2015-05-15 20:38:39 +02:00
James Cole
58365121a3 Fixed tests. 2015-05-15 20:15:58 +02:00
James Cole
d5a154d2e6 Cleaned up reports and associated views. 2015-05-15 20:07:51 +02:00
James Cole
b20f369aef Some new tests. 2015-05-15 18:39:59 +02:00
James Cole
abb8aa0b29 Merge branch 'feature/translations' into develop 2015-05-15 18:38:26 +02:00
James Cole
5368a0f1d7 Some new translations. 2015-05-14 19:44:20 +02:00
James Cole
d3897eece7 This should fix the tests on the translations branch. 2015-05-14 18:15:31 +02:00
James Cole
a82b829da9 More translations. 2015-05-14 18:00:56 +02:00
James Cole
9f5058e81a Some new translations. 2015-05-14 17:52:50 +02:00
James Cole
5b19263720 Some more [skip ci] 2015-05-14 16:43:12 +02:00
James Cole
9d5a0db0d9 [skip ci] 2015-05-14 16:36:08 +02:00
James Cole
4bd8a7014f Count things [skip ci] 2015-05-14 16:35:41 +02:00
James Cole
353e96d951 Better (and translated) delete forms. [skip ci] 2015-05-14 16:32:35 +02:00
James Cole
149a6f92b0 Translations [skip ci] 2015-05-14 15:56:23 +02:00
James Cole
d66426c137 Nieuw vertalingen [skip ci] 2015-05-14 15:53:56 +02:00
James Cole
4fc9966392 Chart translations [skip ci] 2015-05-14 13:43:58 +02:00
James Cole
417766f0db Various date localisation. [skip ci] 2015-05-14 13:41:21 +02:00
James Cole
de9ac97887 Also english. [skip ci] 2015-05-14 13:19:26 +02:00
James Cole
6be42f112a Charts and titles in Dutch. [skip ci] 2015-05-14 13:17:53 +02:00
James Cole
3895ae63c7 Some translations for lists [skip ci] 2015-05-14 13:00:43 +02:00
James Cole
607d416d54 Better translation for 'balance' [skip ci] 2015-05-14 12:11:56 +02:00
James Cole
038693dc86 More index translations [skip ci] 2015-05-14 12:10:42 +02:00
James Cole
cc9be13544 Added a bread crumb [skip ci] 2015-05-14 11:50:48 +02:00
James Cole
9c1474087f Forgot one [skip ci] 2015-05-14 11:50:04 +02:00
James Cole
831de2bcf4 New menu translations [skip ci] 2015-05-14 11:48:59 +02:00
James Cole
eb687333bb Small edits to buttons and others. [skip ci] 2015-05-14 11:31:54 +02:00
James Cole
5cc7966d54 Some new form things. [skip ci] 2015-05-14 11:25:43 +02:00
James Cole
d1a34e7a6f Breadcrumbs translated to English. [skip ci] 2015-05-14 11:22:20 +02:00
James Cole
d63d791717 Translated all bread crumbs to Dutch. [skip ci] 2015-05-14 11:17:56 +02:00
James Cole
015570c741 Small translations as a basic start [skip ci] 2015-05-14 10:26:15 +02:00
James Cole
8400ebc9c6 Added a language test, added Dutch basic file. [skip ci] 2015-05-14 10:03:57 +02:00
James Cole
9f99e7c0af Edit and set language [skip ci] 2015-05-14 09:59:30 +02:00
James Cole
392c1fc399 Add language selector to preferences [skip ci] 2015-05-14 09:56:41 +02:00
James Cole
d3cea7a89c Add new supported languages [skip ci] 2015-05-14 09:52:58 +02:00
James Cole
1dcf7407e6 Cleanup [skip ci] 2015-05-14 09:51:54 +02:00
James Cole
d543c033a3 Small fixes to translations. 2015-05-14 09:50:07 +02:00
James Cole
aaa186be5e Merge branch 'release/3.4.0.6' 2015-05-14 09:21:23 +02:00
James Cole
2054b5b3dd Merge branch 'release/3.4.0.6' into develop 2015-05-14 09:21:23 +02:00
James Cole
98ae5b0ca0 New read me. 2015-05-14 09:21:12 +02:00
James Cole
36c8171d0f Clean up and tests. 2015-05-14 09:01:10 +02:00
James Cole
3603eb94cc Different budget query [skip ci] 2015-05-14 08:08:25 +02:00
James Cole
0e068d4ccf Fixed some tests. 2015-05-13 21:37:11 +02:00
James Cole
199f348ff4 Smaller mobile pages. 2015-05-13 21:32:33 +02:00
James Cole
b22655fb7c Fix helper 2015-05-13 21:32:27 +02:00
James Cole
06cc9618ba Some new tests 2015-05-13 21:32:21 +02:00
James Cole
b9019c8c7f Smaller screens update [skip ci] 2015-05-13 06:34:17 +02:00
James Cole
77e133e67c Some cleaning up for mobile screens [skip ci] 2015-05-13 06:31:32 +02:00
James Cole
571e7df807 Some tiny optimisations. 2015-05-12 20:51:03 +02:00
James Cole
22f4d2979a Added some more ignores, edited some middleware. 2015-05-11 22:12:53 +02:00
James Cole
e46e366694 Added some tests to cover an event. 2015-05-11 21:45:31 +02:00
James Cole
9ca79f767c Merge branch 'release/3.4.0.5' into develop 2015-05-10 19:18:57 +02:00
226 changed files with 9953 additions and 4468 deletions

23
.codeclimate.yml Normal file
View File

@@ -0,0 +1,23 @@
# Save as .codeclimate.yml (note leading .) in project root directory
languages:
JavaScript: true
PHP: true
exclude_paths:
- "public/packages/maximebf/php-debugbar/debugbar.js"
- "public/packages/maximebf/php-debugbar/widgets.js"
- "public/packages/maximebf/php-debugbar/openhandler.js"
- "public/packages/maximebf/php-debugbar/widgets/sqlqueries/widget.js"
- "public/js/bootstrap3-typeahead.min.js"
- "public/js/bootstrap-sortable.js"
- "public/js/bootstrap-tagsinput.min.js"
- "public/js/bootstrap-tagsinput.min.js.map"
- "public/js/daterangepicker.js"
- "public/js/jquery-2.1.3.min.js"
- "public/js/jquery-2.1.3.min.js.map"
- "public/js/jquery-ui.min.js"
- "public/js/metisMenu.js"
- "public/js/moment.min.js"
- "public/js/sb-admin-2.js"
- "tests/*"
- "database/*"
- "storage/*"

View File

@@ -16,4 +16,5 @@ EMAIL_DRIVER=smtp
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=
EMAIL_PRETEND=false
EMAIL_PRETEND=false
RUNCLEANUP=true

View File

@@ -1,5 +1,5 @@
# Firefly III
#### v3.4.0.5
#### v3.4.0.9
[![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)
@@ -13,11 +13,13 @@
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared
household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.
Firefly is a system you'll have install yourself on webhosting of your choosing.
_Firefly is a system you'll have install yourself on webhosting of your choosing._
Personal financial management is pretty difficult, and everybody has their own approach to it. Some people
make budgets, other people limit their cashflow by throwing away their credit cards, others get a better job.
There are tons of ways to save and earn money.
make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase
their current cashflow. There are tons of ways to save and earn money.
Firefly works on the principle that if you know where you're money is going, you can stop it from going there.
To get to know Firefly, and to see if it fits you, check out these resources:
@@ -28,24 +30,15 @@ To get to know Firefly, and to see if it fits you, check out these resources:
and the philosophy behind it.
### About the name (if you care)
#### About the name (if you care)
It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since.
## Running and installing
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
## Current features
- [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management;
- You can manage different types of accounts
- You can manage different types of accounts;
- [Asset](https://en.wikipedia.org/wiki/Asset) accounts
- Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household))
- Saving accounts
@@ -63,8 +56,8 @@ Everything is organised:
- Clear views that should show you how you're doing;
- Easy navigation through your records;
- Browse back and forth to see previous months or even years;
- Lots of charts because we all love them.
- Financial reporting showing you how well you are doing;
- Lots of charts because we all love them;
- Financial reporting showing you how well you are doing.
## Screenshots
@@ -84,6 +77,16 @@ _Please note that everything in these screenshots is fictional and may not be re
![Piggy banks](https://i.nder.be/hkud0h53)
## Running and installing
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
## Current state
Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list

View File

@@ -26,6 +26,8 @@ class Kernel extends ConsoleKernel
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return void
*/
protected function schedule(Schedule $schedule)

View File

@@ -27,6 +27,7 @@ class Handler extends ExceptionHandler
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @SuppressWarnings(PHPMD.ShortVariable)
*
* @return \Illuminate\Http\Response
*/
@@ -43,6 +44,7 @@ class Handler extends ExceptionHandler
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
* @SuppressWarnings(PHPMD.ShortVariable)
*
* @param \Exception $e
*

View File

@@ -6,7 +6,6 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Log;
/**
* Class ConnectJournalToPiggyBank
@@ -19,6 +18,8 @@ class ConnectJournalToPiggyBank
/**
* Create the event handler.
*
* @codeCoverageIgnore
*
*/
public function __construct()
{
@@ -28,54 +29,36 @@ class ConnectJournalToPiggyBank
/**
* Handle the event when journal is saved.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param JournalCreated $event
*
* @return void
* @return boolean
*/
public function handle(JournalCreated $event)
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
if (intval($piggyBankId) < 1) {
return;
}
Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId);
/** @var PiggyBank $piggyBank */
$piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank) || $journal->transactionType->type != 'Transfer') {
return;
}
Log::debug('Found a piggy bank');
$amount = $journal->amount;
Log::debug('Amount: ' . $amount);
if ($amount == 0) {
return;
if (is_null($piggyBank)) {
return false;
}
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
Log::debug('Found no repetition for piggy bank for date ' . $journal->date->format('Y M d'));
return;
return false;
}
Log::debug('Found rep! ' . $repetition->id);
/*
* Add amount when
*/
$amount = $journal->amount;
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id == $piggyBank->account_id) {
if ($transaction->amount < 0) {
$amount = $amount * -1;
Log::debug('Transaction is away from piggy, so amount becomes ' . $amount);
} else {
Log::debug('Transaction is to from piggy, so amount stays ' . $amount);
$amount = $transaction->amount * -1;
}
}
}
@@ -83,14 +66,9 @@ class ConnectJournalToPiggyBank
$repetition->currentamount += $amount;
$repetition->save();
PiggyBankEvent::create(
[
'piggy_bank_id' => $piggyBank->id,
'transaction_journal_id' => $journal->id,
'date' => $journal->date,
'amount' => $amount
]
);
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);
return true;
}

View File

@@ -1,36 +0,0 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalDeleted;
/**
* Class JournalDeletedHandler
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class JournalDeletedHandler
{
/**
* Create the event handler.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalDeleted $event
*
* @return void
*/
public function handle(JournalDeleted $event)
{
//
}
}

View File

@@ -7,6 +7,7 @@ use Log;
/**
* Class RescanJournal
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class RescanJournal

View File

@@ -2,10 +2,12 @@
use FireflyIII\Events\JournalSaved;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
/**
* Class UpdateJournalConnection
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class UpdateJournalConnection
@@ -38,7 +40,11 @@ class UpdateJournalConnection
return;
}
$piggyBank = $event->piggyBank()->first();
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
$repetition = null;
if ($piggyBank) {
/** @var PiggyBankRepetition $repetition */
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
}
if (is_null($repetition)) {
return;

View File

@@ -0,0 +1,90 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
* Class Account
*
* @package FireflyIII\Helpers\Collection
*/
class Account
{
/** @var Collection */
protected $accounts;
/** @var float */
protected $difference;
/** @var float */
protected $end;
/** @var float */
protected $start;
/**
* @return \Illuminate\Support\Collection
*/
public function getAccounts()
{
return $this->accounts;
}
/**
* @param \Illuminate\Support\Collection $accounts
*/
public function setAccounts($accounts)
{
$this->accounts = $accounts;
}
/**
* @return float
*/
public function getDifference()
{
return $this->difference;
}
/**
* @param float $difference
*/
public function setDifference($difference)
{
$this->difference = $difference;
}
/**
* @return float
*/
public function getEnd()
{
return $this->end;
}
/**
* @param float $end
*/
public function setEnd($end)
{
$this->end = $end;
}
/**
* @return float
*/
public function getStart()
{
return $this->start;
}
/**
* @param float $start
*/
public function setStart($start)
{
$this->start = $start;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Balance
*
* @package FireflyIII\Helpers\Collection
*/
class Balance
{
/** @var BalanceHeader */
protected $balanceHeader;
/** @var Collection */
protected $balanceLines;
/**
*
*/
public function __construct()
{
$this->balanceLines = new Collection;
}
/**
* @param BalanceLine $line
*/
public function addBalanceLine(BalanceLine $line)
{
$this->balanceLines->push($line);
}
/**
* @return BalanceHeader
*/
public function getBalanceHeader()
{
return $this->balanceHeader;
}
/**
* @param BalanceHeader $balanceHeader
*/
public function setBalanceHeader($balanceHeader)
{
$this->balanceHeader = $balanceHeader;
}
/**
* @return \Illuminate\Support\Collection
*/
public function getBalanceLines()
{
return $this->balanceLines;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
/**
* @codeCoverageIgnore
*
* Class BalanceEntry
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceEntry
{
/** @var AccountModel */
protected $account;
/** @var float */
protected $left = 0.0;
/** @var float */
protected $spent = 0.0;
/**
* @return AccountModel
*/
public function getAccount()
{
return $this->account;
}
/**
* @param AccountModel $account
*/
public function setAccount($account)
{
$this->account = $account;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class BalanceHeader
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceHeader
{
/** @var Collection */
protected $accounts;
/**
*
*/
public function __construct()
{
$this->accounts = new Collection;
}
/**
* @param AccountModel $account
*/
public function addAccount(AccountModel $account)
{
$this->accounts->push($account);
}
/**
* @return Collection
*/
public function getAccounts()
{
return $this->accounts;
}
}

View File

@@ -0,0 +1,170 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class BalanceLine
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceLine
{
const ROLE_DEFAULTROLE = 1;
const ROLE_TAGROLE = 2;
const ROLE_DIFFROLE = 3;
/** @var Collection */
protected $balanceEntries;
/** @var BudgetModel */
protected $budget;
/** @var LimitRepetition */
protected $repetition;
protected $role = self::ROLE_DEFAULTROLE;
/**
*
*/
public function __construct()
{
$this->balanceEntries = new Collection;
}
/**
* @param BalanceEntry $balanceEntry
*/
public function addBalanceEntry(BalanceEntry $balanceEntry)
{
$this->balanceEntries->push($balanceEntry);
}
/**
* @return string
*/
public function getTitle()
{
if ($this->getBudget() instanceof BudgetModel) {
return $this->getBudget()->name;
}
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
return trans('firefly.noBudget');
}
if ($this->getRole() == self::ROLE_TAGROLE) {
return trans('firefly.coveredWithTags');
}
if ($this->getRole() == self::ROLE_DIFFROLE) {
return trans('firefly.leftUnbalanced');
}
}
/**
* @return BudgetModel
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param BudgetModel $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
}
/**
* @return int
*/
public function getRole()
{
return $this->role;
}
/**
* @param int $role
*/
public function setRole($role)
{
$this->role = $role;
}
/**
* If a BalanceLine has a budget/repetition, each BalanceEntry in this BalanceLine
* should have a "spent" value, which is the amount of money that has been spent
* on the given budget/repetition. If you subtract all those amounts from the budget/repetition's
* total amount, this is returned:
*
* @return float
*/
public function leftOfRepetition()
{
$start = $this->getRepetition() ? $this->getRepetition()->amount : 0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$start += $balanceEntry->getSpent();
}
return $start;
}
/**
* @return LimitRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param LimitRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return Collection
*/
public function getBalanceEntries()
{
return $this->balanceEntries;
}
/**
* @param Collection $balanceEntries
*/
public function setBalanceEntries($balanceEntries)
{
$this->balanceEntries = $balanceEntries;
}
/**
* If the BalanceEntries for a BalanceLine have a "left" value, the amount
* of money left in the entire BalanceLine is returned here:
*
* @return float
*/
public function sumOfLeft()
{
$sum = 0.0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$sum += $balanceEntry->getLeft();
}
return $sum;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Bill as BillModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
* Class Bill
*
* @package FireflyIII\Helpers\Collection
*/
class Bill
{
/**
* @var Collection
*/
protected $bills;
/**
*
*/
public function __construct()
{
$this->bills = new Collection;
}
/**
* @param BillLine $bill
*/
public function addBill(BillLine $bill)
{
$this->bills->push($bill);
}
/**
* @return Collection
*/
public function getBills()
{
$this->bills->sortBy(
function (BillLine $bill) {
$active = intval($bill->getBill()->active) == 0 ? 1 : 0;
$name = $bill->getBill()->name;
return $active . $name;
}
);
return $this->bills;
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Bill as BillModel;
/**
* @codeCoverageIgnore
*
* Class BillLine
*
* @package FireflyIII\Helpers\Collection
*/
class BillLine
{
/** @var bool */
protected $active;
/** @var float */
protected $amount;
/** @var BillModel */
protected $bill;
/** @var bool */
protected $hit;
/** @var float */
protected $max;
/** @var float */
protected $min;
/**
* @return float
*/
public function getAmount()
{
return $this->amount;
}
/**
* @param float $amount
*/
public function setAmount($amount)
{
$this->amount = $amount;
}
/**
* @return BillModel
*/
public function getBill()
{
return $this->bill;
}
/**
* @param BillModel $bill
*/
public function setBill($bill)
{
$this->bill = $bill;
}
/**
* @return float
*/
public function getMax()
{
return $this->max;
}
/**
* @param float $max
*/
public function setMax($max)
{
$this->max = $max;
}
/**
* @return float
*/
public function getMin()
{
return $this->min;
}
/**
* @param float $min
*/
public function setMin($min)
{
$this->min = $min;
}
/**
* @return boolean
*/
public function isActive()
{
return $this->active;
}
/**
* @param boolean $active
*/
public function setActive($active)
{
$this->active = $active;
}
/**
* @return boolean
*/
public function isHit()
{
return $this->hit;
}
/**
* @param boolean $hit
*/
public function setHit($hit)
{
$this->hit = $hit;
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Budget
*
* @package FireflyIII\Helpers\Collection
*/
class Budget
{
/** @var Collection */
protected $budgetLines;
/** @var string */
protected $budgeted = '0';
/** @var string */
protected $left = '0';
/** @var string */
protected $overspent = '0';
/** @var string */
protected $spent = '0';
/**
*
*/
public function __construct()
{
$this->budgetLines = new Collection;
}
/**
* @param BudgetLine $budgetLine
*/
public function addBudgetLine(BudgetLine $budgetLine)
{
$this->budgetLines->push($budgetLine);
}
/**
* @param float $add
*/
public function addBudgeted($add)
{
$add = strval(round($add, 2));
bcscale(2);
$this->budgeted = bcadd($this->budgeted, $add);
}
/**
* @param float $add
*/
public function addLeft($add)
{
$add = strval(round($add, 2));
bcscale(2);
$this->left = bcadd($this->left, $add);
}
/**
* @param float $add
*/
public function addOverspent($add)
{
$add = strval(round($add, 2));
bcscale(2);
$this->overspent = bcadd($this->overspent, $add);
}
/**
* @param float $add
*/
public function addSpent($add)
{
$add = strval(round($add, 2));
bcscale(2);
$this->spent = bcadd($this->spent, $add);
}
/**
* @return \Illuminate\Support\Collection
*/
public function getBudgetLines()
{
return $this->budgetLines;
}
/**
* @return string
*/
public function getBudgeted()
{
return $this->budgeted;
}
/**
* @param string $budgeted
*/
public function setBudgeted($budgeted)
{
$this->budgeted = $budgeted;
}
/**
* @return string
*/
public function getLeft()
{
return $this->left;
}
/**
* @param string $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return string
*/
public function getOverspent()
{
return $this->overspent;
}
/**
* @param string $overspent
*/
public function setOverspent($overspent)
{
$this->overspent = strval(round($overspent, 2));
}
/**
* @return string
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param string $spent
*/
public function setSpent($spent)
{
$this->spent = strval(round($spent, 2));
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
/**
* @codeCoverageIgnore
*
* Class BudgetLine
*
* @package FireflyIII\Helpers\Collection
*/
class BudgetLine
{
/** @var BudgetModel */
protected $budget;
/** @var float */
protected $budgeted = 0;
/** @var float */
protected $left = 0;
/** @var float */
protected $overspent = 0;
/** @var LimitRepetition */
protected $repetition;
/** @var float */
protected $spent = 0;
/**
* @return BudgetModel
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param BudgetModel $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
}
/**
* @return float
*/
public function getBudgeted()
{
return $this->budgeted;
}
/**
* @param float $budgeted
*/
public function setBudgeted($budgeted)
{
$this->budgeted = $budgeted;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return float
*/
public function getOverspent()
{
return $this->overspent;
}
/**
* @param float $overspent
*/
public function setOverspent($overspent)
{
$this->overspent = $overspent;
}
/**
* @return LimitRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param LimitRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 16/05/15
* Time: 13:09
*/
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Category as CategoryModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Category
*
* @package FireflyIII\Helpers\Collection
*/
class Category
{
/** @var Collection */
protected $categories;
/** @var string */
protected $total = '0';
/**
*
*/
public function __construct()
{
$this->categories = new Collection;
}
/**
* @param CategoryModel $category
*/
public function addCategory(CategoryModel $category)
{
if ($category->spent > 0) {
$this->categories->push($category);
}
}
/**
* @param float $add
*/
public function addTotal($add)
{
$add = strval(round($add, 2));
bcscale(2);
$this->total = bcadd($this->total, $add);
}
/**
* @return Collection
*/
public function getCategories()
{
$this->categories->sortByDesc(
function (CategoryModel $category) {
return $category->spent;
}
);
return $this->categories;
}
/**
* @return string
*/
public function getTotal()
{
return strval(round($this->total, 2));
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
/**
* @codeCoverageIgnore
*
* Class Expense
*
* @package FireflyIII\Helpers\Collection
*/
class Expense
{
/** @var Collection */
protected $expenses;
/** @var string */
protected $total = '0';
/**
*
*/
public function __construct()
{
$this->expenses = new Collection;
}
/**
* @param TransactionJournal $entry
*/
public function addOrCreateExpense(TransactionJournal $entry)
{
$accountId = $entry->account_id;
if (!$this->expenses->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = strval(round($entry->amount, 2));
$newObject->name = $entry->name;
$newObject->count = 1;
$newObject->id = $accountId;
$this->expenses->put($accountId, $newObject);
} else {
bcscale(2);
$existing = $this->expenses->get($accountId);
$existing->amount = bcadd($existing->amount, $entry->amount);
$existing->count++;
$this->expenses->put($accountId, $existing);
}
}
/**
* @param $add
*/
public function addToTotal($add)
{
$add = strval(round($add, 2));
bcscale(2);
$this->total = bcadd($this->total, $add);
}
/**
* @return Collection
*/
public function getExpenses()
{
$this->expenses->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $this->expenses;
}
/**
* @return string
*/
public function getTotal()
{
return strval(round($this->total, 2));
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
/**
* @codeCoverageIgnore
*
* Class Income
*
* @package FireflyIII\Helpers\Collection
*/
class Income
{
/** @var Collection */
protected $incomes;
/** @var float */
protected $total;
/**
*
*/
public function __construct()
{
$this->incomes = new Collection;
}
/**
* @param TransactionJournal $entry
*/
public function addOrCreateIncome(TransactionJournal $entry)
{
$accountId = $entry->account_id;
if (!$this->incomes->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = strval(round($entry->amount, 2));
$newObject->name = $entry->name;
$newObject->count = 1;
$newObject->id = $accountId;
$this->incomes->put($accountId, $newObject);
} else {
bcscale(2);
$existing = $this->incomes->get($accountId);
$existing->amount = bcadd($existing->amount, $entry->amount);
$existing->count++;
$this->incomes->put($accountId, $existing);
}
}
/**
* @param $add
*/
public function addToTotal($add)
{
$add = strval(round($add, 2));
bcscale(2);
$this->total = bcadd($this->total, $add);
}
/**
* @return Collection
*/
public function getIncomes()
{
$this->incomes->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $this->incomes;
}
/**
* @return float
*/
public function getTotal()
{
return strval(round($this->total, 2));
}
}

View File

@@ -17,6 +17,8 @@ class Help implements HelpInterface
{
/**
* @codeCoverageIgnore
*
* @param $key
*
* @return string
@@ -27,13 +29,15 @@ class Help implements HelpInterface
}
/**
* @codeCoverageIgnore
*
* @param $route
*
* @return array
*/
public function getFromGithub($route)
{
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md';
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => $route,
@@ -54,6 +58,8 @@ class Help implements HelpInterface
}
/**
* @codeCoverageIgnore
*
* @param $route
*
* @return bool
@@ -64,6 +70,8 @@ class Help implements HelpInterface
}
/**
* @codeCoverageIgnore
*
* @param $route
* @param array $content
*
@@ -76,6 +84,8 @@ class Help implements HelpInterface
}
/**
* @codeCoverageIgnore
*
* @param $route
*
* @return bool

View File

@@ -33,7 +33,7 @@ class ReminderHelper implements ReminderHelperInterface
$ranges = $this->getReminderRanges($piggyBank, $start);
$currentRep = $piggyBank->currentRelevantRep();
$left = $piggyBank->targetamount - $currentRep->currentamount;
$perReminder = $left / count($ranges);
$perReminder = count($ranges) == 0 ? $left : $left / count($ranges);
} else {
$perReminder = null;
$ranges = [];
@@ -46,8 +46,6 @@ class ReminderHelper implements ReminderHelperInterface
'leftToSave' => $left,
];
// create one:
$reminder = new Reminder;
$reminder->user()->associate(Auth::user());
$reminder->startdate = $start;
@@ -65,6 +63,30 @@ class ReminderHelper implements ReminderHelperInterface
}
}
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date)
{
$ranges = $this->getReminderRanges($piggyBank);
foreach ($ranges as $range) {
if ($date < $range['end'] && $date > $range['start']) {
// create a reminder here!
$this->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
}
/**
* This routine will return an array consisting of two dates which indicate the start
* and end date for each reminder that this piggy bank will have, if the piggy bank has

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Reminders;
use Carbon\Carbon;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
/**
@@ -49,4 +50,15 @@ interface ReminderHelperInterface
* @return Reminder
*/
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end);
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date);
}

View File

@@ -3,12 +3,23 @@
namespace FireflyIII\Helpers\Report;
use App;
use Auth;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\BalanceEntry;
use FireflyIII\Helpers\Collection\BalanceHeader;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collection\Bill as BillCollection;
use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Steam;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
/**
* Class ReportHelper
@@ -18,66 +29,348 @@ use Steam;
class ReportHelper implements ReportHelperInterface
{
/**
* This methods fails to take in account transfers FROM shared accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
*
* @return Collection
*/
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15)
{
$result = $this->_queries->journalsByExpenseAccount($start, $end);
$array = $this->_helper->makeArray($result);
$limited = $this->_helper->limitArray($array, $limit);
/** @var ReportQueryInterface */
protected $query;
return $limited;
/**
* @codeCoverageIgnore
*
* @param ReportQueryInterface $query
*
*/
public function __construct(ReportQueryInterface $query)
{
$this->query = $query;
}
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
*
* @param Carbon $date
* @param Carbon $end
* @param $shared
*
* @return Account
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared)
{
$accounts = $this->query->getAllAccounts($date, $end, $shared);
$start = 0;
$end = 0;
$diff = 0;
// remove cash account, if any:
$accounts = $accounts->filter(
function (Account $account) {
if ($account->accountType->type != 'Cash account') {
return $account;
}
} // @codeCoverageIgnore
);
// summarize:
foreach ($accounts as $account) {
$start += $account->startBalance;
$end += $account->endBalance;
$diff += ($account->endBalance - $account->startBalance);
}
$object = new AccountCollection;
$object->setStart($start);
$object->setEnd($end);
$object->setDifference($diff);
$object->setAccounts($accounts);
return $object;
}
/**
*
* The balance report contains a Balance object which in turn contains:
*
* A BalanceHeader object which contains all relevant user asset accounts for the report.
*
* A number of BalanceLine objects, which hold:
* - A budget
* - A number of BalanceEntry objects.
*
* The BalanceEntry object holds:
* - The same budget (again)
* - A user asset account as mentioned in the BalanceHeader
* - The amount of money spent on the budget by the user asset account
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared)
{
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface');
$balance = new Balance;
// build a balance header:
$header = new BalanceHeader;
$accounts = $this->query->getAllAccounts($start, $end, $shared);
$budgets = $repository->getBudgets();
foreach ($accounts as $account) {
$header->addAccount($account);
}
/** @var BudgetModel $budget */
foreach ($budgets as $budget) {
$line = new BalanceLine;
$line->setBudget($budget);
// get budget amount for current period:
$rep = $repository->getCurrentRepetition($budget, $start);
$line->setRepetition($rep);
// loop accounts:
foreach ($accounts as $account) {
$balanceEntry = new BalanceEntry;
$balanceEntry->setAccount($account);
// get spent:
$spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant.
$balanceEntry->setSpent($spent);
$line->addBalanceEntry($balanceEntry);
}
// add line to balance:
$balance->addBalanceLine($line);
}
// then a new line for without budget.
// and one for the tags:
$empty = new BalanceLine;
$tags = new BalanceLine;
$diffLine = new BalanceLine;
$tags->setRole(BalanceLine::ROLE_TAGROLE);
$diffLine->setRole(BalanceLine::ROLE_DIFFROLE);
foreach ($accounts as $account) {
$spent = $this->query->spentNoBudget($account, $start, $end);
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
$diff = $spent + $left;
// budget
$budgetEntry = new BalanceEntry;
$budgetEntry->setAccount($account);
$budgetEntry->setSpent($spent);
$empty->addBalanceEntry($budgetEntry);
// balanced by tags
$tagEntry = new BalanceEntry;
$tagEntry->setAccount($account);
$tagEntry->setLeft($left);
$tags->addBalanceEntry($tagEntry);
// difference:
$diffEntry = new BalanceEntry;
$diffEntry->setAccount($account);
$diffEntry->setSpent($diff);
$diffLine->addBalanceEntry($diffEntry);
}
$balance->addBalanceLine($empty);
$balance->addBalanceLine($tags);
$balance->addBalanceLine($diffLine);
$balance->setBalanceHeader($header);
return $balance;
}
/**
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end, $shared)
{
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$bills = $repository->getBills();
$collection = new BillCollection;
/** @var Bill $bill */
foreach ($bills as $bill) {
$billLine = new BillLine;
$billLine->setBill($bill);
$billLine->setActive(intval($bill->active) == 1);
$billLine->setMin(floatval($bill->amount_min));
$billLine->setMax(floatval($bill->amount_max));
// is hit in period?
$set = $repository->getJournalsInRange($bill, $start, $end);
if ($set->count() == 0) {
$billLine->setHit(false);
} else {
$billLine->setHit(true);
$amount = 0;
foreach ($set as $entry) {
$amount += $entry->amount;
}
$billLine->setAmount($amount);
}
$collection->addBill($billLine);
}
return $collection;
}
/**
* This method gets some kind of list for a monthly overview.
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @param Carbon $date
* @param bool $showSharedReports
*
* @return Collection
* @return BudgetCollection
*/
public function getBudgetsForMonth(Carbon $date, $showSharedReports = false)
public function getBudgetReport(Carbon $start, Carbon $end, $shared)
{
/** @var \FireflyIII\Helpers\Report\ReportQueryInterface $query */
$query = App::make('FireflyIII\Helpers\Report\ReportQueryInterface');
$object = new BudgetCollection;
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC')
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as queryAmount']);
foreach ($set as $budget) {
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['queryAmount'] = isset($budgets[0]['queryAmount']) ? $budgets[0]['queryAmount'] : 0.0;
$budgets[0]['name'] = 'No budget';
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
$spent = $repository->spentInPeriodCorrected($budget, $start, $end, $shared);
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setOverspent($spent);
$object->addOverspent($spent);
$object->addBudgetLine($budgetLine);
continue;
}
// one or more repetitions for budget:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setRepetition($repetition);
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, $shared);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$budgetLine->setLeft($left);
$budgetLine->setSpent($spent);
$budgetLine->setOverspent($overspent);
$budgetLine->setBudgeted($repetition->amount);
$object->addBudgeted($repetition->amount);
$object->addSpent($spent);
$object->addLeft($left);
$object->addOverspent($overspent);
$object->addBudgetLine($budgetLine);
}
// find transactions to shared asset accounts, which are without a budget by default:
// which is only relevant when shared asset accounts are hidden.
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end)->sum('queryAmount');
$budgets[0]['spent'] += floatval($transfers) * -1;
}
return $budgets;
// stuff outside of budgets:
$noBudget = $repository->getWithoutBudgetSum($start, $end);
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
{
$object = new CategoryCollection;
/**
* GET CATEGORIES:
*/
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
$set = $repository->getCategories();
foreach ($set as $category) {
$spent = $repository->spentInPeriodCorrected($category, $start, $end, $shared);
$category->spent = $spent;
$object->addCategory($category);
$object->addTotal($spent);
}
return $object;
}
/**
* Get a full report on the users expenses during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared)
{
$object = new Expense;
$set = $this->query->expenseInPeriodCorrected($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateExpense($entry);
}
return $object;
}
/**
* Get a full report on the users incomes during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared)
{
$object = new Income;
$set = $this->query->incomeInPeriodCorrected($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateIncome($entry);
}
return $object;
}
/**
@@ -87,13 +380,14 @@ class ReportHelper implements ReportHelperInterface
*/
public function listOfMonths(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$months = [];
while ($start <= $end) {
$year = $start->year;
$months[$year][] = [
'formatted' => $start->format('F Y'),
'formatted' => $start->formatLocalized('%B %Y'),
'month' => $start->month,
'year' => $year,
];
@@ -102,77 +396,4 @@ class ReportHelper implements ReportHelperInterface
return $months;
}
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$years = [];
while ($start <= $end) {
$years[] = $start->year;
$start->addYear();
}
$years[] = Carbon::now()->year;
// force the current year.
$years = array_unique($years);
return $years;
}
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date, $showSharedReports = false)
{
$start = clone $date;
$end = clone $date;
$sharedAccounts = [];
if ($showSharedReports === false) {
$sharedCollection = Auth::user()->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', '=', 'accountRole')
->where('account_meta.data', '=', json_encode('sharedAsset'))
->get(['accounts.id']);
foreach ($sharedCollection as $account) {
$sharedAccounts[] = $account->id;
}
}
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*'])
->filter(
function (Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;
}
return null;
}
);
$report = [];
$start->startOfYear()->subDay();
$end->endOfYear();
foreach ($accounts as $account) {
$startBalance = Steam::balance($account, $start);
$endBalance = Steam::balance($account, $end);
$report[] = [
'start' => $startBalance,
'end' => $endBalance,
'hide' => ($startBalance == 0 && $endBalance == 0),
'account' => $account,
'shared' => $account->accountRole == 'sharedAsset'
];
}
return $report;
}
}

View File

@@ -3,7 +3,12 @@
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use FireflyIII\Helpers\Collection\Account;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
/**
* Interface ReportHelperInterface
@@ -13,26 +18,78 @@ use Illuminate\Support\Collection;
interface ReportHelperInterface
{
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
*
* @param Carbon $date
* @param Carbon $end
* @param boolean $shared
*
* @return Account
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared);
/**
* This methods fails to take in account transfers FROM shared accounts.
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Collection
* @return Account
*/
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15);
public function getBillReport(Carbon $start, Carbon $end, $shared);
/**
* This method gets some kind of list for a monthly overview.
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @param Carbon $date
*
* @return Collection
* @return Balance
*/
public function getBudgetsForMonth(Carbon $date);
public function getBalanceReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared);
/**
* Get a full report on the users expenses during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared);
/**
* Get a full report on the users incomes during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared);
/**
* @param Carbon $date
@@ -41,18 +98,4 @@ interface ReportHelperInterface
*/
public function listOfMonths(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date);
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date, $showSharedReports = false);
}

View File

@@ -5,11 +5,10 @@ namespace FireflyIII\Helpers\Report;
use Auth;
use Carbon\Carbon;
use Crypt;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Steam;
@@ -21,101 +20,62 @@ use Steam;
*/
class ReportQuery implements ReportQueryInterface
{
/**
* This query retrieves a list of accounts that are active and not shared.
* See ReportQueryInterface::incomeInPeriodCorrected
*
* @param bool $showSharedReports
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*
*/
public function accountList($showSharedReports = false)
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
{
$query = Auth::user()->accounts();
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole");
}
)->where(
$query = $this->queryJournalsWithTransactions($start, $end);
if ($includeShared === false) {
$query->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedAsset"');
$query->orWhereNull('account_meta.data');
$query->where(
function (Builder $q) { // only get withdrawals not from a shared account
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) { // and transfers from a shared account.
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
}
);
} else {
$query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine.
}
$query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
->where('active', 1)
->orderBy('accounts.name', 'ASC');
$query->orderBy('transaction_journals.date');
return $query->get(['accounts.*']);
}
// get everything
$data = $query->get(
['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted']
);
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end)
{
$data->each(
function (TransactionJournal $journal) {
if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
}
);
$data = $data->filter(
function (TransactionJournal $journal) {
if ($journal->amount != 0) {
return $journal;
}
} // @codeCoverageIgnore
);
$set = TransactionJournal::
leftJoin('transaction_group_transaction_journal', 'transaction_group_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin(
'transaction_group_transaction_journal as otherFromGroup', function (JoinClause $join) {
$join->on('otherFromGroup.transaction_group_id', '=', 'transaction_group_transaction_journal.transaction_group_id')
->on('otherFromGroup.transaction_journal_id', '!=', 'transaction_journals.id');
}
)
->leftJoin('transaction_journals as otherJournals', 'otherJournals.id', '=', 'otherFromGroup.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'otherJournals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'otherJournals.id')
->before($end)->after($start)
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->get(
[
'transaction_journals.*',
'transactions.amount as queryAmount'
]
);
return $set;
}
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end)
{
return floatval($this->balancedTransactionsList($account, $start, $end)->sum('queryAmount'));
return $data;
}
/**
@@ -123,15 +83,15 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
* @param bool $includeShared
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false)
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false)
{
$query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
->accountTypeIn(['Default account', 'Asset account', 'Cash account']);
if ($showSharedReports === false) {
if ($includeShared === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
@@ -167,55 +127,26 @@ class ReportQuery implements ReportQueryInterface
return $set;
}
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `queryAmount`')]);
}
/**
* Get a list of transaction journals that have no budget, filtered for the specified account
* and the specified date range.
* This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
* will simply list the transaction journals only. This should allow any follow up counting to be accurate with
* regards to tags.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $query->get(['budgets.name', 'transactions.amount as queryAmount', 'transaction_journals.*']);
}
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
* @param bool $includeShared
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false)
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
if ($includeShared === false) {
// only get deposits not to a shared account
// and transfers to a shared account.
$query->where(
@@ -238,339 +169,77 @@ class ReportQuery implements ReportQueryInterface
// any deposit is fine.
$query->where('transaction_types.type', 'Deposit');
}
$query->groupBy('transaction_journals.id')->orderBy('transaction_journals.date');
$query->orderBy('transaction_journals.date');
// get everything, decrypt and return
// get everything
$data = $query->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
DB::Raw('SUM(`t_to`.`amount`) as `queryAmount`'),
'transaction_journals.date',
't_from.account_id as account_id',
'ac_from.name as name',
'ac_from.encrypted as account_encrypted'
]
['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted']
);
$data->each(
function (Model $object) {
$object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) AS `spent`')]);
}
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('queryAmount');
$data = $query->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `queryAmount`')]);
// decrypt data:
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// get all withdrawals not from a shared accounts
// and all transfers to a shared account
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
function (TransactionJournal $journal) {
if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
);
} else {
// any withdrawal goes:
$query->where('transaction_types.type', 'Withdrawal');
}
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('queryAmount', 'DESC');
$data = $query->get(['t_to.account_id as id', 'ac_to.name as name', 'ac_to.encrypted', DB::Raw('SUM(t_to.amount) as `queryAmount`')]);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// show queries where transfer type is deposit, and its not to a shared account
// or where its a transfer and its from a shared account (both count as incomes)
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"');
}
);
$data = $data->filter(
function (TransactionJournal $journal) {
if ($journal->amount != 0) {
return $journal;
}
);
} else {
// any deposit goes:
$query->where('transaction_types.type', 'Deposit');
}
$query->groupBy('t_from.account_id')->orderBy('queryAmount');
$data = $query->get(
['t_from.account_id as account_id', 'ac_from.name as name', 'ac_from.encrypted as encrypted', DB::Raw('SUM(t_from.amount) as `queryAmount`')]
);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
} // @codeCoverageIgnore
);
return $data;
}
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '"sharedAsset"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', Auth::user()->id)
->get(
['transaction_journals.id', 'transaction_journals.description', 'transactions.account_id', 'accounts.name',
'transactions.amount as queryAmount']
);
}
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->where('account_meta.data', '"sharedAsset"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('categories.name')
->get(
[
'categories.id',
'categories.name as name',
DB::Raw('SUM(`transactions`.`amount`) * -1 AS `queryAmount`')
]
);
}
/**
*
* This query will get all transaction journals and budget information for a specified account
* in a certain date range, where the transaction journal does not have a budget.
* There is no get() specified, this is up to the method itself.
* Covers tags
*
* @param Account $account
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Builder
* @return float
*/
protected function queryJournalsNoBudget(Account $account, Carbon $start, Carbon $end)
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
return floatval(
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->transactionTypes(['Withdrawal'])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->where('budget_transaction_journal.budget_id', $budget->id)
->get(['transaction_journals.*'])->sum('amount')
) * -1;
}
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
* @param bool $shared
*
* @return float
*/
public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false)
{
return floatval(
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->transactionTypes(['Withdrawal'])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount')
);
}
/**
@@ -609,5 +278,4 @@ class ReportQuery implements ReportQueryInterface
return $query;
}
}

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use Illuminate\Support\Collection;
/**
@@ -15,152 +16,66 @@ interface ReportQueryInterface
{
/**
* This query retrieves a list of accounts that are active and not shared.
* See ReportQueryInterface::incomeInPeriodCorrected
*
* @param bool $showSharedReports
* This method returns all "expense" journals in a certain period, which are both transfers to a shared account
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*
*/
public function accountList($showSharedReports = false);
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end);
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end);
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
/**
* Get a users accounts combined with various meta-data related to the start and end date.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
* @param bool $includeShared
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false);
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false);
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
* This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
* will simply list the transaction journals only. This should allow any follow up counting to be accurate with
* regards to tags.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*/
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
/**
* Covers tags as well.
*
* @param Account $account
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
* @return float
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end);
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end);
/**
* Get a list of transaction journals that have no budget, filtered for the specified account
* and the specified date range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
* @param bool $shared
*
* @return Collection
* @return float
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end);
public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false);
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end);
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end);
}

View File

@@ -22,13 +22,13 @@ use View;
class AccountController extends Controller
{
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts');
View::share('title', trans('firefly.accounts'));
}
/**
@@ -96,7 +96,7 @@ class AccountController extends Controller
{
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$openingBalance = $repository->openingBalanceTransaction($account);
@@ -137,7 +137,7 @@ class AccountController extends Controller
*/
public function index(AccountRepositoryInterface $repository, $what)
{
$subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what);
$subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$types = Config::get('firefly.accountTypesByIdentifier.' . $what);
$accounts = $repository->getAccounts($types);
@@ -170,7 +170,7 @@ class AccountController extends Controller
$subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type);
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$journals = $repository->getJournals($account, $page);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$subTitle = trans('firefly.details_for_' . $what, ['name' => $account->name]);
$journals->setPath('accounts/show/' . $account->id);

View File

@@ -38,6 +38,7 @@ class AuthController extends Controller
*
* @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\Registrar $registrar
*
* @codeCoverageIgnore
*
*/

View File

@@ -7,6 +7,7 @@ use Illuminate\Foundation\Auth\ResetsPasswords;
/**
* Class PasswordController
*
* @codeCoverageIgnore
* @package FireflyIII\Http\Controllers\Auth
*/
@@ -35,6 +36,8 @@ class PasswordController extends Controller
* @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwords
*
* @codeCoverageIgnore
*
*/
public function __construct(Guard $auth, PasswordBroker $passwords)
{

View File

@@ -7,7 +7,6 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input;
use Redirect;
@@ -24,59 +23,15 @@ class BillController extends Controller
{
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Bills');
View::share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o');
}
/**
* @param AccountRepositoryInterface $repository
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function add(AccountRepositoryInterface $repository, Bill $bill)
{
$matches = explode(',', $bill->match);
$description = [];
$expense = null;
// get users expense accounts:
$accounts = $repository->getAccounts(Config::get('firefly.accountTypesByIdentifier.expense'));
foreach ($matches as $match) {
$match = strtolower($match);
// find expense account for each word if not found already:
if (is_null($expense)) {
/** @var Account $account */
foreach ($accounts as $account) {
$name = strtolower($account->name);
if (!(strpos($name, $match) === false)) {
$expense = $account;
break;
}
}
}
if (is_null($expense)) {
$description[] = $match;
}
}
$parameters = [
'description' => ucfirst(join(' ', $description)),
'expense_account' => is_null($expense) ? '' : $expense->name,
'amount' => round(($bill->amount_min + $bill->amount_max), 2),
];
Session::put('preFilled', $parameters);
return Redirect::to(route('transactions.create', 'withdrawal'));
}
/**
* @return $this
*/

View File

@@ -24,12 +24,12 @@ class BudgetController extends Controller
{
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Budgets');
View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks');
View::share('hideBudgets', true);
}
@@ -60,7 +60,7 @@ class BudgetController extends Controller
Session::put('budgets.create.url', URL::previous());
}
Session::forget('budgets.create.fromStore');
$subTitle = 'Create a new budget';
$subTitle = trans('firefly.create_new_budget');
return view('budgets.create', compact('subTitle'));
}
@@ -137,7 +137,8 @@ class BudgetController extends Controller
$budgets->each(
function (Budget $budget) use ($repository) {
$date = Session::get('start', Carbon::now()->startOfMonth());
$budget->spent = $repository->spentInMonth($budget, $date);
$end = Session::get('end', Carbon::now()->endOfMonth());
$budget->spent = $repository->spentInPeriodCorrected($budget, $date, $end);
$budget->currentRep = $repository->getCurrentRepetition($budget, $date);
}
);

View File

@@ -21,12 +21,12 @@ class CategoryController extends Controller
{
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Categories');
View::share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart');
}

View File

@@ -0,0 +1,150 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class AccountController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class AccountController extends Controller
{
/**
* Shows the balances for all the user's accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, AccountRepositoryInterface $repository, $year, $month, $shared = false)
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
/** @var Collection $accounts */
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
if ($shared === false) {
// remove the shared accounts from the collection:
/** @var Account $account */
foreach ($accounts as $index => $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$accounts->forget($index);
}
}
}
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows the balances for all the user's frontpage accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, AccountRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows an account's balance for a single month.
*
* @param GChart $chart
* @param Account $account
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(GChart $chart, Account $account)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Response;
use Session;
use Steam;
/**
* Class BillController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class BillController extends Controller
{
/**
* Shows the overview for a bill. The min/max amount and matched journals.
*
* @param GChart $chart
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(GChart $chart, BillRepositoryInterface $repository, Bill $bill)
{
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.maxAmount'), 'number');
$chart->addColumn(trans('firefly.minAmount'), 'number');
$chart->addColumn(trans('firefly.billEntry'), 'number');
// get first transaction or today for start:
$results = $repository->getJournals($bill);
/** @var TransactionJournal $result */
foreach ($results as $result) {
$chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows all bills and whether or not theyve been paid this month (pie chart).
*
* @param GChart $chart
*
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{
$chart->addColumn(trans('firefly.name'), 'string');
$chart->addColumn(trans('firefly.amount'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$bills = $repository->getActiveBills();
$paid = new Collection; // journals.
$unpaid = new Collection; // bills
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
} else {
$paid = $paid->merge($journals);
}
}
}
$creditCards = $accounts->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, $end, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
// find transfer(s) TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
$paid = $paid->merge($journals);
}
}
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount += floatval($entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount += $amount;
unset($amount, $description);
}
$chart->addRow(trans('firefly.unpaid') . ': ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow(trans('firefly.paid') . ': ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,195 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Session;
/**
* Class BudgetController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class BudgetController extends Controller
{
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
*/
public function budget(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget)
{
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
$first = $repository->getFirstBudgetLimitDate($budget);
$range = $viewRange = Preferences::get('viewRange', '1M')->data;
$last = Session::get('end', new Carbon);
$final = clone $last;
$final->addYears(2);
$last = Navigation::endOfX($last, $range, $final);
while ($first < $last) {
$end = Navigation::addPeriod($first, $range, 0);
$spent = $repository->spentInPeriodCorrected($budget, $first, $end);
$chart->addRow($end, $spent);
$first = Navigation::addPeriod($first, $range, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows the amount left in a specific budget limit.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetLimit(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$chart->addColumn(trans('firefly.day'), 'date');
$chart->addColumn(trans('firefly.left'), 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDayCorrected($budget, $start);
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows a budget list with spent/left/overspent.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, BudgetRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.budget'), 'string');
$chart->addColumn(trans('firefly.left'), 'number');
$chart->addColumn(trans('firefly.spent'), 'number');
$chart->addColumn(trans('firefly.overspent'), 'number');
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->spentInPeriodCorrected($budget, $start, $end, true);
$allEntries->push([$budget->name, 0, 0, $expenses]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, true);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? floatval($repetition->amount) : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$allEntries->push(
[$budget->name . ' (' . $repetition->startdate->formatLocalized($this->monthAndDayFormat) . ')',
$left,
$spent,
$overspent
]
);
}
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Show a yearly overview for a budget.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(GChart $chart, BudgetRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$budgets = $repository->getBudgets();
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($budgets as $budget) {
$spent = $repository->spentInPeriodCorrected($budget, $start, $month, $shared);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Navigation;
use Preferences;
use Response;
use Session;
/**
* Class CategoryController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class CategoryController extends Controller
{
/**
* Show an overview for a category for all time, per month/week/year.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
$range = Preferences::get('viewRange', '1M')->data;
// jump to start of week / month / year / etc
$start = Navigation::startOfPeriod($start, $range);
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
$end = new Carbon;
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriodCorrected($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Show this month's category overview.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, CategoryRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.category'), 'string');
$chart->addColumn(trans('firefly.spent'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = $repository->getCategoriesAndExpensesCorrected($start, $end);
// sort by callback:
uasort(
$set,
function ($left, $right) {
if ($left['sum'] == $right['sum']) {
return 0;
}
return ($left['sum'] < $right['sum']) ? 1 : -1;
}
);
foreach ($set as $entry) {
$sum = floatval($entry['sum']);
if ($sum != 0) {
$chart->addRow($entry['name'], $sum);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function month(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$chart->addRow(clone $start, $spent);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* This chart will only show expenses.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(GChart $chart, CategoryRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$categories = $repository->getCategories();
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($categories as $category) {
$chart->addColumn($category->name, 'number');
}
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($categories as $category) {
$spent = $repository->spentInPeriodCorrected($category, $start, $month, $shared);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 16/05/15
* Time: 09:36
*/
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Response;
/**
* Class PiggyBankController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class PiggyBankController extends Controller
{
/**
* Shows the piggy bank history.
*
* @param GChart $chart
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function history(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.balance'), 'number');
/** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Controllers\Controller;
use Grumpydictator\Gchart\GChart;
use Response;
/**
* Class ReportController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class ReportController extends Controller
{
/**
* Summarizes all income and expenses, per month, for a given year.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInOut(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
{
// get start and end of year
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$chart->addColumn(trans('firefly.month'), 'date');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$incomeSum = floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
$expenseSum = floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Summarizes all income and expenses for a given year. Gives a total and an average.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInOutSummarized(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$income = 0;
$expense = 0;
$count = 0;
$chart->addColumn(trans('firefly.summary'), 'string');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$income += floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
$expense += floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
$count++;
$start->addMonth();
}
// add total + average:
$chart->addRow(trans('firefly.sum'), $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -1,8 +1,11 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Config;
use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Preferences;
use View;
/**
@@ -15,8 +18,13 @@ abstract class Controller extends BaseController
use DispatchesCommands, ValidatesRequests;
/** @var string */
protected $monthAndDayFormat;
/** @var string */
protected $monthFormat;
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
@@ -24,5 +32,16 @@ abstract class Controller extends BaseController
View::share('hideCategories', false);
View::share('hideBills', false);
View::share('hideTags', false);
if (Auth::check()) {
$pref = Preferences::get('language', 'en');
$lang = $pref->data;
$this->monthFormat = Config::get('firefly.month.' . $lang);
$this->monthAndDayFormat = Config::get('firefly.monthAndDay.' . $lang);
View::share('monthFormat', $this->monthFormat);
View::share('monthAndDayFormat', $this->monthAndDayFormat);
View::share('language', $lang);
}
}
}

View File

@@ -23,12 +23,12 @@ class CurrencyController extends Controller
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Currencies');
View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd');
}

View File

@@ -1,590 +0,0 @@
<?php namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class GoogleChartController
*
* @package FireflyIII\Http\Controllers
*/
class GoogleChartController extends Controller
{
/**
* @param GChart $chart
* @param Account $account
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function accountBalanceChart(GChart $chart, Account $account)
{
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allAccountsBalanceChart(GChart $chart, AccountRepositoryInterface $repository)
{
$chart->addColumn('Day of the month', 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allBudgetsAndSpending(GChart $chart, BudgetRepositoryInterface $repository, $year)
{
$budgets = $repository->getBudgets();
$chart->addColumn('Month', 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$spent = $repository->spentInMonth($budget, $start);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allBudgetsHomeChart(GChart $chart, BudgetRepositoryInterface $repository)
{
$chart->addColumn('Budget', 'string');
$chart->addColumn('Left', 'number');
$chart->addColumn('Spent', 'number');
$chart->addColumn('Overspent', 'number');
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->sumBudgetExpensesInPeriod($budget, $start, $end);
$allEntries->push([$budget->name, 0, 0, $expenses]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->sumBudgetExpensesInPeriod($budget, $repetition->startdate, $repetition->enddate);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$allEntries->push(
[$budget->name . ' (' . $repetition->startdate->format('j M Y') . ')',
$left,
$spent,
$overspent
]
);
}
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push(['(no budget)', 0, 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allCategoriesHomeChart(GChart $chart, CategoryRepositoryInterface $repository)
{
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = $repository->getCategoriesAndExpenses($start, $end);
foreach ($set as $entry) {
$isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$chart->addRow($name, floatval($entry->sum));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billOverview(GChart $chart, BillRepositoryInterface $repository, Bill $bill)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number');
$chart->addColumn('Min amount', 'number');
$chart->addColumn('Recorded bill entry', 'number');
// get first transaction or today for start:
$results = $repository->getJournals($bill);
/** @var TransactionJournal $result */
foreach ($results as $result) {
$chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
*
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billsOverview(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$bills = $repository->getActiveBills();
$paid = new Collection; // journals.
$unpaid = new Collection; // bills
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
} else {
$paid = $paid->merge($journals);
}
}
}
$creditCards = $accounts->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
// find transfer(s) TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
$paid = $paid->merge($journals);
}
}
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount += floatval($entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount += $amount;
unset($amount, $description);
}
$chart->addRow('Unpaid: ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow('Paid: ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetLimitSpending(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$chart->addColumn('Day', 'date');
$chart->addColumn('Left', 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDay($budget, $start);
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param int $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetsAndSpending(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, $year = 0)
{
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
if ($year == 0) {
$start = $repository->getFirstBudgetLimitDate($budget);
$end = $repository->getLastBudgetLimitDate($budget);
} else {
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
}
while ($start <= $end) {
$spent = $repository->spentInMonth($budget, $start);
$budgeted = $repository->getLimitAmountOnDate($budget, $start);
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function categoryOverviewChart(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
/** @var Preference $range */
$range = Preferences::get('viewRange', '1M');
// jump to start of week / month / year / etc (TODO).
$start = Navigation::startOfPeriod($start, $range->data);
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$end = new Carbon;
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range->data);
$spent = $repository->spentInPeriodSum($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range->data, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function categoryPeriodChart(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$end = Session::get('end', Carbon::now()->endOfMonth());
while ($start <= $end) {
$spent = $repository->spentOnDaySum($category, $start);
$chart->addRow(clone $start, $spent);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function piggyBankHistory(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number');
/** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInExp(GChart $chart, ReportQueryInterface $query, $year)
{
$start = new Carbon('01-01-' . $year);
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
// get report query interface.
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income && total expenses:
$incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInExpSum(GChart $chart, ReportQueryInterface $query, $year)
{
$start = new Carbon('01-01-' . $year);
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$income = 0;
$expense = 0;
$count = 0;
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
// total expenses:
$expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$income += $incomeSum;
$expense += $expenseSum;
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -6,6 +6,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Preferences;
use Redirect;
use Route;
use Session;
use Steam;
@@ -53,7 +54,7 @@ class HomeController extends Controller
$types = Config::get('firefly.accountTypesByIdentifier.asset');
$count = $repository->countAccounts($types);
$title = 'Firefly';
$subTitle = 'What\'s playing?';
$subTitle = trans('firefly.welcomeBack');
$mainTitleIcon = 'fa-fire';
$transactions = [];
$frontPage = Preferences::get('frontPageAccounts', []);
@@ -63,13 +64,11 @@ class HomeController extends Controller
$savings = $repository->getSavingsAccounts();
$piggyBankAccounts = $repository->getPiggyBankAccounts();
$savingsTotal = 0;
foreach ($savings as $savingAccount) {
$savingsTotal += Steam::balance($savingAccount, $end);
}
// check if all books are correct.
$sum = $repository->sumOfEverything();
if ($sum != 0) {
Session::flash(
@@ -89,5 +88,78 @@ class HomeController extends Controller
return view('index', compact('count', 'title', 'savings', 'subTitle', 'mainTitleIcon', 'transactions', 'savingsTotal', 'piggyBankAccounts'));
}
/**
* @codeCoverageIgnore
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function routes()
{
$directory = '/vagrant_data/Sites/firefly-iii-help';
$languages = array_keys(Config::get('firefly.lang'));
$routes = [];
$ignored = [
'debugbar.openhandler', 'debugbar.assets.css', 'debugbar.assets.js', 'register', 'routes', 'daterange',
'flush', 'delete-account-post', 'change-password-post', 'logout', 'login', 'tags.hideTagHelp',
'budgets.postIncome', 'flush'
];
$ignoreMatch = ['.store', '.update', '.destroy', 'json.'];
$routeCollection = Route::getRoutes();
/** @var \Illuminate\Routing\Route $object */
foreach ($routeCollection as $object) {
// get name:
$name = $object->getName();
// has name and not in ignore list?
if (strlen($name) > 0 && !in_array($name, $ignored)) {
// not in ignoreMatch?
$continue = true;
foreach ($ignoreMatch as $ignore) {
$match = strpos($name, $ignore);
if (!($match === false)) {
$continue = false;
}
}
unset($ignore, $match);
if ($continue) {
$routes[] = $name;
// check all languages:
foreach ($languages as $lang) {
$file = $directory . '/' . $lang . '/' . $name . '.md';
if (!file_exists($file)) {
touch($file);
echo $name . '<br />';
}
}
}
}
}
// loop directories with language file.
// tag the ones not in the list of approved routes.
foreach ($languages as $lang) {
$dir = $directory . '/' . $lang;
$set = scandir($dir);
foreach ($set as $entry) {
if ($entry != '.' && $entry != '..') {
$name = str_replace('.md', '', $entry);
if (!in_array($name, $routes)) {
$file = $dir . '/' . $entry;
unlink($file);
}
}
}
}
echo 'Done!';
}
}

View File

@@ -12,7 +12,6 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
@@ -39,19 +38,14 @@ class JsonController extends Controller
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = 0;
// these two functions are the same as the chart TODO
// these two functions are the same as the chart
$bills = $repository->getActiveBills();
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$amount += $repository->getJournalsInRange($bill, $range['start'], $range['end'])->sum('amount');
}
$amount += $repository->billPaymentsInRange($bill, $start, $end);
}
unset($ranges, $bill, $range, $bills);
unset($bill, $bills);
/**
* Find credit card accounts and possibly unpaid credit card bills.
@@ -60,7 +54,7 @@ class JsonController extends Controller
// if the balance is not zero, the monthly payment is still underway.
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$balance = Steam::balance($creditCard, $end, true);
if ($balance == 0) {
// find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
@@ -100,7 +94,7 @@ class JsonController extends Controller
$creditCards = $accountRepository->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$balance = Steam::balance($creditCard, $end, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
@@ -128,7 +122,12 @@ class JsonController extends Controller
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->incomeByPeriod($start, $end, true)->sum('queryAmount');
$amount = $reportQuery->incomeInPeriodCorrected($start, $end, true)->sum('amount');
// $amount = 0;
// foreach($set as $entry) {
// //echo $entry->description.' ('.$entry->tags->count().'): ' . $entry->amount."\n";
// $amount += $entry->amount;
// }
return Response::json(['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
}
@@ -142,7 +141,7 @@ class JsonController extends Controller
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->journalsByExpenseAccount($start, $end, true)->sum('queryAmount');
$amount = $reportQuery->expenseInPeriodCorrected($start, $end, true)->sum('amount');
return Response::json(['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
}
@@ -202,30 +201,6 @@ class JsonController extends Controller
}
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function setSharedReports()
{
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$new = !$pref->data;
Preferences::set('showSharedReports', $new);
return Response::json(['value' => $new]);
}
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showSharedReports()
{
$pref = Preferences::get('showSharedReports', false);
return Response::json(['value' => $pref->data]);
}
/**
* Returns a JSON list of all beneficiaries.
*

View File

@@ -26,12 +26,12 @@ class PiggyBankController extends Controller
{
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Piggy banks');
View::share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
@@ -45,7 +45,8 @@ class PiggyBankController extends Controller
*/
public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank)
{
$leftOnAccount = $repository->leftOnAccount($piggyBank->account);
$date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $repository->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave);
@@ -157,6 +158,7 @@ class PiggyBankController extends Controller
{
/** @var Collection $piggyBanks */
$piggyBanks = $piggyRepository->getPiggyBanks();
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = [];
/** @var PiggyBank $piggyBank */
@@ -172,8 +174,8 @@ class PiggyBankController extends Controller
if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [
'name' => $account->name,
'balance' => Steam::balance($account, null, true),
'leftForPiggyBanks' => $repository->leftOnAccount($account),
'balance' => Steam::balance($account, $end, true),
'leftForPiggyBanks' => $repository->leftOnAccount($account, $end),
'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount),
'leftToSave' => $piggyBank->leftToSave
@@ -215,7 +217,8 @@ class PiggyBankController extends Controller
public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank)
{
$amount = round(floatval(Input::get('amount')), 2);
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account);
$date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Http\Controllers;
use Config;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Preferences;
@@ -16,12 +17,12 @@ class PreferencesController extends Controller
{
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Preferences');
View::share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear');
}
@@ -37,9 +38,11 @@ class PreferencesController extends Controller
$viewRange = $viewRangePref->data;
$frontPageAccounts = Preferences::get('frontPageAccounts', []);
$budgetMax = Preferences::get('budgetMaximum', 1000);
$languagePref = Preferences::get('language', 'en');
$language = $languagePref->data;
$budgetMaximum = $budgetMax->data;
return view('preferences.index', compact('budgetMaximum', 'accounts', 'frontPageAccounts', 'viewRange'));
return view('preferences.index', compact('budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange'));
}
/**
@@ -49,10 +52,12 @@ class PreferencesController extends Controller
{
// front page accounts
$frontPageAccounts = [];
foreach (Input::get('frontPageAccounts') as $id) {
$frontPageAccounts[] = intval($id);
if (is_array(Input::get('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'));
@@ -65,6 +70,12 @@ class PreferencesController extends Controller
$budgetMaximum = intval(Input::get('budgetMaximum'));
Preferences::set('budgetMaximum', $budgetMaximum);
// language:
$lang = Input::get('language');
if (in_array($lang, array_keys(Config::get('firefly.lang')))) {
Preferences::set('language', $lang);
}
Session::flash('success', 'Preferences saved!');

View File

@@ -2,13 +2,13 @@
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionJournal;
use Preferences;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Session;
use Steam;
use View;
/**
@@ -21,311 +21,134 @@ class ReportController extends Controller
/** @var ReportHelperInterface */
protected $helper;
/** @var ReportQueryInterface */
protected $query;
/**
* @codeCoverageIgnore
*
* @param ReportHelperInterface $helper
* @param ReportQueryInterface $query
*/
public function __construct(ReportHelperInterface $helper, ReportQueryInterface $query)
public function __construct(ReportHelperInterface $helper)
{
$this->query = $query;
parent::__construct();
$this->helper = $helper;
View::share('title', 'Reports');
View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart');
}
/**
* @param string $year
* @param string $month
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\View\View
*/
public function budget($year = '2014', $month = '1')
{
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
// should show shared reports?
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$accountAmounts = []; // array with sums of spent amounts on each account.
$accounts = $this->query->getAllAccounts($start, $end, $showSharedReports); // all accounts and some data.
foreach ($accounts as $account) {
$budgets = $this->query->getBudgetSummary($account, $start, $end);// get budget summary for this account:
$balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end);
$accountAmounts[$account->id] = $balancedAmount;
// balance out the transactions (see transaction groups & tags) ^^
// array with budget information for each account:
$array = [];
// should always hide account
$hide = true;
// loop all budgets
/** @var \FireflyIII\Models\Budget $budget */
foreach ($budgets as $budget) {
$id = intval($budget->id);
$data = $budget->toArray();
$array[$id] = $data;
// no longer hide account if any budget has money in it.
if (floatval($data['queryAmount']) != 0) {
$hide = false;
}
$accountAmounts[$account->id] += $data['queryAmount'];
}
$account->hide = $hide;
$account->budgetInformation = $array;
$account->balancedAmount = $balancedAmount;
}
/**
* Start getBudgetsForMonth DONE
*/
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
*/
return view('reports.budget', compact('subTitle', 'accountAmounts', 'year', 'month', 'subTitleIcon', 'date', 'accounts', 'budgets'));
}
/**
* @return View
* @internal param ReportHelperInterface $helper
*
*/
public function index()
public function index(AccountRepositoryInterface $repository)
{
$start = Session::get('first');
$months = $this->helper->listOfMonths($start);
$years = $this->helper->listOfYears($start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
$start = Session::get('first');
$months = $this->helper->listOfMonths($start);
return view('reports.index', compact('years', 'months', 'title', 'mainTitleIcon'));
}
// does the user have shared accounts?
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
$hasShared = false;
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $this->query->balancedTransactionsList($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return View
* @internal param ReportQueryInterface $query
*
*/
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$set = $this->query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $set->filter(
function (TransactionJournal $journal) {
$count = $journal->transactiongroups()->where('relation', 'balance')->count();
if ($count == 0) {
return $journal;
}
return null;
/** @var Account $account */
foreach ($accounts as $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$hasShared = true;
}
);
}
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function modalNoBudget(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $this->query->getTransactionsWithoutBudget($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
return view('reports.index', compact('months', 'hasShared'));
}
/**
* @param string $year
* @param string $month
*
* @param bool $shared
*
* @return \Illuminate\View\View
*/
public function month($year = '2014', $month = '1')
public function month($year = '2014', $month = '1', $shared = false)
{
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report.
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$start = new Carbon($year . '-' . $month . '-01');
$subTitle = trans('firefly.reportForMonth', ['date' => $start->formatLocalized($this->monthFormat)]);
$subTitleIcon = 'fa-calendar';
$end = clone $start;
$incomeTopLength = 8;
$expenseTopLength = 8;
if ($shared == 'shared') {
$shared = true;
$subTitle = trans('firefly.reportForMonthShared', ['date' => $start->formatLocalized($this->monthFormat)]);
}
/**
*
* get income for month (date)
*
*/
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
/**
* Start getIncomeForMonth DONE
*/
$income = $this->query->incomeByPeriod($start, $end, $showSharedReports);
/**
* End getIncomeForMonth DONE
*/
/**
* Start getExpenseGroupedForMonth DONE
*/
$set = $this->query->journalsByExpenseAccount($start, $end, $showSharedReports);
$expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10);
/**
* End getExpenseGroupedForMonth DONE
*/
/**
* Start getBudgetsForMonth DONE
*/
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
*/
/**
* Start getCategoriesForMonth DONE
*/
// all categories.
$result = $this->query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result);
// all transfers
if ($showSharedReports === false) {
$result = $this->query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
} else {
$merged = $categories;
}
// sort.
$sorted = Steam::sortNegativeArray($merged);
// limit to $limit:
$categories = Steam::limitArray($sorted, 10);
/**
* End getCategoriesForMonth DONE
*/
/**
* Start getAccountsForMonth
*/
$list = $this->query->accountList($showSharedReports);
$accounts = [];
/** @var Account $account */
foreach ($list as $account) {
$id = intval($account->id);
/** @noinspection PhpParamsInspection */
$accounts[$id] = [
'name' => $account->name,
'startBalance' => Steam::balance($account, $start),
'endBalance' => Steam::balance($account, $end)
];
$accounts[$id]['difference'] = $accounts[$id]['endBalance'] - $accounts[$id]['startBalance'];
}
/**
* End getAccountsForMonth
*/
$accounts = $this->helper->getAccountReport($start, $end, $shared);
$incomes = $this->helper->getIncomeReport($start, $end, $shared);
$expenses = $this->helper->getExpenseReport($start, $end, $shared);
$budgets = $this->helper->getBudgetReport($start, $end, $shared);
$categories = $this->helper->getCategoryReport($start, $end, $shared);
$balance = $this->helper->getBalanceReport($start, $end, $shared);
$bills = $this->helper->getBillReport($start, $end, $shared);
return view(
'reports.month',
compact(
'income', 'expenses', 'budgets', 'accounts', 'categories',
'date', 'subTitle', 'displaySum', 'subTitleIcon'
'start', 'shared',
'subTitle', 'subTitleIcon',
'accounts',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills'
)
);
}
/**
* @param $year
* @param $year
*
* @param bool $shared
*
* @return $this
*/
public function year($year)
public function year($year, $shared = false)
{
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$subTitle = trans('firefly.reportForYear', ['year' => $year]);
$subTitleIcon = 'fa-bar-chart';
$incomeTopLength = 8;
$expenseTopLength = 8;
if ($shared == 'shared') {
$shared = true;
$subTitle = trans('firefly.reportForYearShared', ['year' => $year]);
}
$end->endOfYear();
$title = 'Reports';
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart';
$balances = $this->helper->yearBalanceReport($date, $showSharedReports);
$groupedIncomes = $this->query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $this->query->journalsByExpenseAccount($date, $end, $showSharedReports);
$accounts = $this->helper->getAccountReport($start, $end, $shared);
$incomes = $this->helper->getIncomeReport($start, $end, $shared);
$expenses = $this->helper->getExpenseReport($start, $end, $shared);
return view(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')
'reports.year',
compact(
'start', // the date for this report.
'shared', // is a shared report?
'accounts', // all accounts
'incomes', 'expenses', // expenses and incomes.
'subTitle', 'subTitleIcon', // subtitle and subtitle icon.
'incomeTopLength', // length of income top X
'expenseTopLength' // length of expense top X.
)
);
}

View File

@@ -32,7 +32,7 @@ use View;
class TagController extends Controller
{
/**
*
* @codeCoverageIgnore
*/
public function __construct()
{
@@ -133,7 +133,7 @@ class TagController extends Controller
* changes to an advancePayment.
*/
if ($tag->tagMode == 'balancingAct') {
if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') {
foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Transfer') {
$allowToAdvancePayment = false;
@@ -289,9 +289,9 @@ class TagController extends Controller
public function update(TagFormRequest $request, TagRepositoryInterface $repository, Tag $tag)
{
if (Input::get('setTag') == 'true') {
$latitude = strlen($request->get('latitude')) > 0 ? $request->get('latitude') : null;
$longitude = strlen($request->get('longitude')) > 0 ? $request->get('longitude') : null;
$zoomLevel = strlen($request->get('zoomLevel')) > 0 ? $request->get('zoomLevel') : null;
$latitude = $request->get('latitude');
$longitude = $request->get('longitude');
$zoomLevel = $request->get('zoomLevel');
} else {
$latitude = null;
$longitude = null;

View File

@@ -26,11 +26,12 @@ use View;
class TransactionController extends Controller
{
/**
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Transactions');
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
}
@@ -44,17 +45,15 @@ class TransactionController extends Controller
{
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)';
$budgets[0] = trans('form.noBudget');
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)';
$piggies[0] = trans('form.noPiggybank');
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id'];
$subTitle = 'Add a new ' . e($what);
$subTitle = trans('form.add_new_' . $what);
foreach ($respondTo as $r) {
if (!is_null(Input::get($r))) {
$preFilled[$r] = Input::get($r);
}
$preFilled[$r] = Input::get($r);
}
Session::put('preFilled', $preFilled);
@@ -119,10 +118,11 @@ class TransactionController extends Controller
$what = strtolower($journal->transactiontype->type);
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)';
$budgets[0] = trans('form.noBudget');
$transactions = $journal->transactions()->orderBy('amount', 'DESC')->get();
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)';
$piggies[0] = trans('form.noPiggybank');
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$preFilled = [
'date' => $journal->date->format('Y-m-d'),
'category' => '',
@@ -150,7 +150,7 @@ class TransactionController extends Controller
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id;
}
$preFilled['amount'] = $journal->amount;
$preFilled['amount'] = $journal->actualAmount;
$preFilled['account_id'] = $journal->assetAccount->id;
$preFilled['expense_account'] = $transactions[0]->account->name;
$preFilled['revenue_account'] = $transactions[1]->account->name;
@@ -182,19 +182,19 @@ class TransactionController extends Controller
case 'expenses':
case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses';
$subTitle = trans('firefly.expenses');
$types = ['Withdrawal'];
break;
case 'revenue':
case 'deposit':
$subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits';
$subTitle = trans('firefly.income');
$types = ['Deposit'];
break;
case 'transfer':
case 'transfers':
$subTitleIcon = 'fa-exchange';
$subTitle = 'Transfers';
$subTitle = trans('firefly.transfers');
$types = ['Transfer'];
break;
}
@@ -249,7 +249,7 @@ class TransactionController extends Controller
$t->after = $t->before + $t->amount;
}
);
$subTitle = e($journal->transactiontype->type) . ' "' . e($journal->description) . '"';
$subTitle = trans('firefly.' . $journal->transactiontype->type) . ' "' . e($journal->description) . '"';
return view('transactions.show', compact('journal', 'subTitle'));
}
@@ -269,7 +269,9 @@ class TransactionController extends Controller
// rescan journal, UpdateJournalConnection
event(new JournalSaved($journal));
// ConnectJournalToPiggyBank
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) {
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
}
$repository->deactivateReminder($request->get('reminder_id'));

View File

@@ -37,6 +37,7 @@ class Kernel extends HttpKernel
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated',
'range' => 'FireflyIII\Http\Middleware\Range',
'cleanup' => 'FireflyIII\Http\Middleware\Cleanup',
'reminders' => 'FireflyIII\Http\Middleware\Reminders',
'piggybanks' => 'FireflyIII\Http\Middleware\PiggyBanks',

View File

@@ -1,8 +1,12 @@
<?php namespace FireflyIII\Http\Middleware;
use App;
use Closure;
use Config;
use FireflyIII\Models\Preference;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Preferences;
/**
* Class Authenticate
@@ -48,6 +52,11 @@ class Authenticate
return redirect()->guest('auth/login');
}
}
// if logged in, set user language:
$pref = Preferences::get('language', 'en');
App::setLocale($pref->data);
setlocale(LC_TIME, Config::get('firefly.locales.' . $pref->data));
return $next($request);
}

View File

@@ -0,0 +1,283 @@
<?php namespace FireflyIII\Http\Middleware;
use Closure;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Session;
/**
* Class Cleanup
*
* @codeCoverageIgnore
* @package FireflyIII\Http\Middleware
*/
class Cleanup
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
*
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
}
}
$run = env('RUNCLEANUP') == 'true' ? true : false;
$count = 0;
if ($run) {
// encrypt account name
$set = Account::where('encrypted', 0)->take(5)->get();
/** @var Account $entry */
foreach ($set as $entry) {
$count++;
$name = $entry->name;
$entry->name = $name;
$entry->save();
}
unset($set, $entry, $name);
// encrypt bill name
$set = Bill::where('name_encrypted', 0)->take(5)->get();
/** @var Bill $entry */
foreach ($set as $entry) {
$count++;
$name = $entry->name;
$entry->name = $name;
$entry->save();
}
unset($set, $entry, $name);
// encrypt bill match
$set = Bill::where('match_encrypted', 0)->take(5)->get();
/** @var Bill $entry */
foreach ($set as $entry) {
$match = $entry->match;
$entry->match = $match;
$entry->save();
}
unset($set, $entry, $match);
// encrypt budget name
$set = Budget::where('encrypted', 0)->take(5)->get();
/** @var Budget $entry */
foreach ($set as $entry) {
$count++;
$name = $entry->name;
$entry->name = $name;
$entry->save();
}
unset($set, $entry, $name);
// encrypt category name
$set = Category::where('encrypted', 0)->take(5)->get();
/** @var Category $entry */
foreach ($set as $entry) {
$count++;
$name = $entry->name;
$entry->name = $name;
$entry->save();
}
unset($set, $entry, $name);
// encrypt piggy bank name
$set = PiggyBank::where('encrypted', 0)->take(5)->get();
/** @var PiggyBank $entry */
foreach ($set as $entry) {
$count++;
$name = $entry->name;
$entry->name = $name;
$entry->save();
}
unset($set, $entry, $name);
// encrypt transaction journal description
$set = TransactionJournal::where('encrypted', 0)->take(5)->get();
/** @var TransactionJournal $entry */
foreach ($set as $entry) {
$count++;
$description = $entry->description;
$entry->description = $description;
$entry->save();
}
unset($set, $entry, $description);
// encrypt reminder metadata
$set = Reminder::where('encrypted', 0)->take(5)->get();
/** @var Reminder $entry */
foreach ($set as $entry) {
$count++;
$metadata = $entry->metadata;
$entry->metadata = $metadata;
$entry->save();
}
unset($set, $entry, $metadata);
// encrypt account virtual balance amount
$set = Account::whereNull('virtual_balance_encrypted')->take(5)->get();
/** @var Account $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->amount;
$entry->virtual_balance = $amount;
$entry->save();
}
unset($set, $entry, $amount);
// encrypt bill amount_min
$set = Bill::whereNull('amount_min_encrypted')->take(5)->get();
/** @var Bill $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->amount_min;
$entry->amount_min = $amount;
$entry->save();
}
unset($set, $entry, $amount);
// encrypt bill amount_max
$set = Bill::whereNull('amount_max_encrypted')->take(5)->get();
/** @var Bill $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->amount_max;
$entry->amount_max = $amount;
$entry->save();
}
unset($set, $entry, $amount);
// encrypt budget limit amount
$set = BudgetLimit::whereNull('amount_encrypted')->take(5)->get();
/** @var BudgetLimit $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->amount;
$entry->amount = $amount;
$entry->save();
}
unset($set, $entry, $amount);
// encrypt limit repetition amount
$set = LimitRepetition::whereNull('amount_encrypted')->take(5)->get();
/** @var LimitRepetition $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->amount;
$entry->amount = $amount;
$entry->save();
}
unset($set, $entry, $amount);
//encrypt piggy bank event amount
$set = PiggyBankEvent::whereNull('amount_encrypted')->take(5)->get();
/** @var PiggyBankEvent $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->amount;
$entry->amount = $amount;
$entry->save();
}
unset($set, $entry, $amount);
// encrypt piggy bank repetition currentamount
$set = PiggyBankRepetition::whereNull('currentamount_encrypted')->take(5)->get();
/** @var PiggyBankRepetition $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->currentamount;
$entry->currentamount = $amount;
$entry->save();
}
unset($set, $entry, $amount);
// encrypt piggy bank targetamount
$set = PiggyBank::whereNull('targetamount_encrypted')->take(5)->get();
/** @var PiggyBank $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->targetamount;
$entry->targetamount = $amount;
$entry->save();
}
unset($set, $entry, $amount);
//encrypt preference name
$set = Preference::whereNull('name_encrypted')->take(5)->get();
/** @var Preference $entry */
foreach ($set as $entry) {
$count++;
$name = $entry->name;
$entry->name = $name;
$entry->save();
}
unset($set, $entry, $name);
//encrypt preference data (add field)
$set = Preference::whereNull('data_encrypted')->take(5)->get();
/** @var Preference $entry */
foreach ($set as $entry) {
$count++;
$data = $entry->data;
$entry->data = $data;
$entry->save();
}
unset($set, $entry, $data);
// encrypt transaction amount
$set = Transaction::whereNull('amount_encrypted')->take(5)->get();
/** @var Transaction $entry */
foreach ($set as $entry) {
$count++;
$amount = $entry->amount;
$entry->amount = $amount;
$entry->save();
}
unset($set, $entry, $amount);
}
if ($count == 0 && $run) {
Session::flash('warning', 'Please open the .env file and change RUNCLEANUP=true to RUNCLEANUP=false');
}
return $next($request);
}
}

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Http\Middleware;
use App;
use Closure;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
@@ -46,23 +45,22 @@ class PiggyBanks
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') {
if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// get piggy banks without a repetition:
/** @var Collection $set */
$set = $this->auth->user()->piggybanks()
->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id')
->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']);
if ($set->count() > 0) {
/** @var PiggyBank $partialPiggy */
foreach ($set as $partialPiggy) {
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($partialPiggy);
$repetition->startdate = is_null($partialPiggy->startdate) ? null : $partialPiggy->startdate;
$repetition->targetdate = is_null($partialPiggy->targetdate) ? null : $partialPiggy->targetdate;
$repetition->currentamount = 0;
$repetition->save();
}
/** @var PiggyBank $partialPiggy */
foreach ($set as $partialPiggy) {
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($partialPiggy);
$repetition->startdate = $partialPiggy->startdate;
$repetition->targetdate = $partialPiggy->targetdate;
$repetition->currentamount = 0;
$repetition->save();
}
unset($partialPiggy, $set, $repetition);
}

View File

@@ -43,29 +43,27 @@ class Range
*
* @param \Illuminate\Http\Request $request
* @param \Closure $theNext
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @return mixed
*/
public function handle(Request $request, Closure $theNext)
{
if ($this->auth->check() && App::environment() != 'testing') {
if ($this->auth->check()) {
// ignore preference. set the range to be the current month:
if (!Session::has('start') && !Session::has('end')) {
/** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M');
$viewRange = Preferences::get('viewRange', '1M')->data;
$start = new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start);
$end = Navigation::updateEndDate($viewRange->data, $start);
$start = Navigation::updateStartDate($viewRange, $start);
$end = Navigation::updateEndDate($viewRange, $start);
Session::put('start', $start);
Session::put('end', $end);
}
if (!Session::has('first')) {
/**
* Get helper thing.
*/
/** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface');
$journal = $repository->first();
@@ -75,16 +73,12 @@ class Range
Session::put('first', Carbon::now()->startOfYear());
}
}
// set current / next / prev month.
$current = Carbon::now()->format('F Y');
$next = Carbon::now()->endOfMonth()->addDay()->format('F Y');
$prev = Carbon::now()->startOfMonth()->subDay()->format('F Y');
$current = Carbon::now()->formatLocalized('%B %Y');
$next = Carbon::now()->endOfMonth()->addDay()->formatLocalized('%B %Y');
$prev = Carbon::now()->startOfMonth()->subDay()->formatLocalized('%B %Y');
View::share('currentMonthName', $current);
View::share('previousMonthName', $prev);
View::share('nextMonthName', $next);
}
return $theNext($request);

View File

@@ -46,36 +46,25 @@ class Reminders
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') {
if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon;
/** @var \FireflyIII\Helpers\Reminders\ReminderHelperInterface $helper */
$helper = App::make('FireflyIII\Helpers\Reminders\ReminderHelperInterface');
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$ranges = $helper->getReminderRanges($piggyBank);
foreach ($ranges as $range) {
if ($today < $range['end'] && $today > $range['start']) {
// create a reminder here!
$helper->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
$helper->createReminders($piggyBank, new Carbon);
}
// delete invalid reminders
$set = $this->auth->user()->reminders()->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')->whereNull('piggy_banks.id')->get(
['reminders.id']
);
foreach ($set as $reminder) {
$reminder->delete();
// this is a construction SQLITE cannot handle :(
if (env('DB_CONNECTION') != 'sqlite') {
Reminder::whereUserId($this->auth->user()->id)
->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')
->whereNull('piggy_banks.id')
->delete();
}
// get and list active reminders:
$reminders = $this->auth->user()->reminders()->today()->get();
$reminders->each(

View File

@@ -50,12 +50,11 @@ class JournalFormRequest extends Request
/**
* @return array
* @throws Exception
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function rules()
{
// can we switch on the "what"?
$what = Input::get('what');
$what = Input::get('what');
$rules = [
'description' => 'required|min:1,max:255',
'what' => 'required|in:withdrawal,deposit,transfer',
@@ -74,8 +73,6 @@ class JournalFormRequest extends Request
if (intval(Input::get('budget_id')) != 0) {
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
}
break;
case 'deposit':
$rules['category'] = 'between:1,255';
@@ -93,7 +90,5 @@ class JournalFormRequest extends Request
}
return $rules;
}
}

View File

@@ -48,6 +48,7 @@ class TagFormRequest extends Request
'date' => 'date',
'latitude' => 'numeric|min:-90|max:90',
'longitude' => 'numeric|min:-90|max:90',
'zoomLevel' => 'numeric|min:0|max:80',
'tagMode' => 'required|in:nothing,balancingAct,advancePayment'
];
}

View File

@@ -1,7 +1,6 @@
<?php
use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
@@ -20,7 +19,7 @@ Breadcrumbs::register(
'home',
function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index'));
$breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
}
);
@@ -28,38 +27,32 @@ Breadcrumbs::register(
'index',
function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index'));
$breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
}
);
//trans('breadcrumbs.')
// accounts
Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
$breadcrumbs->push(ucfirst(e($what)) . ' accounts', route('accounts.index', $what));
$breadcrumbs->push(trans('breadcrumbs.' . strtolower(e($what)) . '_accounts'), route('accounts.index', $what));
}
);
Breadcrumbs::register(
'accounts.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push(trans('breadcrumbs.new_' . strtolower(e($what)) . '_account'), route('accounts.create', $what));
}
);
Breadcrumbs::register(
'accounts.show', function (Generator $breadcrumbs, Account $account) {
switch ($account->accountType->type) {
default:
throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"');
break;
case 'Default account':
case 'Asset account':
$what = 'asset';
break;
case 'Cash account':
$what = 'cash';
break;
case 'Expense account':
case 'Beneficiary account':
$what = 'expense';
break;
case 'Revenue account':
$what = 'revenue';
break;
}
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push(e($account->name), route('accounts.show', $account->id));
}
@@ -67,14 +60,17 @@ Breadcrumbs::register(
Breadcrumbs::register(
'accounts.delete', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Delete ' . e($account->name), route('accounts.delete', $account->id));
$breadcrumbs->push(trans('breadcrumbs.delete_account', ['name' => e($account->name)]), route('accounts.delete', $account->id));
}
);
Breadcrumbs::register(
'accounts.edit', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Edit ' . e($account->name), route('accounts.edit', $account->id));
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$breadcrumbs->push(trans('breadcrumbs.edit_' . $what . '_account', ['name' => e($account->name)]), route('accounts.edit', $account->id));
}
);
@@ -82,26 +78,26 @@ Breadcrumbs::register(
Breadcrumbs::register(
'budgets.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Budgets', route('budgets.index'));
$breadcrumbs->push(trans('breadcrumbs.budgets'), route('budgets.index'));
}
);
Breadcrumbs::register(
'budgets.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push('Create new budget', route('budgets.create'));
$breadcrumbs->push(trans('breadcrumbs.newBudget'), route('budgets.create'));
}
);
Breadcrumbs::register(
'budgets.edit', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Edit ' . e($budget->name), route('budgets.edit', $budget->id));
$breadcrumbs->push(trans('breadcrumbs.edit_budget', ['name' => e($budget->name)]), route('budgets.edit', $budget->id));
}
);
Breadcrumbs::register(
'budgets.delete', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Delete ' . e($budget->name), route('budgets.delete', $budget->id));
$breadcrumbs->push(trans('breadcrumbs.delete_budget', ['name' => e($budget->name)]), route('budgets.delete', $budget->id));
}
);
@@ -128,26 +124,26 @@ Breadcrumbs::register(
Breadcrumbs::register(
'categories.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Categories', route('categories.index'));
$breadcrumbs->push(trans('breadcrumbs.categories'), route('categories.index'));
}
);
Breadcrumbs::register(
'categories.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push('Create new category', route('categories.create'));
$breadcrumbs->push(trans('breadcrumbs.newCategory'), route('categories.create'));
}
);
Breadcrumbs::register(
'categories.edit', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Edit ' . e($category->name), route('categories.edit', $category->id));
$breadcrumbs->push(trans('breadcrumbs.edit_category', ['name' => e($category->name)]), route('categories.edit', $category->id));
}
);
Breadcrumbs::register(
'categories.delete', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Delete ' . e($category->name), route('categories.delete', $category->id));
$breadcrumbs->push(trans('breadcrumbs.delete_category', ['name' => e($category->name)]), route('categories.delete', $category->id));
}
);
@@ -170,20 +166,20 @@ Breadcrumbs::register(
Breadcrumbs::register(
'currency.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Currencies', route('currency.index'));
$breadcrumbs->push(trans('breadcrumbs.currencies'), route('currency.index'));
}
);
Breadcrumbs::register(
'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index');
$breadcrumbs->push('Edit ' . $currency->name, route('currency.edit', $currency->id));
$breadcrumbs->push(trans('breadcrumbs.edit_currency', ['name' => e($currency->name)]), route('currency.edit', $currency->id));
}
);
Breadcrumbs::register(
'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index');
$breadcrumbs->push('Delete ' . $currency->name, route('currency.delete', $currency->id));
$breadcrumbs->push(trans('breadcrumbs.delete_currency', ['name' => e($currency->name)]), route('currency.delete', $currency->id));
}
);
@@ -192,26 +188,26 @@ Breadcrumbs::register(
Breadcrumbs::register(
'piggy-banks.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Piggy banks', route('piggy-banks.index'));
$breadcrumbs->push(trans('breadcrumbs.piggyBanks'), route('piggy-banks.index'));
}
);
Breadcrumbs::register(
'piggy-banks.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push('Create new piggy bank', route('piggy-banks.create'));
$breadcrumbs->push(trans('breadcrumbs.newPiggyBank'), route('piggy-banks.create'));
}
);
Breadcrumbs::register(
'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Edit ' . e($piggyBank->name), route('piggy-banks.edit', $piggyBank->id));
$breadcrumbs->push(trans('breadcrumbs.edit_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.edit', $piggyBank->id));
}
);
Breadcrumbs::register(
'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Delete ' . e($piggyBank->name), route('piggy-banks.delete', $piggyBank->id));
$breadcrumbs->push(trans('breadcrumbs.delete_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.delete', $piggyBank->id));
}
);
@@ -227,7 +223,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'preferences', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Preferences', route('preferences'));
$breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences'));
}
);
@@ -236,14 +232,14 @@ Breadcrumbs::register(
Breadcrumbs::register(
'profile', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Profile', route('profile'));
$breadcrumbs->push(trans('breadcrumbs.profile'), route('profile'));
}
);
Breadcrumbs::register(
'change-password', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile');
$breadcrumbs->push('Change your password', route('change-password'));
$breadcrumbs->push(trans('breadcrumbs.changePassword'), route('change-password'));
}
);
@@ -252,26 +248,26 @@ Breadcrumbs::register(
Breadcrumbs::register(
'bills.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Bills', route('bills.index'));
$breadcrumbs->push(trans('breadcrumbs.bills'), route('bills.index'));
}
);
Breadcrumbs::register(
'bills.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('bills.index');
$breadcrumbs->push('Create new bill', route('bills.create'));
$breadcrumbs->push(trans('breadcrumbs.newBill'), route('bills.create'));
}
);
Breadcrumbs::register(
'bills.edit', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Edit ' . e($bill->name), route('bills.edit', $bill->id));
$breadcrumbs->push(trans('breadcrumbs.edit_bill', ['name' => e($bill->name)]), route('bills.edit', $bill->id));
}
);
Breadcrumbs::register(
'bills.delete', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Delete ' . e($bill->name), route('bills.delete', $bill->id));
$breadcrumbs->push(trans('breadcrumbs.delete_bill', ['name' => e($bill->name)]), route('bills.delete', $bill->id));
}
);
@@ -287,7 +283,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'reminders.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Reminders', route('reminders.index'));
$breadcrumbs->push(trans('breadcrumbs.reminders'), route('reminders.index'));
}
);
@@ -296,7 +292,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) {
$breadcrumbs->parent('reminders.index');
$breadcrumbs->push('Reminder #' . $reminder->id, route('reminders.show', $reminder->id));
$breadcrumbs->push(trans('breadcrumbs.reminder', ['id' => e($reminder->id)]), route('reminders.show', $reminder->id));
}
);
@@ -306,28 +302,33 @@ Breadcrumbs::register(
Breadcrumbs::register(
'reports.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Reports', route('reports.index'));
$breadcrumbs->push(trans('breadcrumbs.reports'), route('reports.index'));
}
);
Breadcrumbs::register(
'reports.year', function (Generator $breadcrumbs, Carbon $date) {
'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push($date->year, route('reports.year', $date->year));
if ($shared) {
$title = trans('breadcrumbs.yearly_report_shared', ['date' => $date->year]);
} else {
$title = trans('breadcrumbs.yearly_report', ['date' => $date->year]);
}
$breadcrumbs->push($title, route('reports.year', $date->year));
}
);
Breadcrumbs::register(
'reports.month', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.year', $date);
$breadcrumbs->push('Monthly report for ' . $date->format('F Y'), route('reports.month', [$date->year, $date->month]));
}
);
'reports.month', function (Generator $breadcrumbs, Carbon $date, $shared) {
$breadcrumbs->parent('reports.year', $date, $shared);
Breadcrumbs::register(
'reports.budget', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push('Budget report for ' . $date->format('F Y'), route('reports.budget', [$date->year, $date->month]));
if ($shared) {
$title = trans('breadcrumbs.monthly_report_shared', ['date' => $date->year]);
} else {
$title = trans('breadcrumbs.monthly_report', ['date' => $date->year]);
}
$breadcrumbs->push($title, route('reports.month', [$date->year, $date->month]));
}
);
@@ -335,7 +336,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'search', function (Generator $breadcrumbs, $query) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Search for "' . e($query) . '"', route('search'));
$breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search'));
}
);
@@ -343,47 +344,26 @@ Breadcrumbs::register(
Breadcrumbs::register(
'transactions.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitle = 'Expenses';
break;
case 'revenue':
case 'deposit':
$subTitle = 'Revenue, income and deposits';
break;
case 'transfer':
case 'transfers':
$subTitle = 'Transfers';
break;
case 'opening balance':
$subTitle = 'Opening balances';
break;
default:
throw new FireflyException('Cannot handle $what "' . e($what) . '" in bread crumbs');
}
$breadcrumbs->push($subTitle, route('transactions.index', $what));
$breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', $what));
}
);
Breadcrumbs::register(
'transactions.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('transactions.index', $what);
$breadcrumbs->push('Create new ' . e($what), route('transactions.create', $what));
$breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('transactions.create', $what));
}
);
Breadcrumbs::register(
'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Edit ' . e($journal->description), route('transactions.edit', $journal->id));
$breadcrumbs->push(trans('breadcrumbs.edit_journal', ['description' => $journal->description]), route('transactions.edit', $journal->id));
}
);
Breadcrumbs::register(
'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Delete ' . e($journal->description), route('transactions.delete', $journal->id));
$breadcrumbs->push(trans('breadcrumbs.delete_journal', ['description' => e($journal->description)]), route('transactions.delete', $journal->id));
}
);
@@ -391,7 +371,7 @@ Breadcrumbs::register(
'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
$breadcrumbs->push(e($journal->description), route('transactions.show', $journal->id));
$breadcrumbs->push($journal->description, route('transactions.show', $journal->id));
}
);
@@ -400,16 +380,32 @@ Breadcrumbs::register(
Breadcrumbs::register(
'tags.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Tags', route('tags.index'));
$breadcrumbs->push(trans('breadcrumbs.tags'), route('tags.index'));
}
);
Breadcrumbs::register(
'tags.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('tags.index');
$breadcrumbs->push('Create tag', route('tags.create'));
$breadcrumbs->push(trans('breadcrumbs.createTag'), route('tags.create'));
}
);
Breadcrumbs::register(
'tags.edit', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.edit_tag', ['tag' => e($tag->tag)]), route('tags.edit', $tag->id));
}
);
Breadcrumbs::register(
'tags.delete', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.delete_tag', ['tag' => e($tag->tag)]), route('tags.delete', $tag->id));
}
);
Breadcrumbs::register(
'tags.show', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.index');

View File

@@ -193,13 +193,14 @@ Route::controllers(
]
);
Route::get('/routes', ['uses' => 'HomeController@routes', 'as' => 'routes']);
/**
* Home Controller
*/
Route::group(
['middleware' => ['auth', 'range', 'reminders', 'piggybanks']], function () {
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index', 'middleware' => 'cleanup']);
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']);
@@ -223,7 +224,6 @@ Route::group(
Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching.
Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']);
Route::get('/bills/add/{bill}', ['uses' => 'BillController@add', 'as' => 'bills.add']);
Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);
Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']);
@@ -273,22 +273,41 @@ Route::group(
/**
* Google Chart Controller
* ALL CHART Controllers
*/
Route::get('/chart/home/account', ['uses' => 'GoogleChartController@allAccountsBalanceChart']);
Route::get('/chart/home/budgets', ['uses' => 'GoogleChartController@allBudgetsHomeChart']);
Route::get('/chart/home/categories', ['uses' => 'GoogleChartController@allCategoriesHomeChart']);
Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']);
Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']);
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending'])->where(['year' => '[0-9]+']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']);
Route::get('/chart/piggy-history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']);
Route::get('/chart/category/{category}/period', ['uses' => 'GoogleChartController@categoryPeriodChart']);
Route::get('/chart/category/{category}/overview', ['uses' => 'GoogleChartController@categoryOverviewChart']);
// accounts:
Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']);
Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
);
Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']);
// bills:
Route::get('/chart/bill/frontpage', ['uses' => 'Chart\BillController@frontpage']);
Route::get('/chart/bill/{bill}', ['uses' => 'Chart\BillController@single']);
// budgets:
Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']);
Route::get('/chart/budget/year/{year}/{shared?}', ['uses' => 'Chart\BudgetController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']);
Route::get('/chart/budget/{budget}', ['uses' => 'Chart\BudgetController@budget']);
// categories:
Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']);
Route::get('/chart/category/year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/category/{category}/month', ['uses' => 'Chart\CategoryController@month']); // should be period.
Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']);
// piggy banks:
Route::get('/chart/piggyBank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);
// reports:
Route::get('/chart/report/in-out/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOut'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/report/in-out-sum/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOutSummarized'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
);
/**
* Help Controller
@@ -306,10 +325,7 @@ Route::group(
Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']);
Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']);
Route::get('/json/box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'json.box.unpaid']);
Route::get('/json/show-shared-reports', 'JsonController@showSharedReports');
Route::get('/json/transaction-journals/{what}', 'JsonController@transactionJournals');
Route::get('/json/show-shared-reports/set', 'JsonController@setSharedReports');
/**
* Piggy Bank Controller
@@ -355,19 +371,13 @@ Route::group(
* Report Controller
*/
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']);
Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']);
Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']);
//Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}']);
Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/reports/{year}/{month}/{shared?}', ['uses' => 'ReportController@month', 'as' => 'reports.month'])->where(
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
);
// pop ups for budget report:
Route::get('/reports/modal/{account}/{year}/{month}/no-budget', ['uses' => 'ReportController@modalNoBudget', 'as' => 'reports.no-budget']);
Route::get(
'/reports/modal/{account}/{year}/{month}/balanced-transfers',
['uses' => 'ReportController@modalBalancedTransfers', 'as' => 'reports.balanced-transfers']
);
Route::get(
'/reports/modal/{account}/{year}/{month}/left-unbalanced', ['uses' => 'ReportController@modalLeftUnbalanced', 'as' => 'reports.left-unbalanced']
);
/**
* Search Controller

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Models;
use App;
use Crypt;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
@@ -18,6 +17,7 @@ class Account extends Model
use SoftDeletes, ValidatingTrait;
protected $fillable = ['user_id', 'account_type_id', 'name', 'active', 'virtual_balance'];
protected $hidden = ['virtual_balance_encrypted', 'encrypted'];
protected $rules
= [
'user_id' => 'required|exists:users,id',
@@ -28,7 +28,7 @@ class Account extends Model
/**
* @param array $fields
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @return Account|null
*/
@@ -50,12 +50,6 @@ class Account extends Model
}
// create it!
$account = Account::create($fields);
if (is_null($account->id)) {
// could not create account:
App::abort(500, 'Could not create new account with data: ' . json_encode($fields) . ' because ' . json_encode($account->getErrors()));
}
return $account;
@@ -117,6 +111,8 @@ class Account extends Model
*
* @param $fieldName
*
* @codeCoverageIgnore
*
* @return string|null
*/
public function getMeta($fieldName)
@@ -133,6 +129,7 @@ class Account extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return string
@@ -144,9 +141,7 @@ class Account extends Model
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
@@ -160,6 +155,7 @@ class Account extends Model
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param array $types
*/
@@ -174,6 +170,7 @@ class Account extends Model
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param string $name
* @param string $value
@@ -191,6 +188,7 @@ class Account extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*/
public function setNameAttribute($value)
@@ -199,6 +197,16 @@ class Account extends Model
$this->attributes['encrypted'] = true;
}
/**
* @param $value
*
* @codeCoverageIgnore
*/
public function setVirtualBalanceAttribute($value)
{
$this->attributes['virtual_balance'] = strval(round($value, 2));
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\HasMany

View File

@@ -4,6 +4,7 @@ use Crypt;
use Illuminate\Database\Eloquent\Model;
/**
* @codeCoverageIgnore
* Class Bill
*
* @package FireflyIII\Models
@@ -14,6 +15,8 @@ class Bill extends Model
protected $fillable
= ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',];
protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted'];
/**
* @return array
*/
@@ -34,9 +37,7 @@ class Bill extends Model
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
@@ -51,9 +52,23 @@ class Bill extends Model
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @param $value
*/
public function setAmountMaxAttribute($value)
{
$this->attributes['amount_max'] = strval(round($value, 2));
}
/**
* @param $value
*/
public function setAmountMinAttribute($value)
{
$this->attributes['amount_min'] = strval(round($value, 2));
}
/**

View File

@@ -15,7 +15,8 @@ class Budget extends Model
use SoftDeletes;
protected $fillable = ['user_id', 'name'];
protected $fillable = ['user_id', 'name', 'active'];
protected $hidden = ['encrypted'];
/**
*
@@ -46,9 +47,7 @@ class Budget extends Model
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**

View File

@@ -11,6 +11,8 @@ use Illuminate\Database\Eloquent\Model;
class BudgetLimit extends Model
{
protected $hidden = ['amount_encrypted'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
@@ -35,4 +37,12 @@ class BudgetLimit extends Model
return $this->hasMany('FireflyIII\Models\LimitRepetition');
}
/**
* @param $value
*/
public function setAmountAttribute($value)
{
$this->attributes['amount'] = strval(round($value, 2));
}
}

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Models;
use App;
use Crypt;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -16,27 +15,11 @@ class Category extends Model
use SoftDeletes;
protected $fillable = ['user_id', 'name'];
/**
* @codeCoverageIgnore
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function transactionjournals()
{
return $this->belongsToMany('FireflyIII\Models\TransactionJournal', 'category_transaction_journal', 'category_id');
}
protected $hidden = ['encrypted'];
/**
* @param array $fields
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @return Account|null
*/
@@ -58,11 +41,6 @@ class Category extends Model
}
// create it!
$category = Category::create($fields);
if (is_null($category->id)) {
// could not create account:
App::abort(500, 'Could not create new category with data: ' . json_encode($fields));
}
return $category;
@@ -70,25 +48,16 @@ class Category extends Model
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
* @return array
*/
public function user()
public function getDates()
{
return $this->belongsTo('FireflyIII\User');
}
/**
* @codeCoverageIgnore
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return string
@@ -103,4 +72,33 @@ class Category extends Model
return $value;
}
/**
* @codeCoverageIgnore
*
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function transactionjournals()
{
return $this->belongsToMany('FireflyIII\Models\TransactionJournal', 'category_transaction_journal', 'category_id');
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@@ -1,19 +1,20 @@
<?php namespace FireflyIII\Models;
use Auth;
use DB;
use Illuminate\Database\Eloquent\Model;
/**
* Class LimitRepetition
*
* @codeCoverageIgnore
*
* @package FireflyIII\Models
*/
class LimitRepetition extends Model
{
protected $hidden = ['amount_encrypted'];
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function budgetLimit()
@@ -22,7 +23,6 @@ class LimitRepetition extends Model
}
/**
* @codeCoverageIgnore
* @return array
*/
public function getDates()
@@ -31,24 +31,11 @@ class LimitRepetition extends Model
}
/**
* @return float
* @param $value
*/
public function spentInRepetition()
public function setAmountAttribute($value)
{
$sum = DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('transaction_journals.date', '>=', $this->startdate->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->enddate->format('Y-m-d'))
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('transactions.deleted_at')
->where('transactions.amount', '>', 0)
->where('limit_repetitions.id', '=', $this->id)
->sum('transactions.amount');
return floatval($sum);
$this->attributes['amount'] = strval(round($value, 2));
}
}

View File

@@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class PiggyBank
*
* @codeCoverageIgnore
*
* @package FireflyIII\Models
*/
class PiggyBank extends Model
@@ -14,10 +16,10 @@ class PiggyBank extends Model
use SoftDeletes;
protected $fillable
= ['name', 'account_id', 'order', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me'];
= ['name', 'account_id', 'order', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me'];
protected $hidden = ['targetamount_encrypted', 'encrypted'];
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function account()
@@ -45,7 +47,6 @@ class PiggyBank extends Model
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBankRepetitions()
@@ -54,7 +55,6 @@ class PiggyBank extends Model
}
/**
* @codeCoverageIgnore
* @return array
*/
public function getDates()
@@ -63,46 +63,7 @@ class PiggyBank extends Model
}
/**
* @codeCoverageIgnore
* @param $value
*
* @return int
*/
public function getRemindMeAttribute($value)
{
return intval($value) == 1;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBankEvents()
{
return $this->hasMany('FireflyIII\Models\PiggyBankEvent');
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function reminders()
{
return $this->morphMany('FireflyIII\Models\Reminder', 'remindersable');
}
/**
* @codeCoverageIgnore
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @codeCoverageIgnore
* @param $value
*
* @return string
@@ -114,8 +75,51 @@ class PiggyBank extends Model
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
*
* @param $value
*
* @return int
*/
public function getRemindMeAttribute($value)
{
return intval($value) == 1;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBankEvents()
{
return $this->hasMany('FireflyIII\Models\PiggyBankEvent');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function reminders()
{
return $this->morphMany('FireflyIII\Models\Reminder', 'remindersable');
}
/**
*
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @param $value
*/
public function setTargetamountAttribute($value)
{
$this->attributes['targetamount'] = strval(round($value, 2));
}
}

View File

@@ -6,12 +6,14 @@ use Illuminate\Database\Eloquent\Model;
* Class PiggyBankEvent
*
* @codeCoverageIgnore
*
* @package FireflyIII\Models
*/
class PiggyBankEvent extends Model
{
protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'amount'];
protected $hidden = ['amount_encrypted'];
/**
* @return array
@@ -29,6 +31,14 @@ class PiggyBankEvent extends Model
return $this->belongsTo('FireflyIII\Models\PiggyBank');
}
/**
* @param $value
*/
public function setAmountAttribute($value)
{
$this->attributes['amount'] = strval(round($value, 2));
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@@ -14,6 +14,7 @@ class PiggyBankRepetition extends Model
{
protected $fillable = ['piggy_bank_id', 'startdate', 'targetdate', 'currentamount'];
protected $hidden = ['currentamount_encrypted'];
/**
* @return array
@@ -66,4 +67,12 @@ class PiggyBankRepetition extends Model
);
}
/**
* @param $value
*/
public function setCurrentamountAttribute($value)
{
$this->attributes['currentamount'] = strval(round($value, 2));
}
}

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model;
/**
@@ -12,6 +13,7 @@ class Preference extends Model
{
protected $fillable = ['user_id', 'data', 'name'];
protected $hidden = ['data_encrypted', 'name_encrypted'];
/**
* @param $value
@@ -20,7 +22,12 @@ class Preference extends Model
*/
public function getDataAttribute($value)
{
return json_decode($value);
if (is_null($this->data_encrypted)) {
return json_decode($value);
}
$data = Crypt::decrypt($this->data_encrypted);
return json_decode($data);
}
/**
@@ -31,12 +38,37 @@ class Preference extends Model
return ['created_at', 'updated_at'];
}
/**
* @param $value
*
* @return float|int
*/
public function getNameAttribute($value)
{
if (is_null($this->name_encrypted)) {
return $value;
}
$value = Crypt::decrypt($this->name_encrypted);
return $value;
}
/**
* @param $value
*/
public function setDataAttribute($value)
{
$this->attributes['data'] = json_encode($value);
$this->attributes['data'] = '';//json_encode($value);
$this->attributes['data_encrypted'] = Crypt::encrypt(json_encode($value));
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name_encrypted'] = Crypt::encrypt($value);
$this->attributes['name'] = $value;
}
/**

View File

@@ -8,6 +8,8 @@ use Illuminate\Database\Eloquent\Model;
/**
* Class Reminder
*
* @codeCoverageIgnore
*
* @package FireflyIII\Models
*/
class Reminder extends Model
@@ -15,9 +17,10 @@ class Reminder extends Model
protected $fillable = ['user_id', 'startdate', 'metadata', 'enddate', 'active', 'notnow', 'remindersable_id', 'remindersable_type',];
protected $hidden = ['encrypted'];
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return int
@@ -28,7 +31,6 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
* @return array
*/
public function getDates()
@@ -37,7 +39,7 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return mixed
@@ -52,7 +54,7 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return bool
@@ -63,7 +65,6 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function remindersable()
@@ -72,7 +73,7 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param Carbon $start
* @param Carbon $end
@@ -85,7 +86,7 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
*
* @return $this
@@ -99,7 +100,7 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
*
* @param $value
*/
public function setMetadataAttribute($value)
@@ -109,7 +110,6 @@ class Reminder extends Model
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()

View File

@@ -2,7 +2,6 @@
namespace FireflyIII\Models;
use App;
use Crypt;
use Illuminate\Database\Eloquent\Model;
use Watson\Validating\ValidatingTrait;
@@ -29,6 +28,8 @@ class Tag extends Model
/**
* @param array $fields
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*
* @return Tag|null
*/
@@ -55,12 +56,6 @@ class Tag extends Model
$fields['tagMode'] = 'nothing';
$fields['description'] = isset($fields['description']) && !is_null($fields['description']) ? $fields['description'] : '';
$tag = Tag::create($fields);
if (is_null($tag->id)) {
// could not create account:
App::abort(500, 'Could not create new tag with data: ' . json_encode($fields) . ' because ' . json_encode($tag->getErrors()));
}
return $tag;
@@ -77,6 +72,7 @@ class Tag extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return string
@@ -88,6 +84,7 @@ class Tag extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return string
@@ -99,6 +96,7 @@ class Tag extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*/
public function setDescriptionAttribute($value)
@@ -108,6 +106,7 @@ class Tag extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*/
public function setTagAttribute($value)

View File

@@ -16,6 +16,7 @@ class Transaction extends Model
{
protected $fillable = ['account_id', 'transaction_journal_id', 'description', 'amount'];
protected $hidden = ['encrypted'];
protected $rules
= [
'account_id' => 'required|exists:accounts,id',
@@ -33,6 +34,24 @@ class Transaction extends Model
return $this->belongsTo('FireflyIII\Models\Account');
}
/**
* @param $value
*
* @return float|int
*/
public function getAmountAttribute($value)
{
return $value;
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
@@ -56,11 +75,11 @@ class Transaction extends Model
}
/**
* @return array
* @param $value
*/
public function getDates()
public function setAmountAttribute($value)
{
return ['created_at', 'updated_at', 'deleted_at'];
$this->attributes['amount'] = strval(round($value, 2));
}
/**

View File

@@ -18,9 +18,9 @@ class TransactionJournal extends Model
use SoftDeletes, ValidatingTrait;
protected $fillable = ['user_id', 'transaction_type_id', 'bill_id', 'transaction_currency_id', 'description', 'completed', 'date', 'encrypted'];
protected $hidden = ['encrypted'];
protected $rules
= [
= [
'user_id' => 'required|exists:users,id',
'transaction_type_id' => 'required|exists:transaction_types,id',
'bill_id' => 'exists:bills,id',
@@ -58,23 +58,93 @@ class TransactionJournal extends Model
return $this->belongsToMany('FireflyIII\Models\Category');
}
/**
* @return string
*/
public function getActualAmountAttribute()
{
$amount = '0';
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0) {
$amount = $t->amount;
}
}
return $amount;
}
/**
* @return float
*/
public function getAmountAttribute()
{
$amount = '0';
bcscale(2);
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0) {
return floatval($t->amount);
$amount = $t->amount;
}
}
return 0;
/*
* If the journal has tags, it gets complicated.
*/
if ($this->tags->count() == 0) {
return $amount;
}
// if journal is part of advancePayment AND journal is a withdrawal,
// then journal is being repaid by other journals, so the actual amount will lower:
/** @var Tag $advancePayment */
$advancePayment = $this->tags()->where('tagMode', 'advancePayment')->first();
if ($advancePayment && $this->transactionType->type == 'Withdrawal') {
// loop other deposits, remove from our amount.
$others = $advancePayment->transactionJournals()->transactionTypes(['Deposit'])->get();
foreach ($others as $other) {
$amount = bcsub($amount, $other->actualAmount);
}
return $amount;
}
// if this journal is part of an advancePayment AND the journal is a deposit,
// then the journal amount is correcting a withdrawal, and the amount is zero:
if ($advancePayment && $this->transactionType->type == 'Deposit') {
return '0';
}
// is balancing act?
$balancingAct = $this->tags()->where('tagMode', 'balancingAct')->first();
if ($balancingAct) {
// this is the expense:
if ($this->transactionType->type == 'Withdrawal') {
$transfer = $balancingAct->transactionJournals()->transactionTypes(['Transfer'])->first();
if ($transfer) {
$amount = bcsub($amount, $transfer->actualAmount);
return $amount;
}
} // @codeCoverageIgnore
} // @codeCoverageIgnore
return $amount;
}
/**
* @return Account|mixed
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany('FireflyIII\Models\Tag');
}
/**
* @return Account
*/
public function getAssetAccountAttribute()
{
@@ -93,7 +163,7 @@ class TransactionJournal extends Model
}
return $this->transactions()->first();
return $this->transactions()->first()->account;
}
/**
@@ -105,6 +175,28 @@ class TransactionJournal extends Model
return $this->hasMany('FireflyIII\Models\Transaction');
}
/**
* @return float
*/
public function getCorrectedActualAmountAttribute()
{
$amount = '0';
$type = $this->transactionType->type;
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0 && $type != 'Withdrawal') {
$amount = $t->amount;
break;
}
if ($t->amount < 0 && $type == 'Withdrawal') {
$amount = $t->amount;
break;
}
}
return $amount;
}
/**
* @codeCoverageIgnore
* @return array
@@ -116,6 +208,7 @@ class TransactionJournal extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*
* @return string
@@ -129,6 +222,21 @@ class TransactionJournal extends Model
return $value;
}
/**
* @return Account
*/
public function getDestinationAccountAttribute()
{
/** @var Transaction $transaction */
foreach ($this->transactions()->get() as $transaction) {
if (floatval($transaction->amount) > 0) {
return $transaction->account;
}
}
return $this->transactions()->first()->account;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\HasMany
@@ -140,6 +248,7 @@ class TransactionJournal extends Model
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param Account $account
*/
@@ -154,6 +263,7 @@ class TransactionJournal extends Model
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param Carbon $date
*
@@ -166,6 +276,7 @@ class TransactionJournal extends Model
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param Carbon $date
*
@@ -178,23 +289,7 @@ class TransactionJournal extends Model
/**
* @codeCoverageIgnore
* @param EloquentBuilder $query
* @param $amount
*/
public function scopeLessThan(EloquentBuilder $query, $amount)
{
if (is_null($this->joinedTransactions)) {
$query->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
);
$this->joinedTransactions = true;
}
$query->where('transactions.amount', '<=', $amount);
}
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param Carbon $date
*
@@ -207,6 +302,7 @@ class TransactionJournal extends Model
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param array $types
*/
@@ -239,6 +335,7 @@ class TransactionJournal extends Model
/**
* @codeCoverageIgnore
*
* @param $value
*/
public function setDescriptionAttribute($value)
@@ -247,15 +344,6 @@ class TransactionJournal extends Model
$this->attributes['encrypted'] = true;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany('FireflyIII\Models\Tag');
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@@ -17,6 +17,8 @@ class ConfigServiceProvider extends ServiceProvider
* to overwrite any "vendor" or package configuration that you may want to
* modify before the application handles the incoming request / command.
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @return void
*/
public function register()
@@ -163,6 +165,7 @@ class ConfigServiceProvider extends ServiceProvider
'Session',
'Route',
'Auth',
'Lang',
'URL',
'Config',
'ExpandedForm' => [

View File

@@ -49,7 +49,47 @@ class EventServiceProvider extends ServiceProvider
public function boot(DispatcherContract $events)
{
parent::boot($events);
$this->registerDeleteEvents();
$this->registerCreateEvents();
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {
$end = Navigation::addPeriod(clone $budgetLimit->startdate, $budgetLimit->repeat_freq, 0);
$end->subDay();
$set = $budgetLimit->limitrepetitions()->where('startdate', $budgetLimit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))
->get();
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed!');
Log::error($e->getMessage());
}
} else {
if ($set->count() == 1) {
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
}
}
);
}
/**
*
*/
protected function registerDeleteEvents()
{
TransactionJournal::deleted(
function (TransactionJournal $journal) {
@@ -59,8 +99,6 @@ class EventServiceProvider extends ServiceProvider
}
}
);
PiggyBank::deleting(
function (PiggyBank $piggyBank) {
$reminders = $piggyBank->reminders()->get();
@@ -82,6 +120,14 @@ class EventServiceProvider extends ServiceProvider
}
);
}
/**
*
*/
protected function registerCreateEvents()
{
// move this routine to a filter
// in case of repeated piggy banks and/or other problems.
PiggyBank::created(
@@ -94,47 +140,6 @@ class EventServiceProvider extends ServiceProvider
$repetition->save();
}
);
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {
$end = Navigation::addPeriod(clone $budgetLimit->startdate, $budgetLimit->repeat_freq, 0);
$end->subDay();
$set = $budgetLimit->limitrepetitions()->where('startdate', $budgetLimit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))
->get();
/*
* Create new LimitRepetition:
*/
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed!');
Log::error($e->getMessage());
}
} else {
if ($set->count() == 1) {
/*
* Update existing one.
*/
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
}
}
);
}
}

View File

@@ -10,9 +10,9 @@ use FireflyIII\Support\Preferences;
use FireflyIII\Support\Steam;
use FireflyIII\Support\Twig\Budget;
use FireflyIII\Support\Twig\General;
use FireflyIII\Support\Twig\Translation;
use FireflyIII\Support\Twig\Journal;
use FireflyIII\Support\Twig\PiggyBank;
use FireflyIII\Support\Twig\Translation;
use FireflyIII\Validation\FireflyValidator;
use Illuminate\Support\ServiceProvider;
use Twig;
@@ -46,6 +46,9 @@ class FireflyServiceProvider extends ServiceProvider
Twig::addExtension(new Translation);
}
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function register()
{

View File

@@ -162,8 +162,6 @@ class AccountRepository implements AccountRepositoryInterface
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
$query->before(Session::get('end', Carbon::now()->endOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth()));
$count = $query->count();
$set = $query->take(50)->offset($offset)->get(['transaction_journals.*']);
$paginator = new LengthAwarePaginator($set, $count, 50, $page);
@@ -289,7 +287,7 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function getTransfersInRange(Account $account, Carbon $start, Carbon $end)
{
return TransactionJournal::whereIn(
$set = TransactionJournal::whereIn(
'id', function (Builder $q) use ($account, $start, $end) {
$q->select('transaction_journals.id')
->from('transactions')
@@ -299,21 +297,31 @@ class AccountRepository implements AccountRepositoryInterface
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transactions.amount', '>', 0)
->where('transaction_types.type', 'Transfer');
}
)->get();
$filtered = $set->filter(
function (TransactionJournal $journal) use ($account) {
if ($journal->destination_account->id == $account->id) {
return $journal;
}
} // @codeCoverageIgnore
);
return $filtered;
}
/**
* @param Account $account
* @param Carbon $date
*
* @return float
*/
public function leftOnAccount(Account $account)
public function leftOnAccount(Account $account, Carbon $date)
{
$balance = Steam::balance($account, null, true);
$balance = Steam::balance($account, $date, true);
/** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount;

View File

@@ -115,10 +115,11 @@ interface AccountRepositoryInterface
/**
* @param Account $account
* @param Carbon $date
*
* @return float
*/
public function leftOnAccount(Account $account);
public function leftOnAccount(Account $account, Carbon $date);
/**
* @param Account $account

View File

@@ -10,7 +10,6 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Log;
use Navigation;
@@ -22,6 +21,27 @@ use Navigation;
*/
class BillRepository implements BillRepositoryInterface
{
/**
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end)
{
$amount = 0;
$journals = $bill->transactionjournals()->before($end)->after($start)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$amount += $journal->amount;
}
return $amount;
}
/**
* Create a fake bill to help the chart controller.
*
@@ -87,16 +107,10 @@ class BillRepository implements BillRepositoryInterface
public function getJournals(Bill $bill)
{
return $bill->transactionjournals()->withRelevantData()
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.amount', '>', 0);
}
)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*', 'transactions.amount']);
->get(['transaction_journals.*']);
}
/**
@@ -251,13 +265,10 @@ class BillRepository implements BillRepositoryInterface
*/
public function scan(Bill $bill, TransactionJournal $journal)
{
/*
* Match words.
*/
$amountMatch = false;
$wordMatch = false;
$matches = explode(',', $bill->match);
$description = strtolower($journal->description);
Log::debug('Now scanning ' . $description);
/*
* Attach expense account to description for more narrow matching.
@@ -295,7 +306,6 @@ class BillRepository implements BillRepositoryInterface
* Match amount.
*/
$amountMatch = false;
if (count($transactions) > 1) {
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));

View File

@@ -15,6 +15,17 @@ use Illuminate\Support\Collection;
interface BillRepositoryInterface
{
/**
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end);
/**
* Create a fake bill to help the chart controller.
*

View File

@@ -8,6 +8,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
@@ -48,9 +49,11 @@ class BudgetRepository implements BudgetRepositoryInterface
*
* @return float
*/
public function expensesOnDay(Budget $budget, Carbon $date)
public function expensesOnDayCorrected(Budget $budget, Carbon $date)
{
return floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($date)->sum('amount'));
$sum = floatval($budget->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount'));
return $sum * -1;
}
/**
@@ -98,7 +101,6 @@ class BudgetRepository implements BudgetRepositoryInterface
public function getBudgets()
{
$budgets = Auth::user()->budgets()->get();
$budgets->sortBy('name');
return $budgets;
}
@@ -246,27 +248,44 @@ class BudgetRepository implements BudgetRepositoryInterface
->whereNotNull('budget_transaction_journal.budget_id');
}
)
->before($end)
->after($start)
->lessThan(0)
->before($end)
->transactionTypes(['Withdrawal'])
->get();
->get(['transaction_journals.*'])->sum('amount');
return floatval($noBudgetSet->sum('amount')) * -1;
return floatval($noBudgetSet) * -1;
}
/**
* @param Budget $budget
* @param Carbon $date
* @param Carbon $start
* @param Carbon $end
* @param bool $shared
*
* @return float
*/
public function spentInMonth(Budget $budget, Carbon $date)
public function spentInPeriodCorrected(Budget $budget, Carbon $start, Carbon $end, $shared = true)
{
$end = clone $date;
$date->startOfMonth();
$end->endOfMonth();
$sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1;
if ($shared === true) {
// get everything:
$sum = floatval($budget->transactionjournals()->before($end)->after($start)->get(['transaction_journals.*'])->sum('amount'));
} else {
// get all journals in this month where the asset account is NOT shared.
$sum = $budget->transactionjournals()
->before($end)
->after($start)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '!=', '"sharedAsset"')
->get(['transaction_journals.*'])
->sum('amount');
$sum = floatval($sum);
}
return $sum;
}
@@ -289,17 +308,6 @@ class BudgetRepository implements BudgetRepositoryInterface
return $newBudget;
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function sumBudgetExpensesInPeriod(Budget $budget, $start, $end)
{
return floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
}
/**
* @param Budget $budget

View File

@@ -27,12 +27,14 @@ interface BudgetRepositoryInterface
public function destroy(Budget $budget);
/**
* Takes tags into account.
*
* @param Budget $budget
* @param Carbon $date
*
* @return float
*/
public function expensesOnDay(Budget $budget, Carbon $date);
public function expensesOnDayCorrected(Budget $budget, Carbon $date);
/**
* @return Collection
@@ -123,12 +125,17 @@ interface BudgetRepositoryInterface
public function getWithoutBudgetSum(Carbon $start, Carbon $end);
/**
* @param Budget $budget
* @param Carbon $date
*
* Same as ::spentInPeriod but corrects journals for their amount (tags).
*
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return float
*/
public function spentInMonth(Budget $budget, Carbon $date);
public function spentInPeriodCorrected(Budget $budget, Carbon $start, Carbon $end, $shared = true);
/**
* @param array $data
@@ -137,15 +144,6 @@ interface BudgetRepositoryInterface
*/
public function store(array $data);
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function sumBudgetExpensesInPeriod(Budget $budget, $start, $end);
/**
* @param Budget $budget
* @param array $data

View File

@@ -4,7 +4,7 @@ namespace FireflyIII\Repositories\Category;
use Auth;
use Carbon\Carbon;
use DB;
use Crypt;
use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
@@ -58,33 +58,44 @@ class CategoryRepository implements CategoryRepositoryInterface
}
/**
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getCategoriesAndExpenses($start, $end)
public function getCategoriesAndExpensesCorrected($start, $end)
{
return TransactionJournal::
where('transaction_journals.user_id', Auth::user()->id)
->leftJoin(
'transactions',
function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end)
->where('categories.user_id', Auth::user()->id)
->after($start)
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('sum', 'DESC')
->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
$set = Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->before($end)
->where('categories.user_id', Auth::user()->id)
->after($start)
->transactionTypes(['Withdrawal'])
->groupBy('categories.id')
->get(['categories.id as category_id', 'categories.encrypted as category_encrypted', 'categories.name', 'transaction_journals.*']);
$result = [];
foreach ($set as $entry) {
$categoryId = intval($entry->category_id);
if (isset($result[$categoryId])) {
$result[$categoryId]['sum'] += floatval($entry->amount);
} else {
$isEncrypted = intval($entry->category_encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? trans('firefly.no_category') : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$result[$categoryId] = [
'name' => $name,
'sum' => floatval($entry->amount),
];
}
}
return $result;
}
/**
@@ -168,22 +179,54 @@ class CategoryRepository implements CategoryRepositoryInterface
* @param Carbon $start
* @param Carbon $end
*
* @param bool $shared
*
* @return float
*/
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end)
public function spentInPeriodCorrected(Category $category, Carbon $start, Carbon $end, $shared = false)
{
return floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
if ($shared === true) {
// shared is true.
// always ignore transfers between accounts!
$sum = floatval(
$category->transactionjournals()
->transactionTypes(['Withdrawal'])
->before($end)->after($start)->get(['transaction_journals.*'])->sum('amount')
);
} else {
// do something else, SEE budgets.
// get all journals in this month where the asset account is NOT shared.
$sum = $category->transactionjournals()
->before($end)
->after($start)
->transactionTypes(['Withdrawal'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '!=', '"sharedAsset"')
->get(['transaction_journals.*'])->sum('amount');
$sum = floatval($sum);
}
return $sum;
}
/**
* Corrected for tags
*
* @param Category $category
* @param Carbon $date
*
* @return float
*/
public function spentOnDaySum(Category $category, Carbon $date)
public function spentOnDaySumCorrected(Category $category, Carbon $date)
{
return floatval($category->transactionjournals()->onDate($date)->lessThan(0)->sum('amount')) * -1;
return floatval($category->transactionjournals()->onDate($date)->get(['transaction_journals.*'])->sum('amount'));
}
/**

View File

@@ -33,12 +33,14 @@ interface CategoryRepositoryInterface
public function getCategories();
/**
* Corrected for tags.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getCategoriesAndExpenses($start, $end);
public function getCategoriesAndExpensesCorrected($start, $end);
/**
* @param Category $category
@@ -71,21 +73,28 @@ interface CategoryRepositoryInterface
public function getWithoutCategory(Carbon $start, Carbon $end);
/**
* Corrected for tags.
*
* @param Category $category
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
*
* @param bool $shared
*
* @return float
*/
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end);
public function spentInPeriodCorrected(Category $category, Carbon $start, Carbon $end, $shared = false);
/**
*
* Corrected for tags.
*
* @param Category $category
* @param Carbon $date
*
* @return float
*/
public function spentOnDaySum(Category $category, Carbon $date);
public function spentOnDaySumCorrected(Category $category, Carbon $date);
/**
* @param array $data

View File

@@ -76,15 +76,20 @@ class JournalRepository implements JournalRepositoryInterface
*/
public function getAmountBefore(TransactionJournal $journal, Transaction $transaction)
{
return floatval(
$transaction->account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)
->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
->where('transaction_journals.order', '>=', $journal->order)
->where('transaction_journals.id', '!=', $journal->id)
->sum('transactions.amount')
);
$set = $transaction->account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)
->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
->where('transaction_journals.order', '>=', $journal->order)
->where('transaction_journals.id', '!=', $journal->id)
->get(['transactions.*']);
$sum = 0;
foreach ($set as $entry) {
$sum += $entry->amount;
}
return $sum;
}
/**
@@ -338,61 +343,86 @@ class JournalRepository implements JournalRepositoryInterface
*/
protected function storeAccounts(TransactionType $type, array $data)
{
$from = null;
$to = null;
$fromAccount = null;
$toAccount = null;
switch ($type->type) {
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
list($fromAccount, $toAccount) = $this->storeWithdrawalAccounts($data);
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
list($fromAccount, $toAccount) = $this->storeDepositAccounts($data);
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
$fromAccount = Account::find($data['account_from_id']);
$toAccount = Account::find($data['account_to_id']);
break;
}
if (is_null($to) || (!is_null($to) && is_null($to->id))) {
if (is_null($toAccount)) {
Log::error('"to"-account is null, so we cannot continue!');
App::abort(500, '"to"-account is null, so we cannot continue!');
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd
if (is_null($from) || (!is_null($from) && is_null($from->id))) {
if (is_null($fromAccount)) {
Log::error('"from"-account is null, so we cannot continue!');
App::abort(500, '"from"-account is null, so we cannot continue!');
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd
return [$from, $to];
return [$fromAccount, $toAccount];
}
/**
* @param array $data
*
* @return array
*/
protected function storeWithdrawalAccounts(array $data)
{
$fromAccount = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$toAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$toAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
return [$fromAccount, $toAccount];
}
/**
* @param array $data
*
* @return array
*/
protected function storeDepositAccounts(array $data)
{
$toAccount = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$fromAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$fromAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
return [$fromAccount, $toAccount];
}
}

View File

@@ -9,7 +9,6 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
use Illuminate\Support\Collection;
use Navigation;
/**
* Class PiggyBankRepository
@@ -19,57 +18,6 @@ use Navigation;
class PiggyBankRepository implements PiggyBankRepositoryInterface
{
/**
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition)
{
/** @var PiggyBank $piggyBank */
$piggyBank = $repetition->piggyBank()->first();
$bars = new Collection;
$currentStart = clone $repetition->startdate;
if (is_null($piggyBank->reminder)) {
$entry = ['repetition' => $repetition, 'amountPerBar' => floatval($piggyBank->targetamount),
'currentAmount' => floatval($repetition->currentamount), 'cumulativeAmount' => floatval($piggyBank->targetamount),
'startDate' => clone $repetition->startdate, 'targetDate' => clone $repetition->targetdate];
$bars->push($this->createPiggyBankPart($entry));
return $bars;
}
while ($currentStart < $repetition->targetdate) {
$currentTarget = Navigation::endOfX($currentStart, $piggyBank->reminder, $repetition->targetdate);
$entry = ['repetition' => $repetition, 'amountPerBar' => null, 'currentAmount' => floatval($repetition->currentamount),
'cumulativeAmount' => null, 'startDate' => $currentStart, 'targetDate' => $currentTarget];
$bars->push($this->createPiggyBankPart($entry));
$currentStart = clone $currentTarget;
$currentStart->addDay();
}
$amountPerBar = floatval($piggyBank->targetamount) / $bars->count();
$cumulative = $amountPerBar;
/** @var PiggyBankPart $bar */
foreach ($bars as $index => $bar) {
$bar->setAmountPerBar($amountPerBar);
$bar->setCumulativeAmount($cumulative);
if ($bars->count() - 1 == $index) {
$bar->setCumulativeAmount($piggyBank->targetamount);
}
$cumulative += $amountPerBar;
}
return $bars;
}
/**
* @param PiggyBank $piggyBank
* @param $amount
@@ -83,24 +31,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return true;
}
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data)
{
$part = new PiggyBankPart;
$part->setRepetition($data['repetition']);
$part->setAmountPerBar($data['amountPerBar']);
$part->setCurrentamount($data['currentAmount']);
$part->setCumulativeAmount($data['cumulativeAmount']);
$part->setStartdate($data['startDate']);
$part->setTargetdate($data['targetDate']);
return $part;
}
/**
* @param PiggyBank $piggyBank
*
@@ -151,8 +81,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
{
// split query to make it work in sqlite:
$set = PiggyBank::
leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id')
->where('accounts.user_id', Auth::user()->id)->get(['piggy_banks.*']);
leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id')
->where('accounts.user_id', Auth::user()->id)->get(['piggy_banks.*']);
foreach ($set as $e) {
$e->order = 0;
$e->save();
@@ -198,7 +128,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
* @param array $data
*
* @return PiggyBank
* @internal param PiggyBank $account
*/
public function update(PiggyBank $piggyBank, array $data)
{

View File

@@ -14,18 +14,6 @@ use Illuminate\Support\Collection;
interface PiggyBankRepositoryInterface
{
/**
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition);
/**
* @return Collection
*/
@@ -38,13 +26,6 @@ interface PiggyBankRepositoryInterface
*/
public function getEvents(PiggyBank $piggyBank);
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data);
/**
* @param PiggyBank $piggyBank
* @param $amount

View File

@@ -4,6 +4,8 @@ namespace FireflyIII\Repositories\Tag;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
@@ -17,7 +19,10 @@ use Illuminate\Support\Collection;
class TagRepository implements TagRepositoryInterface
{
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
*
* @param TransactionJournal $journal
* @param Tag $tag
*
@@ -33,88 +38,58 @@ class TagRepository implements TagRepositoryInterface
return false;
}
if ($tag->tagMode == 'nothing') {
// save it, no problem:
$journal->tags()->save($tag);
return true;
}
/*
* get some withdrawal types:
*/
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
/** @var TransactionType $deposit */
$deposit = TransactionType::whereType('Deposit')->first();
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count();
if ($tag->tagMode == 'balancingAct') {
// only if this is the only withdrawal.
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
switch ($tag->tagMode) {
case 'nothing':
$journal->tags()->save($tag);
return true;
}
// and only if this is the only transfer
if ($journal->transaction_type_id == $transfer->id && $transfers < 1) {
$journal->tags()->save($tag);
return true;
}
// ignore expense
return false;
break;
case 'balancingAct':
return $this->connectBalancingAct($journal, $tag);
break;
case 'advancePayment':
return $this->connectAdvancePayment($journal, $tag);
break;
}
if ($tag->tagMode == 'advancePayment') {
// advance payments cannot accept transfers:
if ($journal->transaction_type_id == $transfer->id) {
return false;
}
// the first transaction to be attached to this
// tag is attached just like that:
if ($withdrawals < 1 && $deposits < 1) {
$journal->tags()->save($tag);
return true;
}
// if withdrawal and already has a withdrawal, return false:
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals == 1) {
return false;
}
// if already has transaction journals, must match ALL asset account id's:
if ($deposits > 0 || $withdrawals == 1) {
$match = true;
/** @var TransactionJournal $check */
foreach ($tag->transactionjournals as $check) {
if ($check->assetAccount->id != $journal->assetAccount->id) {
$match = false;
}
}
if ($match) {
$journal->tags()->save($tag);
return true;
}
}
return false;
}
// @codeCoverageIgnoreStart
return false;
}
// @codeCoverageIgnoreEnd
/**
* This method scans the transaction journals from or to the given asset account
* and checks if these are part of a balancing act. If so, it will sum up the amounts
* transferred into the balancing act (if any) and return this amount.
*
* This method effectively tells you the amount of money that has been balanced out
* correctly in the given period for the given account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function coveredByBalancingActs(Account $account, Carbon $start, Carbon $end)
{
// the quickest way to do this is by scanning all balancingAct tags
// because there will be less of them any way.
$tags = Auth::user()->tags()->where('tagMode', 'balancingAct')->get();
$amount = 0;
/** @var Tag $tag */
foreach ($tags as $tag) {
$journals = $tag->transactionjournals()->after($start)->before($end)->transactionTypes(['Transfer'])->get(['transaction_journals.*']);
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
if ($journal->destination_account->id == $account->id) {
$amount += $journal->amount;
}
}
}
return $amount;
}
/**
* @param Tag $tag
@@ -127,6 +102,7 @@ class TagRepository implements TagRepositoryInterface
return true;
}
// @codeCoverageIgnoreEnd
/**
* @return Collection
@@ -186,4 +162,107 @@ class TagRepository implements TagRepositoryInterface
return $tag;
}
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
protected function connectBalancingAct(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
// only if this is the only withdrawal.
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
$journal->tags()->save($tag);
return true;
}
// and only if this is the only transfer
if ($journal->transaction_type_id == $transfer->id && $transfers < 1) {
$journal->tags()->save($tag);
return true;
}
// ignore expense
return false;
}
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
/** @var TransactionType $deposit */
$deposit = TransactionType::whereType('Deposit')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count();
// advance payments cannot accept transfers:
if ($journal->transaction_type_id == $transfer->id) {
return false;
}
// the first transaction to be attached to this
// tag is attached just like that:
if ($withdrawals < 1 && $deposits < 1) {
$journal->tags()->save($tag);
return true;
}
// if withdrawal and already has a withdrawal, return false:
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals == 1) {
return false;
}
// if already has transaction journals, must match ALL asset account id's:
if ($deposits > 0 || $withdrawals == 1) {
return $this->matchAll($journal, $tag);
}
return false;
}
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return bool
*/
protected function matchAll(TransactionJournal $journal, Tag $tag)
{
$match = true;
/** @var TransactionJournal $check */
foreach ($tag->transactionjournals as $check) {
if ($check->assetAccount->id != $journal->assetAccount->id) {
$match = false;
}
}
if ($match) {
$journal->tags()->save($tag);
return true;
}
return false;
}
}

View File

@@ -2,6 +2,8 @@
namespace FireflyIII\Repositories\Tag;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
@@ -15,6 +17,23 @@ use Illuminate\Support\Collection;
interface TagRepositoryInterface
{
/**
* This method scans the transaction journals from or to the given asset account
* and checks if these are part of a balancing act. If so, it will sum up the amounts
* transferred into the balancing act (if any) and return this amount.
*
* This method effectively tells you the amount of money that has been balanced out
* correctly in the given period for the given account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function coveredByBalancingActs(Account $account, Carbon $start, Carbon $end);
/**
* @param array $data
*

View File

@@ -84,32 +84,20 @@ class Amount
*/
public function formatJournal(TransactionJournal $journal, $coloured = true)
{
$showPositive = true;
if (is_null($journal->symbol)) {
$symbol = $journal->transactionCurrency->symbol;
} else {
$symbol = $journal->symbol;
}
$amount = 0;
if (is_null($journal->type)) {
$type = $journal->transactionType->type;
} else {
$type = $journal->type;
$amount = $journal->amount;
if ($journal->transactionType->type == 'Withdrawal') {
$amount = $amount * -1;
}
if ($type == 'Withdrawal') {
$showPositive = false;
if ($journal->transactionType->type == 'Transfer' && $coloured) {
return '<span class="text-info">' . $this->formatWithSymbol($symbol, $amount, false) . '</span>';
}
foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0 && $showPositive === true) {
$amount = floatval($t->amount);
break;
}
if (floatval($t->amount) < 0 && $showPositive === false) {
$amount = floatval($t->amount);
}
if ($journal->transactionType->type == 'Transfer' && !$coloured) {
return $this->formatWithSymbol($symbol, $amount, false);
}
return $this->formatWithSymbol($symbol, $amount, $coloured);

View File

@@ -51,29 +51,8 @@ class ExpandedForm
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',
'budget_id' => 'Budget',
'openingBalance' => 'Opening balance',
'tagMode' => 'Tag mode',
'tagPosition' => 'Tag location',
'virtualBalance' => 'Virtual balance',
'longitude_latitude' => 'Location',
'targetamount' => 'Target amount',
'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date',
'ccType' => 'Credit card payment plan',
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank'];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
return trans('form.' . $name);
}

View File

@@ -28,21 +28,11 @@ class Navigation
$add = ($skip + 1);
$functionMap = [
'1D' => 'addDays',
'daily' => 'addDays',
'1W' => 'addWeeks',
'weekly' => 'addWeeks',
'week' => 'addWeeks',
'1M' => 'addMonths',
'month' => 'addMonths',
'monthly' => 'addMonths',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYears',
'yearly' => 'addYears',
'1D' => 'addDays', 'daily' => 'addDays',
'1W' => 'addWeeks', 'weekly' => 'addWeeks', 'week' => 'addWeeks',
'1M' => 'addMonths', 'month' => 'addMonths', 'monthly' => 'addMonths', '3M' => 'addMonths',
'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths',
'year' => 'addYears', 'yearly' => 'addYears',
];
$modifierMap = [
'quarter' => 3,
@@ -75,21 +65,11 @@ class Navigation
$currentEnd = clone $theCurrentEnd;
$functionMap = [
'1D' => 'addDay',
'daily' => 'addDay',
'1W' => 'addWeek',
'week' => 'addWeek',
'weekly' => 'addWeek',
'1M' => 'addMonth',
'month' => 'addMonth',
'monthly' => 'addMonth',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYear',
'yearly' => 'addYear',
'1D' => 'addDay', 'daily' => 'addDay',
'1W' => 'addWeek', 'week' => 'addWeek', 'weekly' => 'addWeek',
'1M' => 'addMonth', 'month' => 'addMonth', 'monthly' => 'addMonth',
'3M' => 'addMonths', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths',
'year' => 'addYear', 'yearly' => 'addYear',
];
$modifierMap = [
'quarter' => 3,

View File

@@ -23,12 +23,9 @@ class Steam
*
* @return float
*/
public function balance(Account $account, Carbon $date = null, $ignoreVirtualBalance = false)
public function balance(Account $account, Carbon $date, $ignoreVirtualBalance = false)
{
$date = is_null($date) ? Carbon::now() : $date;
// find the first known transaction on this account:
//
$firstDateObject = $account
->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
@@ -37,22 +34,21 @@ class Steam
$firstDate = is_null($firstDateObject) ? clone $date : new Carbon($firstDateObject->date);
$date = $date < $firstDate ? $firstDate : $date;
/**
*select * from transactions
* left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
* order by date ASC
*/
$balance = floatval(
$account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
);
if (!$ignoreVirtualBalance) {
$balance += floatval($account->virtual_balance);
bcscale(2);
$set = $account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->get(['transactions.*']);
$balance = '0';
foreach ($set as $entry) {
$balance = bcadd($balance, $entry->amount);
}
return $balance;
if (!$ignoreVirtualBalance) {
$balance = bcadd($balance, $account->virtual_balance);
//$balance += floatval($account->virtual_balance);
}
return round($balance, 2);
}
/**

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Support\Twig;
use Auth;
use DB;
use FireflyIII\Models\LimitRepetition;
use Twig_Extension;
use Twig_SimpleFunction;
@@ -21,19 +20,16 @@ class Budget extends Twig_Extension
public function getFunctions()
{
$functions[] = new Twig_SimpleFunction(
'spentInRepetition', function (LimitRepetition $repetition) {
$sum = DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('transaction_journals.date', '>=', $repetition->startdate->format('Y-m-d'))
->where('transaction_journals.date', '<=', $repetition->enddate->format('Y-m-d'))
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('transactions.deleted_at')
->where('transactions.amount', '>', 0)
->where('limit_repetitions.id', '=', $repetition->id)
->sum('transactions.amount');
'spentInRepetitionCorrected', function (LimitRepetition $repetition) {
$sum
= Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->before($repetition->enddate)
->after($repetition->startdate)
->where('limit_repetitions.id', '=', $repetition->id)
->get(['transaction_journals.*'])->sum('amount');
return floatval($sum);
}

View File

@@ -3,10 +3,12 @@
namespace FireflyIII\Support\Twig;
use App;
use Carbon\Carbon;
use Config;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use Route;
use Session;
use Twig_Extension;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
@@ -21,6 +23,7 @@ class General extends Twig_Extension
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @return array
*/
public function getFilters()
@@ -56,8 +59,9 @@ class General extends Twig_Extension
if (is_null($account)) {
return 'NULL';
}
$date = Session::get('end', Carbon::now()->endOfMonth());
return App::make('steam')->balance($account);
return App::make('steam')->balance($account, $date);
}
);

View File

@@ -18,6 +18,7 @@ class Journal extends Twig_Extension
{
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @return array
*/
public function getFilters()
@@ -27,20 +28,25 @@ class Journal extends Twig_Extension
$filters[] = new Twig_SimpleFilter(
'typeIcon', function (TransactionJournal $journal) {
$type = $journal->transactionType->type;
if ($type == 'Withdrawal') {
return '<span class="glyphicon glyphicon-arrow-left" title="Withdrawal"></span>';
}
if ($type == 'Deposit') {
return '<span class="glyphicon glyphicon-arrow-right" title="Deposit"></span>';
}
if ($type == 'Transfer') {
return '<i class="fa fa-fw fa-exchange" title="Transfer"></i>';
}
if ($type == 'Opening balance') {
return '<span class="glyphicon glyphicon-ban-circle" title="Opening balance"></span>';
switch ($type) {
case 'Withdrawal':
return '<span class="glyphicon glyphicon-arrow-left" title="' . trans('firefly.withdrawal') . '"></span>';
break;
case 'Deposit':
return '<span class="glyphicon glyphicon-arrow-right" title="' . trans('firefly.deposit') . '"></span>';
break;
case 'Transfer':
return '<i class="fa fa-fw fa-exchange" title="' . trans('firefly.transfer') . '"></i>';
break;
case 'Opening balance':
return '<span class="glyphicon glyphicon-ban-circle" title="' . trans('firefly.openingBalance') . '"></span>';
break;
default:
return '';
break;
}
return '';
}, ['is_safe' => ['html']]
);
@@ -49,6 +55,8 @@ class Journal extends Twig_Extension
}
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @return array
*/
public function getFunctions()
@@ -70,13 +78,41 @@ class Journal extends Twig_Extension
if ($journal->tags->count() == 0) {
return App::make('amount')->formatJournal($journal);
}
foreach ($journal->tags as $tag) {
if ($tag->tagMode == 'balancingAct') {
// return tag formatted for a "balancing act".
// return tag formatted for a "balancing act", even if other
// tags are present.
$amount = App::make('amount')->format($journal->actual_amount, false);
return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</a>';
}
/*
* AdvancePayment with a deposit will show the tag instead of the amount:
*/
if ($tag->tagMode == 'advancePayment' && $journal->transactionType->type == 'Deposit') {
$amount = App::make('amount')->formatJournal($journal, false);
return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</span>';
. '"><i class="fa fa-fw fa-sort-numeric-desc"></i> ' . $tag->tag . '</a>';
}
/*
* AdvancePayment with a withdrawal will show the amount with a link to
* the tag. The TransactionJournal should properly calculate the amount.
*/
if ($tag->tagMode == 'advancePayment' && $journal->transactionType->type == 'Withdrawal') {
$amount = App::make('amount')->formatJournal($journal);
return '<a href="' . route('tags.show', $tag->id) . '">' . $amount . '</a>';
}
if ($tag->tagMode == 'nothing') {
// return the amount:
return App::make('amount')->formatJournal($journal);
}
}

View File

@@ -24,7 +24,7 @@ class Translation extends Twig_Extension
$filters[] = new Twig_SimpleFilter(
'_', function ($name) {
return trans('firefly.'.$name);
return trans('firefly.' . $name);
}, ['is_safe' => ['html']]
);

View File

@@ -29,10 +29,11 @@ class FireflyValidator extends Validator
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{
parent::__construct($translator, $data, $rules, $messages);
parent::__construct($translator, $data, $rules, $messages, $customAttributes);
}
/**

View File

@@ -21,7 +21,7 @@ return [
'defaultAsset' => 'Default asset account',
'sharedAsset' => 'Shared asset account',
'savingAsset' => 'Savings account',
'ccAsset' => 'Credit card',
'ccAsset' => 'Credit card',
],
'range_to_text' => [
@@ -32,7 +32,7 @@ return [
'6M' => 'half year',
'custom' => '(custom)'
],
'ccTypes' => [
'ccTypes' => [
'monthlyFull' => 'Full payment every month'
],
'range_to_name' => [
@@ -56,6 +56,7 @@ return [
'asset' => 'Asset accounts',
'expense' => 'Expense accounts',
'revenue' => 'Revenue accounts',
'cash' => 'Cash accounts',
],
'subIconsByIdentifier' =>
[
@@ -77,9 +78,9 @@ return [
],
'accountTypeByIdentifier' =>
[
'asset' => 'Asset account',
'expense' => 'Expense account',
'revenue' => 'Revenue account'
'asset' => 'Asset account',
'expense' => 'Expense account',
'revenue' => 'Revenue account'
],
'shortNamesByFullName' =>
[
@@ -89,6 +90,22 @@ return [
'Beneficiary account' => 'expense',
'Revenue account' => 'revenue',
'Cash account' => 'cash',
]
],
'lang' => [
'en' => 'English',
'nl' => 'Nederlands'
],
'locales' => [
'en' => ['en', 'English', 'en_US', 'en_US.utf8'],
'nl' => ['nl', 'Dutch', 'nl_NL', 'nl_NL.utf8'],
],
'month' => [
'en' => '%B %Y',
'nl' => '%B %Y',
],
'monthAndDay' => [
'en' => '%B %e, %Y',
'nl' => '%e %B %Y',
],
];

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