mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Compare commits
	
		
			211 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 42458ce11d | ||
|  | aceb683d07 | ||
|  | b7517b49ed | ||
|  | 849b711b79 | ||
|  | 25585b28c7 | ||
|  | 073da8fb2a | ||
|  | a787ff3f3c | ||
|  | 733b6d7eb7 | ||
|  | 36d8dee853 | ||
|  | 65a2e07d24 | ||
|  | 7c97c558ab | ||
|  | a6bb61050c | ||
|  | b184aa2315 | ||
|  | e4595333e7 | ||
|  | 41dd139bde | ||
|  | c577dd302a | ||
|  | 0ab87de78b | ||
|  | 8a22509b41 | ||
|  | b024c18441 | ||
|  | d9ac681a68 | ||
|  | 637a5579ec | ||
|  | 4794156e80 | ||
|  | 5f4db7874c | ||
|  | b4ea1839a5 | ||
|  | 6a6d889983 | ||
|  | 287c2e7af8 | ||
|  | 0fe6acc8cf | ||
|  | 7d2dab7ca0 | ||
|  | f68c1aff26 | ||
|  | 81662473a6 | ||
|  | d40645be68 | ||
|  | a53550537f | ||
|  | 223ad16616 | ||
|  | 3f060979d7 | ||
|  | 2eac9081ea | ||
|  | b3eef4f40b | ||
|  | dd70fbad3f | ||
|  | 8cb7a1aef8 | ||
|  | a687140056 | ||
|  | 3cba673a9c | ||
|  | 01de230785 | ||
|  | e405d06f23 | ||
|  | d9b70f7ad8 | ||
|  | 0ef5825d98 | ||
|  | 1e76a5fc3f | ||
|  | 1fbdb3d0ae | ||
|  | d5bcf5497f | ||
|  | 28aaea1aa3 | ||
|  | 980d9ce885 | ||
|  | ec601efa6e | ||
|  | b3209d3b4d | ||
|  | 4ce978b9f3 | ||
|  | a84064663a | ||
|  | 6798cea268 | ||
|  | 8e86196352 | ||
|  | 1b3d345fbd | ||
|  | 7d2627515f | ||
|  | aa9eb8ca64 | ||
|  | 9015d6ca16 | ||
|  | 217483639d | ||
|  | 78e80530d3 | ||
|  | 3bbecfe830 | ||
|  | 9ab3679d49 | ||
|  | fc44a52ba5 | ||
|  | bb2b71bdc0 | ||
|  | b23d2a9d95 | ||
|  | eeb773fd7b | ||
|  | 53a582f374 | ||
|  | 73110f6a51 | ||
|  | 5667663fef | ||
|  | fb664ba17d | ||
|  | 0c10190a8e | ||
|  | 183a323ef6 | ||
|  | 90bada5497 | ||
|  | 7c043e1923 | ||
|  | 2720ae3c46 | ||
|  | 401508577e | ||
|  | b0a31cebc2 | ||
|  | 95adb428fa | ||
|  | f92a0310dd | ||
|  | 84f0cb3765 | ||
|  | d49e6787d6 | ||
|  | 0884853a6f | ||
|  | 1967c63006 | ||
|  | 9461e7b70a | ||
|  | f1e5df566c | ||
|  | fb02a0d5ad | ||
|  | e438a02fa3 | ||
|  | b112452aa1 | ||
|  | 1a2fc81af3 | ||
|  | 38bbda982c | ||
|  | 41ad6e64d1 | ||
|  | efcad0b935 | ||
|  | e892b69a96 | ||
|  | 5dfc04e777 | ||
|  | c119a42d70 | ||
|  | 802541b796 | ||
|  | 0770c79777 | ||
|  | 5f4669341e | ||
|  | f15fc80233 | ||
|  | a7d75ea94a | ||
|  | ba4bddf756 | ||
|  | 6a26408552 | ||
|  | c39c59fff5 | ||
|  | c1ba8dc6a7 | ||
|  | f2825da878 | ||
|  | c61f1307d8 | ||
|  | 9e88d7a60d | ||
|  | 406ae25162 | ||
|  | dbfb342021 | ||
|  | 4632142e06 | ||
|  | 9ae036f297 | ||
|  | 497b8c48c8 | ||
|  | 5d11949313 | ||
|  | a91c9f04c5 | ||
|  | 4f3493f9ff | ||
|  | 49b8742082 | ||
|  | 69cee59e23 | ||
|  | 19402b9022 | ||
|  | 62ba40b687 | ||
|  | f9af9a4fbe | ||
|  | c2ab43d0ab | ||
|  | af28e6e7b9 | ||
|  | 114ad7f292 | ||
|  | 44eb67f94e | ||
|  | 0203fee174 | ||
|  | a1ba340ead | ||
|  | 0ae9ff4575 | ||
|  | 5b501cb942 | ||
|  | 0255b7a4a0 | ||
|  | 15ef0bab1d | ||
|  | decad6830b | ||
|  | b6e0b985c2 | ||
|  | c140f71878 | ||
|  | 87044e6b8e | ||
|  | affa9014d2 | ||
|  | 4bbc3c3bd8 | ||
|  | d296dbbc23 | ||
|  | 9bcd27b847 | ||
|  | 2a54b36db0 | ||
|  | 77fb02daa4 | ||
|  | 1963ac191f | ||
|  | 33da8aa987 | ||
|  | 0192484044 | ||
|  | 3c0863d8ea | ||
|  | 710d6dfb74 | ||
|  | 2359542d72 | ||
|  | e1a2b4b9af | ||
|  | 0eadfa1c83 | ||
|  | c8dd935460 | ||
|  | e2227271b5 | ||
|  | 7a639a1d6e | ||
|  | 9edb9b91b2 | ||
|  | b2adeb20d9 | ||
|  | fa665de847 | ||
|  | ab9e5f716d | ||
|  | 5788db9f07 | ||
|  | 3068a8d58d | ||
|  | 14aacf42b9 | ||
|  | d1b97da309 | ||
|  | 867074e7b2 | ||
|  | 18748510b1 | ||
|  | bcf71cdf85 | ||
|  | 3290ce85a9 | ||
|  | 60ef80c1a5 | ||
|  | 74e852b8bd | ||
|  | 90ae21d257 | ||
|  | fdf03cd8e2 | ||
|  | f6586be5e7 | ||
|  | f9dc627d84 | ||
|  | 309177ca9c | ||
|  | 456d2342b6 | ||
|  | 0717aa22d7 | ||
|  | b8e07ac38e | ||
|  | c7273e4b60 | ||
|  | 33d4fd4af0 | ||
|  | 71b11e26d2 | ||
|  | 77f4111b09 | ||
|  | 9965297f36 | ||
|  | 5ca466a826 | ||
|  | 90f417facc | ||
|  | eacbd038b7 | ||
|  | 5446e85424 | ||
|  | 77b4942691 | ||
|  | 824cf71e0b | ||
|  | 239bbd30c0 | ||
|  | 6f6b653d54 | ||
|  | e4155ce735 | ||
|  | 7eaf307834 | ||
|  | 7db7950415 | ||
|  | fcc184cd2a | ||
|  | 6423feff3a | ||
|  | e97da25d5a | ||
|  | f49a37a38e | ||
|  | 07e6b33095 | ||
|  | 9136b50e3c | ||
|  | c3fd5c7136 | ||
|  | 98612dd253 | ||
|  | 4d7f5238dd | ||
|  | f472a01a80 | ||
|  | 420b5790e3 | ||
|  | 2e342e47a7 | ||
|  | 58b3334f05 | ||
|  | 4cd955e5cf | ||
|  | 4dbf410cdf | ||
|  | 074e6d52b1 | ||
|  | a0a36c5137 | ||
|  | 9db4137a1b | ||
|  | 85f1e744b8 | ||
|  | 8d90061d90 | ||
|  | b20d84e4b8 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -11,4 +11,6 @@ tests/_output/* | ||||
| _ide_helper.php | ||||
| /build/logs/clover.xml | ||||
| index.html* | ||||
| app/storage/firefly-export* | ||||
| app/storage/firefly-export* | ||||
| .vagrant | ||||
| firefly-iii-import-*.json | ||||
|   | ||||
							
								
								
									
										53
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,7 +2,6 @@ firefly-iii | ||||
| =========== | ||||
|  | ||||
| [](https://travis-ci.org/JC5/firefly-iii) | ||||
| [](https://coveralls.io/r/JC5/firefly-iii?branch=master) | ||||
|  | ||||
|  | ||||
| [](https://packagist.org/packages/grumpydictator/firefly-iii) | ||||
| @@ -14,42 +13,52 @@ Firefly Mark III is a new version of Firefly built upon best practices and lesso | ||||
| from building [Firefly](https://github.com/JC5/Firefly). It's Mark III since the original Firefly never made it outside of my | ||||
| laptop and [Firefly II](https://github.com/JC5/Firefly) is live. | ||||
|  | ||||
| ## Current features | ||||
|  | ||||
| - [A double-entry bookkeeping system](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system). | ||||
| - You can store, edit and remove withdrawals, deposits and transfers. This allows you full financial management; | ||||
| - It's possible to create, change and manage money using _budgets_; | ||||
| - Organize transactions using categories; | ||||
| - Save towards a goal using piggy banks; | ||||
| - Predict and anticipate large expenses using "repeated expenses" (ie. yearly taxes); | ||||
| - Predict and anticipate bills using "recurring transactions" (rent for example). | ||||
|  | ||||
| Everything is organised: | ||||
|  | ||||
| - Clear views that should show you how you're doing; | ||||
| - Easy navigation through your records; | ||||
| - Browse back and forth to see previous months or even years; | ||||
| - Lots of help text in case you don't get it; | ||||
| - Lots of charts because we all love them. | ||||
|  | ||||
| ## Changes | ||||
|  | ||||
| Firefly III will feature: | ||||
| Firefly III will feature, but does not feature yet: | ||||
|  | ||||
| - Double-entry bookkeeping system; | ||||
| - Better budgeting tools; | ||||
| - Better financial reporting; | ||||
| - Financial reporting showing you how well you are doing; | ||||
| - More control over other resources outside of personal finance | ||||
|   - Accounts shared with a partner (household accounts) | ||||
|   - Debts | ||||
|   - Credit cards | ||||
| - More robust code base (mainly for my own peace of mind); | ||||
| - More test-coverage (aka: actual test coverage); | ||||
|  | ||||
| ## More features | ||||
|   | ||||
| - Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control. | ||||
| - Firefly will be able to join transactions. | ||||
| - Transfers and transactions will be combined into one internal datatype which is more consistent with what you're actually doing: moving money from A to B. The fact that A or B or both are yours should not matter. And it will not, in the future. | ||||
| - The nesting of budgets, categories and beneficiaries will be removed. | ||||
| - Firefly will be able to automatically login a specified account. Although this is pretty unsafe, it removes the need for you to login to your own tool.  | ||||
| - Transfers and transactions are combined into one internal datatype which is more consistent with what you're actually doing: moving money from A to B. The fact that A or B or both are yours should not matter. | ||||
| - Any other features I might not have thought of. | ||||
|  | ||||
| ## Not changed | ||||
| Some stuff has been removed: | ||||
|  | ||||
| - The nesting of budgets, categories and beneficiaries is removed because it was pretty pointless. | ||||
| - Firefly will not encrypt the content of the (MySQL) tables. Old versions of Firefly had this capability but it sucks when searching, sorting and organizing entries. | ||||
|  | ||||
| ## Current state | ||||
| I have the basics up and running and test coverage is doing very well. | ||||
| I have the basics up and running. Test coverage is currently non-existent. | ||||
|  | ||||
| Current issues are the consistent look-and-feel of forms and likewise, the consistent inner workings of most of Firefly. | ||||
| Example: every "create"-action tends to be slightly different from the rest. Also is the fact that not all lists | ||||
| and forms are equally well thought of; some are not looking very well or miss feedback. | ||||
| Although I have not checked extensively, some forms and views have CSRF vulnerabilities. This is because not all | ||||
| views escape all characters by default. Will be fixed. | ||||
|  | ||||
| Most forms will not allow you to enter invalid data because the database cracks, not because it's actually checked. | ||||
| I'm still thinking about a way to build consistent forms. Laravel doesn't really cut it. | ||||
| The current layout / look & feel is a pretty basic Bootstrap3 template. I am currently working on a more consistent, | ||||
| expanded layout which will feature shiny AJAX things and data tables and all the Web 3.0 goodies you've come to expect | ||||
| from social media sites. | ||||
|  | ||||
| A lot of views have CSRF vulnerabilities. The general advice is NOT to use this tool in production. | ||||
|  | ||||
| Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)! | ||||
| Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)! | ||||
| @@ -1 +0,0 @@ | ||||
| If you place an image here called foobar.png then you can access that image by going to http://<hostname>/assets/foobar.png | ||||
| @@ -1,16 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require highslide/highslide-full.min | ||||
| //= require highslide/highslide.config | ||||
| //= require_tree highcharts | ||||
| //= require firefly/accounts | ||||
| @@ -1,15 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require jquery | ||||
| //= require bootstrap/bootstrap.min | ||||
| //= require firefly/reminders | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require_tree highcharts | ||||
| //= require firefly/budgets/default | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require_tree highcharts | ||||
| //= require firefly/budgets/limit | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require_tree highcharts | ||||
| //= require firefly/budgets/nolimit | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require_tree highcharts | ||||
| //= require firefly/budgets/session | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require_tree highcharts | ||||
| //= require firefly/budgets | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require_tree highcharts | ||||
| //= require firefly/categories | ||||
| @@ -1,95 +0,0 @@ | ||||
| $(function () { | ||||
| if($('#chart').length == 1) { | ||||
|     /** | ||||
|      * get data from controller for home charts: | ||||
|      */ | ||||
|     $.getJSON('chart/home/account/' + accountID).success(function (data) { | ||||
|         var options = { | ||||
|             chart: { | ||||
|                 renderTo: 'chart', | ||||
|                 type: 'spline' | ||||
|             }, | ||||
|  | ||||
|             series: data.series, | ||||
|             title: { | ||||
|                 text: data.chart_title | ||||
|             }, | ||||
|             yAxis: { | ||||
|                 formatter: function () { | ||||
|                     return '$' + Highcharts.numberFormat(this.y, 0); | ||||
|                 } | ||||
|             }, | ||||
|             subtitle: { | ||||
|                 text: data.subtitle, | ||||
|                 useHTML: true | ||||
|             }, | ||||
|  | ||||
|             xAxis: { | ||||
|                 floor: 0, | ||||
|                 type: 'datetime', | ||||
|                 dateTimeLabelFormats: { | ||||
|                     day: '%e %b', | ||||
|                     year: '%b' | ||||
|                 }, | ||||
|                 title: { | ||||
|                     text: 'Date' | ||||
|                 } | ||||
|             }, | ||||
|             tooltip: { | ||||
|                 shared: true, | ||||
|                 crosshairs: false, | ||||
|                 formatter: function () { | ||||
|                     var str = '<span style="font-size:80%;">' + Highcharts.dateFormat("%A, %e %B", this.x) + '</span><br />'; | ||||
|                     for (x in this.points) { | ||||
|                         var point = this.points[x]; | ||||
|                         var colour = point.point.pointAttr[''].fill; | ||||
|                         str += '<span style="color:' + colour + '">' + point.series.name + '</span>: € ' + Highcharts.numberFormat(point.y, 2) + '<br />'; | ||||
|                     } | ||||
|                     //console.log(); | ||||
|                     return str; | ||||
|                     return '<span style="font-size:80%;">' + this.series.name + ' on ' + Highcharts.dateFormat("%e %B", this.x) + ':</span><br /> € ' + Highcharts.numberFormat(this.y, 2); | ||||
|                 } | ||||
|             }, | ||||
|             plotOptions: { | ||||
|                 line: { | ||||
|                     shadow: true | ||||
|                 }, | ||||
|                 series: { | ||||
|                     cursor: 'pointer', | ||||
|                     negativeColor: '#FF0000', | ||||
|                     threshold: 0, | ||||
|                     lineWidth: 1, | ||||
|                     marker: { | ||||
|                         radius: 2 | ||||
|                     }, | ||||
|                     point: { | ||||
|                         events: { | ||||
|                             click: function (e) { | ||||
|                                 hs.htmlExpand(null, { | ||||
|                                         src: 'chart/home/info/' + this.series.name + '/' + Highcharts.dateFormat("%d/%m/%Y", this.x), | ||||
|                                         pageOrigin: { | ||||
|                                             x: e.pageX, | ||||
|                                             y: e.pageY | ||||
|                                         }, | ||||
|                                         objectType: 'ajax', | ||||
|                                         headingText: '<a href="#">' + this.series.name + '</a>', | ||||
|                                         width: 250 | ||||
|                                     } | ||||
|                                 ) | ||||
|                                 ; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             credits: { | ||||
|                 enabled: false | ||||
|             } | ||||
|         }; | ||||
|         $('#chart').highcharts(options); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| }); | ||||
| @@ -1,4 +0,0 @@ | ||||
| $(function () { | ||||
|  | ||||
|  | ||||
| }); | ||||
| @@ -1,7 +0,0 @@ | ||||
| $.getJSON('json/beneficiaries').success(function (data) { | ||||
|     $('input[name="beneficiary"]').typeahead({ source: data }); | ||||
| }); | ||||
|  | ||||
| $.getJSON('json/categories').success(function (data) { | ||||
|     $('input[name="category"]').typeahead({ source: data }); | ||||
| }); | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,12 +0,0 @@ | ||||
| /** | ||||
| *	Site-specific configuration settings for Highslide JS | ||||
| */ | ||||
| hs.graphicsDir = 'assets/highslide/'; | ||||
| hs.outlineType = 'rounded-white'; | ||||
| hs.wrapperClassName = 'draggable-header'; | ||||
| hs.captionEval = 'this.a.title'; | ||||
| hs.showCredits = false; | ||||
| hs.marginTop = 20; | ||||
| hs.marginRight = 20; | ||||
| hs.marginBottom = 20; | ||||
| hs.marginLeft = 20; | ||||
| @@ -1,16 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require highslide/highslide-full.min | ||||
| //= require highslide/highslide.config | ||||
| //= require_tree highcharts | ||||
| //= require firefly/index | ||||
| @@ -1,13 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require firefly/piggybanks-create | ||||
| @@ -1,13 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require firefly/piggybanks | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require tagsinput/bootstrap-tagsinput.min | ||||
| //= require firefly/recurring | ||||
| @@ -1,14 +0,0 @@ | ||||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||||
| // listed below. | ||||
| // | ||||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
| // can be referenced here using a relative path. | ||||
| // | ||||
| // It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
| // gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
| // but before any files alphabetically greater than 'application.js'  | ||||
| // | ||||
| // The available directives right now are require, require_directory, and require_tree | ||||
| // | ||||
| //= require typeahead/bootstrap3-typeahead.min | ||||
| //= require firefly/transactions | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,13 +0,0 @@ | ||||
| /** | ||||
|  * This is a manifest file that'll be compiled into application.css, which will include all the files | ||||
|  * listed below. | ||||
|  * | ||||
|  * Any Css/Less files within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
|  * can be referenced here using a relative path. | ||||
|  * | ||||
|  * It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
|  * gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
|  * but before any files alphabetically greater than 'application.css'  | ||||
|  * | ||||
|  *= require highslide/highslide | ||||
|  */ | ||||
| @@ -1,13 +0,0 @@ | ||||
| /** | ||||
|  * This is a manifest file that'll be compiled into application.css, which will include all the files | ||||
|  * listed below. | ||||
|  * | ||||
|  * Any Css/Less files within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
|  * can be referenced here using a relative path. | ||||
|  * | ||||
|  * It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
|  * gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
|  * but before any files alphabetically greater than 'application.css'  | ||||
|  * | ||||
|  *= require bootstrap/bootstrap.min | ||||
|  */ | ||||
| @@ -1,13 +0,0 @@ | ||||
| /** | ||||
|  * This is a manifest file that'll be compiled into application.css, which will include all the files | ||||
|  * listed below. | ||||
|  * | ||||
|  * Any Css/Less files within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
|  * can be referenced here using a relative path. | ||||
|  * | ||||
|  * It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
|  * gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
|  * but before any files alphabetically greater than 'application.css'  | ||||
|  * | ||||
|  *= require highslide/highslide | ||||
|  */ | ||||
| @@ -1,13 +0,0 @@ | ||||
| /** | ||||
|  * This is a manifest file that'll be compiled into application.css, which will include all the files | ||||
|  * listed below. | ||||
|  * | ||||
|  * Any Css/Less files within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||||
|  * can be referenced here using a relative path. | ||||
|  * | ||||
|  * It's not advisable to add code directly here, but if you do, it'll appear in whatever order it  | ||||
|  * gets included (e.g. say you have require_tree . then the code will appear after all the directories  | ||||
|  * but before any files alphabetically greater than 'application.css'  | ||||
|  * | ||||
|  *= require tagsinput/bootstrap-tagsinput | ||||
|  */ | ||||
							
								
								
									
										7
									
								
								app/breadcrumbs.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/breadcrumbs.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| <?php | ||||
| /* | ||||
|  * Back home. | ||||
|  */ | ||||
| Breadcrumbs::register('home', function($breadcrumbs) { | ||||
|     $breadcrumbs->push('Home', route('index')); | ||||
| }); | ||||
							
								
								
									
										3
									
								
								app/config/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								app/config/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | ||||
| local/ | ||||
| laptop/ | ||||
| laptop/ | ||||
| vagrant/ | ||||
| @@ -6,6 +6,7 @@ return [ | ||||
|     'timezone'        => 'UTC', | ||||
|     'locale'          => 'en', | ||||
|     'fallback_locale' => 'en', | ||||
|     'log_level'       => 'notice', | ||||
|     'key'             => 'D93oqmVsIARg23FC3cbsHuBGk0uXQc3r', | ||||
|     'cipher'          => MCRYPT_RIJNDAEL_128, | ||||
|     'providers'       => [ | ||||
| @@ -36,11 +37,13 @@ return [ | ||||
|         'Illuminate\Validation\ValidationServiceProvider', | ||||
|         'Illuminate\View\ViewServiceProvider', | ||||
|         'Illuminate\Workbench\WorkbenchServiceProvider', | ||||
| #        'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', | ||||
| #        'Barryvdh\Debugbar\ServiceProvider', | ||||
|         'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', | ||||
|         'Barryvdh\Debugbar\ServiceProvider', | ||||
|         'Firefly\Storage\StorageServiceProvider', | ||||
|         'Firefly\Helper\HelperServiceProvider', | ||||
|         'Codesleeve\AssetPipeline\AssetPipelineServiceProvider', | ||||
|         'Firefly\Validation\ValidationServiceProvider', | ||||
|         'DaveJamesMiller\Breadcrumbs\ServiceProvider', | ||||
|         'TwigBridge\ServiceProvider' | ||||
|     ], | ||||
|     'manifest'        => storage_path() . '/meta', | ||||
|     'aliases'         => [ | ||||
| @@ -83,6 +86,8 @@ return [ | ||||
|         'URL'               => 'Illuminate\Support\Facades\URL', | ||||
|         'Validator'         => 'Illuminate\Support\Facades\Validator', | ||||
|         'View'              => 'Illuminate\Support\Facades\View', | ||||
|         'Breadcrumbs'       => 'DaveJamesMiller\Breadcrumbs\Facade', | ||||
|         'Twig'              => 'TwigBridge\Facade\Twig', | ||||
|  | ||||
|     ], | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| use Carbon\Carbon; | ||||
|  | ||||
| return [ | ||||
|     'index_periods'          => ['1D', '1W', '1M', '3M', '6M', 'custom'], | ||||
|     'index_periods'          => ['1D', '1W', '1M', '3M', '6M','1Y', 'custom'], | ||||
|     'budget_periods'         => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'], | ||||
|     'piggybank_periods'      => ['day', 'week', 'month', 'year'], | ||||
|     'periods_to_text'        => [ | ||||
| @@ -21,6 +21,14 @@ return [ | ||||
|         '6M'     => 'half year', | ||||
|         'custom' => '(custom)' | ||||
|     ], | ||||
|     'range_to_name'          => [ | ||||
|         '1D'     => 'one day', | ||||
|         '1W'     => 'one week', | ||||
|         '1M'     => 'one month', | ||||
|         '3M'     => 'three months', | ||||
|         '6M'     => 'six months', | ||||
|         '1Y'     => 'one year', | ||||
|     ], | ||||
|     'range_to_repeat_freq'   => [ | ||||
|         '1D'     => 'weekly', | ||||
|         '1W'     => 'weekly', | ||||
|   | ||||
| @@ -1,331 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /* | ||||
| |-------------------------------------------------------------------------- | ||||
| | EnvironmentFilter | ||||
| |-------------------------------------------------------------------------- | ||||
| | | ||||
| | This is used to run filters on specific environments. For example, if you | ||||
| | only want to run a filter on production and staging environments | ||||
| | | ||||
| | new EnvironmentFilter(new FilterExample, App::environment(), ['production', 'staging')), | ||||
| | | ||||
| */ | ||||
| use Codesleeve\AssetPipeline\Filters\EnvironmentFilter; | ||||
|  | ||||
| return [ | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| routing array | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| This is passed to the Route::group and allows us to group and filter the | ||||
| 	| routes for our package | ||||
| 	| | ||||
| 	*/ | ||||
| 	'routing' => [ | ||||
| 		'prefix' => '/assets' | ||||
|     ], | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| paths | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| These are the directories we search for files in. | ||||
| 	| | ||||
| 	| NOTE that the '.' in require_tree . is relative to where the manifest file | ||||
| 	| (i.e. app/assets/javascripts/application.js) is located | ||||
| 	| | ||||
| 	*/ | ||||
| 	'paths' => [ | ||||
| 		'app/assets/javascripts', | ||||
| 		'app/assets/stylesheets', | ||||
| 		'app/assets/images', | ||||
| 		'lib/assets/javascripts', | ||||
| 		'lib/assets/stylesheets', | ||||
| 		'lib/assets/images', | ||||
| 		'provider/assets/javascripts', | ||||
| 		'provider/assets/stylesheets', | ||||
| 		'provider/assets/images' | ||||
|     ], | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| mimes | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| In order to know which mime type to send back to the server | ||||
| 	| we need to know if it is a javascript or stylesheet type. If | ||||
| 	| the extension is not found below then we just return a regular | ||||
| 	| download. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'mimes' => [ | ||||
| 	    'javascripts' => ['.js', '.js.coffee', '.coffee', '.html', '.min.js'], | ||||
| 	    'stylesheets' => ['.css', '.css.less', '.css.sass', '.css.scss', '.less', '.sass', '.scss', '.min.css'], | ||||
|     ], | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| filters | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| In order for a file to be included with sprockets, it needs to be listed | ||||
| 	| here and we can also do any preprocessing on files with the extension if | ||||
| 	| we choose to. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'filters' => [ | ||||
| 		'.min.js' => [ | ||||
|  | ||||
| 		], | ||||
| 		'.min.css' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 		], | ||||
| 		'.js' => [ | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\JSMinPlusFilter, App::environment()), | ||||
| 		], | ||||
| 		'.js.coffee' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\CoffeeScript, | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\JSMinPlusFilter, App::environment()), | ||||
| 		], | ||||
| 		'.coffee' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\CoffeeScript, | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\JSMinPlusFilter, App::environment()), | ||||
| 		], | ||||
| 		'.css' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\CssMinFilter, App::environment()), | ||||
|         ], | ||||
| 		'.css.less' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\LessphpFilter, | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\CssMinFilter, App::environment()), | ||||
|         ], | ||||
| 		'.css.sass' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\SassFilter, | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\CssMinFilter, App::environment()), | ||||
|         ], | ||||
| 		'.css.scss' => [ | ||||
| 			new Assetic\Filter\ScssphpFilter, | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\CssMinFilter, App::environment()), | ||||
|         ], | ||||
| 		'.less' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\LessphpFilter, | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\CssMinFilter, App::environment()), | ||||
|         ], | ||||
| 		'.sass' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\SassFilter, | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\CssMinFilter, App::environment()), | ||||
|         ], | ||||
| 		'.scss' => [ | ||||
| 			new Assetic\Filter\ScssphpFilter, | ||||
| 			new Codesleeve\AssetPipeline\Filters\URLRewrite(App::make('url')->to('/')), | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\CssMinFilter, App::environment()), | ||||
|         ], | ||||
| 		'.html' => [ | ||||
| 			new Codesleeve\AssetPipeline\Filters\JST, | ||||
| 			new EnvironmentFilter(new Codesleeve\AssetPipeline\Filters\JSMinPlusFilter, App::environment()), | ||||
|         ] | ||||
|     ], | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| cache | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| By default we cache assets on production environment permanently. We also cache | ||||
| 	| all files using the `cache_server` driver below but the cache is busted anytime | ||||
| 	| those files are modified. On production we will cache and the only way to bust | ||||
| 	| the cache is to delete files from app/storage/cache/asset-pipeline or run a | ||||
| 	| command php artisan assets:clean -f somefilename.js -f application.css ... | ||||
| 	| | ||||
| 	*/ | ||||
| 	'cache' => 	['production'], | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| cache_server | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| You can create your own CacheInterface if the filesystem cache is not up to | ||||
| 	| your standards. This is for caching asset files on the server-side. | ||||
| 	| | ||||
| 	| Please note that caching is used on **ALL** environments always. This is done | ||||
| 	| to increase performance of the pipeline. Cached files will be busted when the | ||||
| 	| file changes. | ||||
| 	| | ||||
| 	| However, manifest files are regenerated (not cached) when the environment is | ||||
| 	| not found within the 'cache' array. This lets you develop on local and still | ||||
| 	| utilize caching, so you don't have to regenerate all precompiled files while | ||||
| 	| developing on your assets. | ||||
| 	| | ||||
| 	| See more in CacheInterface.php at | ||||
| 	| | ||||
| 	|    https://github.com/kriswallsmith/assetic/blob/master/src/Assetic/Cache | ||||
| 	| | ||||
| 	| | ||||
| 	*/ | ||||
| 	'cache_server' => new Assetic\Cache\FilesystemCache(App::make('path.storage') . '/cache/asset-pipeline'), | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| cache_client | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| If you want to handle 304's and what not, to keep users from refetching | ||||
| 	| your assets and saving your bandwidth you can use a cache_client driver | ||||
| 	| that handles this. This doesn't handle assets on the server-side, use | ||||
| 	| cache_server for that. This only works when the current environment is | ||||
| 	| listed within `cache` | ||||
| 	| | ||||
| 	| Note that this needs to implement the interface | ||||
| 	| | ||||
| 	|	Codesleeve\Sprockets\Interfaces\ClientCacheInterface | ||||
| 	| | ||||
| 	| or this won't work correctly. It is a wrapper class around your cache_server | ||||
| 	| driver and also uses the AssetCache class to help access files. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'cache_client' => new Codesleeve\AssetPipeline\Filters\ClientCacheFilter, | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| concat | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| This allows us to turn on the asset concatenation for specific | ||||
| 	| environments listed below. You can turn off local environment if | ||||
| 	| you are trying to troubleshoot, but you will likely have better | ||||
| 	| performance if you leave concat on (except if you are doing a lot | ||||
| 	| of minification stuff on each page refresh) | ||||
| 	| | ||||
| 	*/ | ||||
| 	'concat' => ['production', 'local'], | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| directives | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| This allows us to turn completely control which directives are used | ||||
| 	| for the sprockets parser that asset pipeline uses to parse manifest files. | ||||
| 	| | ||||
| 	| It is probably safe just to leave this alone unless you are familar with | ||||
| 	| what is actually going on here. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'directives' => [ | ||||
| 		'require ' => new Codesleeve\Sprockets\Directives\RequireFile, | ||||
| 		'require_directory ' => new Codesleeve\Sprockets\Directives\RequireDirectory, | ||||
| 		'require_tree ' => new Codesleeve\Sprockets\Directives\RequireTree, | ||||
| 		'require_tree_df ' => new Codesleeve\Sprockets\Directives\RequireTreeDf, | ||||
| 		'require_self' => new Codesleeve\Sprockets\Directives\RequireSelf, | ||||
| 		'include ' => new Codesleeve\Sprockets\Directives\IncludeFile, | ||||
| 		'include_directory ' => new Codesleeve\Sprockets\Directives\IncludeDirectory, | ||||
| 		'include_tree ' => new Codesleeve\Sprockets\Directives\IncludeTree, | ||||
| 		'stub ' => new Codesleeve\Sprockets\Directives\Stub, | ||||
| 		'depend_on ' => new Codesleeve\Sprockets\Directives\DependOn, | ||||
|     ], | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| javascript_include_tag | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| This allows us to completely control how the javascript_include_tag function | ||||
| 	| works for asset pipeline. | ||||
| 	| | ||||
| 	| It is probably safe just to leave this alone unless you are familar with | ||||
| 	| what is actually going on here. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'javascript_include_tag' => new Codesleeve\AssetPipeline\Composers\JavascriptComposer, | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| stylesheet_link_tag | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| This allows us to completely control how the stylesheet_link_tag function | ||||
| 	| works for asset pipeline. | ||||
| 	| | ||||
| 	| It is probably safe just to leave this alone unless you are familar with | ||||
| 	| what is actually going on here. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'stylesheet_link_tag' => new Codesleeve\AssetPipeline\Composers\StylesheetComposer, | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| image_tag | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| This allows us to completely control how the image_tag function | ||||
| 	| works for asset pipeline. | ||||
| 	| | ||||
| 	| It is probably safe just to leave this alone unless you are familar with | ||||
| 	| what is actually going on here. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'image_tag' => new Codesleeve\AssetPipeline\Composers\ImageComposer, | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| controller_action | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| Asset pipeline will route all requests through the controller action | ||||
| 	| listed here. This allows us to completely control how the controller | ||||
| 	| should behave for incoming requests for assets. | ||||
| 	| | ||||
| 	| It is probably safe just to leave this alone unless you are familar with | ||||
| 	| what is actually going on here. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'controller_action' => '\Codesleeve\AssetPipeline\AssetPipelineController@file', | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| sprockets_filter | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| When concatenation is turned on, when an asset is fetched from the sprockets | ||||
| 	| generator it is filtered through this filter class named below. This allows us | ||||
| 	| to modify the sprockets filter if we need to behave differently. | ||||
| 	| | ||||
| 	| It is probably safe just to leave this alone unless you are familar with | ||||
| 	| what is actually going on here. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'sprockets_filter' => '\Codesleeve\Sprockets\SprocketsFilter', | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| sprockets_filter | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| When concatenation is turned on, assets are filtered via SprocketsFilter | ||||
| 	| and we can do global filters on the resulting dump file. This would be | ||||
| 	| useful if you wanted to apply a filter to all javascript or stylesheet files | ||||
| 	| like minification. Out of the box we don't have any filters here. Add at | ||||
| 	| your own risk. I don't put minification filters here because the minify | ||||
| 	| doesn't always work perfectly and can bjork your entire concatenated | ||||
| 	| javascript or stylesheet file if it messes up. | ||||
| 	| | ||||
| 	| It is probably safe just to leave this alone unless you are familar with | ||||
| 	| what is actually going on here. | ||||
| 	| | ||||
| 	*/ | ||||
| 	'sprockets_filters' => [ | ||||
| 		'javascripts' => [], | ||||
| 		'stylesheets' => [], | ||||
|     ], | ||||
|  | ||||
| ]; | ||||
| @@ -0,0 +1,5 @@ | ||||
| <?php | ||||
|  | ||||
| return array( | ||||
|     'view' => 'laravel-breadcrumbs::bootstrap3', | ||||
| ); | ||||
							
								
								
									
										134
									
								
								app/config/packages/rcrowe/twigbridge/extensions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								app/config/packages/rcrowe/twigbridge/extensions.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * This file is part of the TwigBridge package. | ||||
|  * | ||||
|  * @copyright Robert Crowe <hello@vivalacrowe.com> | ||||
|  * | ||||
|  * For the full copyright and license information, please view the LICENSE | ||||
|  * file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Configuration options for the built-in extensions. | ||||
|  */ | ||||
| return [ | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Extensions | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | Enabled extensions. | ||||
|     | | ||||
|     | `Twig_Extension_Debug` is enabled automatically if twig.debug is TRUE. | ||||
|     | | ||||
|     */ | ||||
|     'enabled' => [ | ||||
|         'TwigBridge\Extension\Loader\Facades', | ||||
|         'TwigBridge\Extension\Loader\Filters', | ||||
|         'TwigBridge\Extension\Loader\Functions', | ||||
|  | ||||
|         'TwigBridge\Extension\Laravel\Auth', | ||||
|         'TwigBridge\Extension\Laravel\Config', | ||||
|         'TwigBridge\Extension\Laravel\Form', | ||||
|         'TwigBridge\Extension\Laravel\Html', | ||||
|         'TwigBridge\Extension\Laravel\Input', | ||||
|         'TwigBridge\Extension\Laravel\Session', | ||||
|         'TwigBridge\Extension\Laravel\String', | ||||
|         'TwigBridge\Extension\Laravel\Translator', | ||||
|         'TwigBridge\Extension\Laravel\Url', | ||||
|  | ||||
|         // 'TwigBridge\Extension\Laravel\Legacy\Facades', | ||||
|     ], | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Facades | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | Available facades. Access like `{{ Config.get('foo.bar') }}`. | ||||
|     | | ||||
|     | Each facade can take an optional array of options. To mark the whole facade | ||||
|     | as safe you can set the option `'is_safe' => true`. Setting the facade as | ||||
|     | safe means that any HTML returned will not be escaped. | ||||
|     | | ||||
|     | It is advisable to not set the whole facade as safe and instead mark the | ||||
|     | each appropriate method as safe for security reasons. You can do that with | ||||
|     | the following syntax: | ||||
|     | | ||||
|     | <code> | ||||
|     |     'Form' => [ | ||||
|     |         'is_safe' => [ | ||||
|     |             'open' | ||||
|     |         ] | ||||
|     |     ] | ||||
|     | </code> | ||||
|     | | ||||
|     | The values of the `is_safe` array must match the called method on the facade | ||||
|     | in order to be marked as safe. | ||||
|     | | ||||
|     */ | ||||
|     'facades' => [], | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Functions | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | Available functions. Access like `{{ secure_url(...) }}`. | ||||
|     | | ||||
|     | Each function can take an optional array of options. These options are | ||||
|     | passed directly to `Twig_SimpleFunction`. | ||||
|     | | ||||
|     | So for example, to mark a function as safe you can do the following: | ||||
|     | | ||||
|     | <code> | ||||
|     |     'link_to' => [ | ||||
|     |         'is_safe' => ['html'] | ||||
|     |     ] | ||||
|     | </code> | ||||
|     | | ||||
|     | The options array also takes a `callback` that allows you to name the | ||||
|     | function differently in your Twig templates than what it's actually called. | ||||
|     | | ||||
|     | <code> | ||||
|     |     'link' => [ | ||||
|     |         'callback' => 'link_to' | ||||
|     |     ] | ||||
|     | </code> | ||||
|     | | ||||
|     */ | ||||
|     'functions' => [], | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Filters | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | Available filters. Access like `{{ variable|filter }}`. | ||||
|     | | ||||
|     | Each filter can take an optional array of options. These options are | ||||
|     | passed directly to `Twig_SimpleFilter`. | ||||
|     | | ||||
|     | So for example, to mark a filter as safe you can do the following: | ||||
|     | | ||||
|     | <code> | ||||
|     |     'studly_case' => [ | ||||
|     |         'is_safe' => ['html'] | ||||
|     |     ] | ||||
|     | </code> | ||||
|     | | ||||
|     | The options array also takes a `callback` that allows you to name the | ||||
|     | filter differently in your Twig templates than what is actually called. | ||||
|     | | ||||
|     | <code> | ||||
|     |     'snake' => [ | ||||
|     |         'callback' => 'snake_case' | ||||
|     |     ] | ||||
|     | </code> | ||||
|     | | ||||
|     */ | ||||
|     'filters' => [], | ||||
|  | ||||
| ]; | ||||
							
								
								
									
										88
									
								
								app/config/packages/rcrowe/twigbridge/twig.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								app/config/packages/rcrowe/twigbridge/twig.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * This file is part of the TwigBridge package. | ||||
|  * | ||||
|  * @copyright Robert Crowe <hello@vivalacrowe.com> | ||||
|  * | ||||
|  * For the full copyright and license information, please view the LICENSE | ||||
|  * file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| use Illuminate\Support\Facades\Config; | ||||
|  | ||||
| /** | ||||
|  * Configuration options for Twig. | ||||
|  */ | ||||
| return [ | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Extension | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | File extension for Twig view files. | ||||
|     | | ||||
|     */ | ||||
|     'extension' => 'twig', | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Accepts all Twig environment configuration options | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | http://twig.sensiolabs.org/doc/api.html#environment-options | ||||
|     | | ||||
|     */ | ||||
|     'environment' => [ | ||||
|  | ||||
|         // When set to true, the generated templates have a __toString() method | ||||
|         // that you can use to display the generated nodes. | ||||
|         // default: false | ||||
|         'debug' => Config::get('app.debug', false), | ||||
|  | ||||
|         // The charset used by the templates. | ||||
|         // default: utf-8 | ||||
|         'charset' => 'utf-8', | ||||
|  | ||||
|         // The base template class to use for generated templates. | ||||
|         // default: TwigBridge\Twig\Template | ||||
|         'base_template_class' => 'TwigBridge\Twig\Template', | ||||
|  | ||||
|         // An absolute path where to store the compiled templates, or false to disable caching. If null | ||||
|         // then the cache file path is used. | ||||
|         // default: cache file storage path | ||||
|         'cache' => null, | ||||
|  | ||||
|         // When developing with Twig, it's useful to recompile the template | ||||
|         // whenever the source code changes. If you don't provide a value | ||||
|         // for the auto_reload option, it will be determined automatically based on the debug value. | ||||
|         'auto_reload' => true, | ||||
|  | ||||
|         // If set to false, Twig will silently ignore invalid variables | ||||
|         // (variables and or attributes/methods that do not exist) and | ||||
|         // replace them with a null value. When set to true, Twig throws an exception instead. | ||||
|         // default: false | ||||
|         'strict_variables' => false, | ||||
|  | ||||
|         // If set to true, auto-escaping will be enabled by default for all templates. | ||||
|         // default: true | ||||
|         'autoescape' => true, | ||||
|  | ||||
|         // A flag that indicates which optimizations to apply | ||||
|         // (default to -1 -- all optimizations are enabled; set it to 0 to disable) | ||||
|         'optimizations' => -1, | ||||
|     ], | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Global variables | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | These will always be passed in and can be accessed as Twig variables. | ||||
|     | NOTE: these will be overwritten if you pass data into the view with the same key. | ||||
|     | | ||||
|     */ | ||||
|     'globals' => [], | ||||
|  | ||||
| ]; | ||||
| @@ -5,6 +5,8 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI; | ||||
|  | ||||
| /** | ||||
|  * Class AccountController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class AccountController extends \BaseController | ||||
| { | ||||
| @@ -18,131 +20,222 @@ class AccountController extends \BaseController | ||||
|      */ | ||||
|     public function __construct(ARI $repository, AI $accounts) | ||||
|     { | ||||
|         $this->_accounts = $accounts; | ||||
|         $this->_accounts   = $accounts; | ||||
|         $this->_repository = $repository; | ||||
|         View::share('mainTitleIcon', 'fa-credit-card'); | ||||
|         View::share('title', 'Accounts'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function create() | ||||
|     public function create($what) | ||||
|     { | ||||
|         return View::make('accounts.create'); | ||||
|         switch ($what) { | ||||
|             case 'asset': | ||||
|                 View::share('subTitleIcon', 'fa-money'); | ||||
|                 break; | ||||
|             case 'expense': | ||||
|                 View::share('subTitleIcon', 'fa-shopping-cart'); | ||||
|                 break; | ||||
|             case 'revenue': | ||||
|                 View::share('subTitleIcon', 'fa-download'); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with( | ||||
|             'what', $what | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function asset() | ||||
|     { | ||||
|         View::share('subTitleIcon', 'fa-money'); | ||||
|  | ||||
|         $accounts = $this->_repository->getOfTypes(['Asset account', 'Default account']); | ||||
|  | ||||
|         return View::make('accounts.asset')->with('subTitle', 'Asset accounts')->with( | ||||
|             'accounts', $accounts | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function expense() | ||||
|     { | ||||
|         View::share('subTitleIcon', 'fa-shopping-cart'); | ||||
|  | ||||
|         $accounts = $this->_repository->getOfTypes(['Expense account', 'Beneficiary account']); | ||||
|  | ||||
|         return View::make('accounts.expense')->with('subTitle', 'Expense accounts')->with( | ||||
|             'accounts', $accounts | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function revenue() | ||||
|     { | ||||
|         View::share('subTitleIcon', 'fa-download'); | ||||
|  | ||||
|         $accounts = $this->_repository->getOfTypes(['Revenue account']); | ||||
|  | ||||
|         return View::make('accounts.revenue')->with('subTitle', 'Revenue accounts')->with( | ||||
|             'accounts', $accounts | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function delete(Account $account) | ||||
|     { | ||||
|         $accountType = $account->accountType()->first(); | ||||
|  | ||||
|         if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') { | ||||
|             return \View::make('error')->with( | ||||
|                 'message', 'Cannot edit this account type (' . $accountType->description . ').' | ||||
|         return View::make('accounts.delete')->with('account', $account) | ||||
|             ->with( | ||||
|                 'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"' | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return View::make('accounts.delete')->with('account', $account); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function destroy(Account $account) | ||||
|     { | ||||
|         $accountType = $account->accountType()->first(); | ||||
|  | ||||
|         if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') { | ||||
|             return View::make('error')->with( | ||||
|                 'message', 'Cannot edit this account type (' . $accountType->description . ').' | ||||
|             ); | ||||
|         } | ||||
|         $result = $this->_repository->destroy($account); | ||||
|         if ($result === true) { | ||||
|             Session::flash('success', 'The account was deleted.'); | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not delete the account.'); | ||||
|         $type = $account->accountType->type; | ||||
|         $this->_repository->destroy($account); | ||||
|         Session::flash('success', 'The account was deleted.'); | ||||
|         switch ($type) { | ||||
|             case 'Asset account': | ||||
|             case 'Default account': | ||||
|                 return Redirect::route('accounts.asset'); | ||||
|                 break; | ||||
|             case 'Expense account': | ||||
|             case 'Beneficiary account': | ||||
|                 return Redirect::route('accounts.expense'); | ||||
|                 break; | ||||
|             case 'Revenue account': | ||||
|                 return Redirect::route('accounts.revenue'); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return Redirect::route('accounts.index'); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function edit(Account $account) | ||||
|     { | ||||
|         $accountType = $account->accountType()->first(); | ||||
|  | ||||
|         if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') { | ||||
|             return View::make('error')->with( | ||||
|                 'message', 'Cannot edit this account type (' . $accountType->description . ').' | ||||
|             ); | ||||
|         switch ($account->accountType->type) { | ||||
|             case 'Asset account': | ||||
|             case 'Default account': | ||||
|                 View::share('subTitleIcon', 'fa-money'); | ||||
|                 break; | ||||
|             case 'Expense account': | ||||
|             case 'Beneficiary account': | ||||
|                 View::share('subTitleIcon', 'fa-shopping-cart'); | ||||
|                 break; | ||||
|             case 'Revenue account': | ||||
|                 View::share('subTitleIcon', 'fa-download'); | ||||
|                 break; | ||||
|         } | ||||
|         $openingBalance = $this->_accounts->openingBalanceTransaction($account); | ||||
|  | ||||
|         return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance); | ||||
|         $openingBalance = $this->_accounts->openingBalanceTransaction($account); | ||||
|         return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance) | ||||
|  | ||||
|             ->with('subTitle', 'Edit ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         $accounts = $this->_repository->get(); | ||||
|         $display = $this->_accounts->index($accounts); | ||||
|  | ||||
|         return View::make('accounts.index')->with('accounts', $display); | ||||
|         return View::make('error')->with('message', 'This view has been disabled'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function show(Account $account) | ||||
|     { | ||||
|         $accountType = $account->accountType()->first(); | ||||
|         if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') { | ||||
|             return View::make('error')->with( | ||||
|                 'message', 'Cannot show this account type (' . $accountType->description . ').' | ||||
|             ); | ||||
|         switch ($account->accountType->type) { | ||||
|             case 'Asset account': | ||||
|             case 'Default account': | ||||
|                 View::share('subTitleIcon', 'fa-money'); | ||||
|                 break; | ||||
|             case 'Expense account': | ||||
|             case 'Beneficiary account': | ||||
|                 View::share('subTitleIcon', 'fa-shopping-cart'); | ||||
|                 break; | ||||
|             case 'Revenue account': | ||||
|                 View::share('subTitleIcon', 'fa-download'); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         $show = $this->_accounts->show($account, 40); | ||||
|  | ||||
|         return View::make('accounts.show')->with('account', $account)->with('show', $show); | ||||
|         $data = $this->_accounts->show($account, 40); | ||||
|  | ||||
|         return View::make('accounts.show')->with('account', $account)->with('show', $data)->with( | ||||
|             'subTitle', | ||||
|             'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function store() | ||||
|     { | ||||
|  | ||||
|         $account = $this->_repository->store(Input::all()); | ||||
|         $data         = Input::all(); | ||||
|         $data['what'] = isset($data['what']) && $data['what'] != '' ? $data['what'] : 'asset'; | ||||
|  | ||||
|  | ||||
|         switch ($data['what']) { | ||||
|             default: | ||||
|             case 'asset': | ||||
|                 $data['account_type'] = 'Asset account'; | ||||
|                 break; | ||||
|             case 'expense': | ||||
|                 $data['account_type'] = 'Expense account'; | ||||
|                 break; | ||||
|             case 'revenue': | ||||
|                 $data['account_type'] = 'Revenue account'; | ||||
|                 break; | ||||
|  | ||||
|         } | ||||
|         $account = $this->_repository->store($data); | ||||
|  | ||||
|         if ($account->validate()) { | ||||
|             // saved! return to wherever. | ||||
|             Session::flash('success', 'Account "' . $account->name . '" created!'); | ||||
|             if (Input::get('create') == '1') { | ||||
|                 return Redirect::route('accounts.create')->withInput(); | ||||
|             if (intval(Input::get('create')) === 1) { | ||||
|                 return Redirect::route('accounts.create', $data['what'])->withInput(); | ||||
|             } else { | ||||
|                 return Redirect::route('accounts.index'); | ||||
|  | ||||
|                 return Redirect::route('accounts.' . e($data['what'])); | ||||
|             } | ||||
|         } else { | ||||
|             // did not save, return with error: | ||||
|             Session::flash('error', 'Could not save the new account. Please check the form.'); | ||||
|             Session::flash('error', 'Could not save the new account: ' . $account->errors()->first()); | ||||
|  | ||||
|             return Redirect::route('accounts.create')->withErrors($account->errors())->withInput(); | ||||
|             return Redirect::route('accounts.create', $data['what'])->withErrors($account->errors())->withInput(); | ||||
|  | ||||
|         } | ||||
|     } | ||||
| @@ -150,21 +243,28 @@ class AccountController extends \BaseController | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function update(Account $account) | ||||
|     { | ||||
|         $accountType = $account->accountType()->first(); | ||||
|         if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') { | ||||
|             return View::make('error')->with( | ||||
|                 'message', 'Cannot show this account type (' . $accountType->description . ').' | ||||
|             ); | ||||
|         } | ||||
|         /** @var \Account $account */ | ||||
|         $account = $this->_repository->update($account, Input::all()); | ||||
|         if ($account->validate()) { | ||||
|             Session::flash('success', 'Account "' . $account->name . '" updated.'); | ||||
|             switch ($account->accountType->type) { | ||||
|                 case 'Asset account': | ||||
|                 case 'Default account': | ||||
|                     return Redirect::route('accounts.asset'); | ||||
|                     break; | ||||
|                 case 'Expense account': | ||||
|                 case 'Beneficiary account': | ||||
|                     return Redirect::route('accounts.expense'); | ||||
|                     break; | ||||
|                 case 'Revenue account': | ||||
|                     return Redirect::route('accounts.revenue'); | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
|             return Redirect::route('accounts.index'); | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not update account: ' . $account->errors()->first()); | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| use Illuminate\Routing\Controller; | ||||
|  | ||||
| /** | ||||
|  * Class BaseController | ||||
|  */ | ||||
| @@ -11,8 +13,6 @@ class BaseController extends Controller | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         \Event::fire('limits.check'); | ||||
|         \Event::fire('piggybanks.check'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -1,11 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Firefly\Helper\Controllers\BudgetInterface as BI; | ||||
| use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI; | ||||
|  | ||||
| /** | ||||
|  * Class BudgetController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||
|  * | ||||
|  */ | ||||
| class BudgetController extends BaseController | ||||
| { | ||||
| @@ -19,8 +24,42 @@ class BudgetController extends BaseController | ||||
|      */ | ||||
|     public function __construct(BI $budgets, BRI $repository) | ||||
|     { | ||||
|         $this->_budgets = $budgets; | ||||
|         $this->_budgets    = $budgets; | ||||
|         $this->_repository = $repository; | ||||
|         View::share('title', 'Budgets'); | ||||
|         View::share('mainTitleIcon', 'fa-tasks'); | ||||
|     } | ||||
|  | ||||
|     public function nobudget($view = 'session') { | ||||
|         switch($view) { | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot show transactions without a budget for view "'.$view.'".'); | ||||
|                 break; | ||||
|             case 'session': | ||||
|                 $start = Session::get('start'); | ||||
|                 $end   = Session::get('end'); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         // Add expenses that have no budget: | ||||
|         $set = \Auth::user()->transactionjournals()->whereNotIn( | ||||
|             'transaction_journals.id', function ($query) use ($start, $end) { | ||||
|                 $query->select('transaction_journals.id')->from('transaction_journals') | ||||
|                       ->leftJoin( | ||||
|                           'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', | ||||
|                           'transaction_journals.id' | ||||
|                       ) | ||||
|                       ->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id') | ||||
|                       ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) | ||||
|                       ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) | ||||
|                       ->where('components.class', 'Budget'); | ||||
|             } | ||||
|         )->before($end)->after($start)->get(); | ||||
|  | ||||
|         return View::make('budgets.nobudget') | ||||
|                    ->with('view', $view) | ||||
|                    ->with('transactions',$set) | ||||
|                    ->with('subTitle', 'Transactions without a budget'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -30,7 +69,7 @@ class BudgetController extends BaseController | ||||
|     { | ||||
|         $periods = \Config::get('firefly.periods_to_text'); | ||||
|  | ||||
|         return View::make('budgets.create')->with('periods', $periods); | ||||
|         return View::make('budgets.create')->with('periods', $periods)->with('subTitle', 'Create a new budget'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -40,7 +79,8 @@ class BudgetController extends BaseController | ||||
|      */ | ||||
|     public function delete(Budget $budget) | ||||
|     { | ||||
|         return View::make('budgets.delete')->with('budget', $budget); | ||||
|         return View::make('budgets.delete')->with('budget', $budget) | ||||
|             ->with('subTitle', 'Delete budget "' . $budget->name . '"'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -50,20 +90,16 @@ class BudgetController extends BaseController | ||||
|      */ | ||||
|     public function destroy(Budget $budget) | ||||
|     { | ||||
|         // remove budget | ||||
|         Event::fire('budgets.destroy', [$budget]); // just before deletion. | ||||
|         $result = $this->_repository->destroy($budget); | ||||
|         if ($result === true) { | ||||
|             Session::flash('success', 'The budget was deleted.'); | ||||
|             if (Input::get('from') == 'date') { | ||||
|                 return Redirect::route('budgets.index'); | ||||
|             } else { | ||||
|                 return Redirect::route('budgets.index.budget'); | ||||
|             } | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not delete the budget. Check the logs to be sure.'); | ||||
|         } | ||||
|         $this->_repository->destroy($budget); | ||||
|         Session::flash('success', 'The budget was deleted.'); | ||||
|  | ||||
|         return Redirect::route('budgets.index'); | ||||
|         // redirect: | ||||
|         if (Input::get('from') == 'date') { | ||||
|             return Redirect::route('budgets.index'); | ||||
|         } | ||||
|         return Redirect::route('budgets.index.budget'); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -74,7 +110,8 @@ class BudgetController extends BaseController | ||||
|      */ | ||||
|     public function edit(Budget $budget) | ||||
|     { | ||||
|         return View::make('budgets.edit')->with('budget', $budget); | ||||
|         return View::make('budgets.edit')->with('budget', $budget) | ||||
|             ->with('subTitle', 'Edit budget "' . $budget->name . '"'); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -83,63 +120,78 @@ class BudgetController extends BaseController | ||||
|      */ | ||||
|     public function indexByBudget() | ||||
|     { | ||||
|         View::share('subTitleIcon', 'fa-folder-open'); | ||||
|  | ||||
|         $budgets = $this->_repository->get(); | ||||
|         $today = new Carbon; | ||||
|  | ||||
|  | ||||
|         return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', $today); | ||||
|         return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', new Carbon) | ||||
|             ->with('subTitle', 'Grouped by budget'); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this|\Illuminate\View\View | ||||
|      * @throws Firefly\Exception\FireflyException | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function indexByDate() | ||||
|     { | ||||
|         View::share('subTitleIcon', 'fa-calendar'); | ||||
|  | ||||
|         // get a list of dates by getting all repetitions: | ||||
|         $set = $this->_repository->get(); | ||||
|         $set     = $this->_repository->get(); | ||||
|         $budgets = $this->_budgets->organizeByDate($set); | ||||
|  | ||||
|  | ||||
|         return View::make('budgets.indexByDate')->with('budgets', $budgets); | ||||
|         return View::make('budgets.indexByDate')->with('budgets', $budgets) | ||||
|             ->with('subTitle', 'Grouped by date'); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Budget $budget | ||||
|      * Three use cases for this view: | ||||
|      * | ||||
|      * - Show everything. | ||||
|      * - Show a specific repetition. | ||||
|      * - Show everything shows NO repetition. | ||||
|      * | ||||
|      * @param Budget          $budget | ||||
|      * @param LimitRepetition $repetition | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function show(Budget $budget) | ||||
|     public function show(Budget $budget, \LimitRepetition $repetition = null) | ||||
|     { | ||||
|         /** | ||||
|          * Use the | ||||
|          */ | ||||
|         $useSessionDates = Input::get('useSession') == 'true' ? true : false; | ||||
|  | ||||
|  | ||||
|         $filters = []; | ||||
|  | ||||
|         if (!is_null(Input::get('rep'))) { | ||||
|             $repetitionId = intval(Input::get('rep')); | ||||
|             $repetitions = $this->_budgets->organizeRepetition($repetitionId); | ||||
|             $filters[] = $repetitions[0]['limit']; | ||||
|             $filters[] = $repetitions[0]['limitrepetition']; | ||||
|         } else { | ||||
|             if (Input::get('noenvelope') == 'true') { | ||||
|                 $repetitions = $this->_budgets->outsideRepetitions($budget); | ||||
|                 $filters[] = 'no_envelope'; | ||||
|             } else { | ||||
|                 // grab all limit repetitions, order them, show them: | ||||
|                 $repetitions = $this->_budgets->organizeRepetitions($budget, $useSessionDates); | ||||
|             } | ||||
|         $view            = null; | ||||
|         $title           = null; | ||||
|         \Log::debug('Is envelope true? ' . (Input::get('noenvelope') == 'true')); | ||||
|         switch (true) { | ||||
|             case (!is_null($repetition)): | ||||
|                 $data  = $this->_budgets->organizeRepetition($repetition); | ||||
|                 $view  = 1; | ||||
|                 $title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf( | ||||
|                         $repetition->limit->amount, | ||||
|                         false | ||||
|                     ); | ||||
|                 break; | ||||
|             case (Input::get('noenvelope') == 'true'): | ||||
|                 $data  = $this->_budgets->outsideRepetitions($budget); | ||||
|                 $view  = 2; | ||||
|                 $title = $budget->name . ', transactions outside an envelope'; | ||||
|                 break; | ||||
|             default: | ||||
|                 $data  = $this->_budgets->organizeRepetitions($budget, $useSessionDates); | ||||
|                 $view  = $useSessionDates ? 3 : 4; | ||||
|                 $title = $useSessionDates ? $budget->name . ' in session period' : $budget->name; | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return View::make('budgets.show')->with('budget', $budget)->with('repetitions', $repetitions)->with( | ||||
|             'filters', $filters | ||||
|         )->with('highlight', Input::get('highlight'))->with('useSessionDates', $useSessionDates); | ||||
|         return View::make('budgets.show') | ||||
|             ->with('budget', $budget) | ||||
|             ->with('repetitions', $data) | ||||
|             ->with('view', $view) | ||||
|             ->with('highlight', Input::get('highlight')) | ||||
|             ->with('useSessionDates', $useSessionDates) | ||||
|             ->with('subTitle', 'Overview for ' . $title); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -5,6 +5,8 @@ use Firefly\Storage\Category\CategoryRepositoryInterface as CRI; | ||||
|  | ||||
| /** | ||||
|  * Class CategoryController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class CategoryController extends BaseController | ||||
| { | ||||
| @@ -18,7 +20,9 @@ class CategoryController extends BaseController | ||||
|     public function __construct(CRI $repository, CI $category) | ||||
|     { | ||||
|         $this->_repository = $repository; | ||||
|         $this->_category = $category; | ||||
|         $this->_category   = $category; | ||||
|         View::share('title','Categories'); | ||||
|         View::share('mainTitleIcon', 'fa-bar-chart'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -26,7 +30,7 @@ class CategoryController extends BaseController | ||||
|      */ | ||||
|     public function create() | ||||
|     { | ||||
|         return View::make('categories.create'); | ||||
|         return View::make('categories.create')->with('subTitle', 'Create a new category'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -36,7 +40,8 @@ class CategoryController extends BaseController | ||||
|      */ | ||||
|     public function delete(Category $category) | ||||
|     { | ||||
|         return View::make('categories.delete')->with('category', $category); | ||||
|         return View::make('categories.delete')->with('category', $category) | ||||
|             ->with('subTitle', 'Delete category "' . $category->name . '"'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -46,13 +51,8 @@ class CategoryController extends BaseController | ||||
|      */ | ||||
|     public function destroy(Category $category) | ||||
|     { | ||||
|         $result = $this->_repository->destroy($category); | ||||
|         if ($result === true) { | ||||
|             Session::flash('success', 'The category was deleted.'); | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not delete the category. Check the logs to be sure.'); | ||||
|         } | ||||
|  | ||||
|         $this->_repository->destroy($category); | ||||
|         Session::flash('success', 'The category was deleted.'); | ||||
|         return Redirect::route('categories.index'); | ||||
|     } | ||||
|  | ||||
| @@ -63,7 +63,8 @@ class CategoryController extends BaseController | ||||
|      */ | ||||
|     public function edit(Category $category) | ||||
|     { | ||||
|         return View::make('categories.edit')->with('category', $category); | ||||
|         return View::make('categories.edit')->with('category', $category) | ||||
|             ->with('subTitle', 'Edit category "' . $category->name . '"'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -73,7 +74,8 @@ class CategoryController extends BaseController | ||||
|     { | ||||
|         $categories = $this->_repository->get(); | ||||
|  | ||||
|         return View::make('categories.index')->with('categories', $categories); | ||||
|         return View::make('categories.index')->with('categories', $categories) | ||||
|             ->with('subTitle', 'All your categories'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -84,14 +86,14 @@ class CategoryController extends BaseController | ||||
|     public function show(Category $category) | ||||
|     { | ||||
|         $start = \Session::get('start'); | ||||
|         $end = \Session::get('end'); | ||||
|         $end   = \Session::get('end'); | ||||
|  | ||||
|  | ||||
|         $journals = $this->_category->journalsInRange($category, $start, $end); | ||||
|  | ||||
|         return View::make('categories.show')->with('category', $category)->with('journals', $journals)->with( | ||||
|             'highlight', Input::get('highlight') | ||||
|         ); | ||||
|         )->with('subTitle', 'Overview for category "' . $category->name . '"'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -1,11 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Firefly\Helper\Controllers\ChartInterface; | ||||
| use Firefly\Storage\Account\AccountRepositoryInterface; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class ChartController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||
|  */ | ||||
| class ChartController extends BaseController | ||||
| { | ||||
| @@ -15,66 +20,66 @@ class ChartController extends BaseController | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param ChartInterface             $chart | ||||
|      * @param ChartInterface $chart | ||||
|      * @param AccountRepositoryInterface $accounts | ||||
|      */ | ||||
|     public function __construct(ChartInterface $chart, AccountRepositoryInterface $accounts) | ||||
|     { | ||||
|         $this->_chart = $chart; | ||||
|         $this->_chart    = $chart; | ||||
|         $this->_accounts = $accounts; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method takes a budget, all limits and all their repetitions and displays three numbers per repetition: | ||||
|      * the amount of money in the repetition (represented as "an envelope"), the amount spent and the spent percentage. | ||||
|      * | ||||
|      * @param Budget $budget | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function budgetDefault(\Budget $budget) | ||||
|     { | ||||
|         $expense = []; | ||||
|         $left = []; | ||||
|         $expense  = []; | ||||
|         $left     = []; | ||||
|         $envelope = []; | ||||
|         // get all limit repetitions for this budget. | ||||
|         /** @var \Limit $limit */ | ||||
|         foreach ($budget->limits as $limit) { | ||||
|             /** @var \LimitRepetition $rep */ | ||||
|             foreach ($limit->limitrepetitions as $rep) { | ||||
|                 $spentInRep = \Transaction:: | ||||
|                     leftJoin( | ||||
|                         'transaction_journals', 'transaction_journals.id', '=', | ||||
|                         'transactions.transaction_journal_id' | ||||
|                     ) | ||||
|                     ->leftJoin( | ||||
|                         'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', | ||||
|                         '=', | ||||
|                         'transaction_journals.id' | ||||
|                     )->where('component_transaction_journal.component_id', '=', $budget->id)->where( | ||||
|                         'transaction_journals.date', '>=', $rep->startdate->format('Y-m-d') | ||||
|                     )->where('transaction_journals.date', '<=', $rep->enddate->format('Y-m-d'))->where( | ||||
|                         'amount', '>', 0 | ||||
|                     )->sum('amount'); | ||||
|  | ||||
|  | ||||
|                 $pct = round(($spentInRep / $limit->amount) * 100, 2); | ||||
|                 $name = $rep->periodShow(); | ||||
|                 $expense[] = [$name, floatval($spentInRep)]; | ||||
|                 $left[] = [$name, $pct]; | ||||
|                 // get the amount of money spent in this period on this budget. | ||||
|                 $spentInRep = $rep->amount - $rep->leftInRepetition(); | ||||
|                 $pct        = round((floatval($spentInRep) / floatval($limit->amount)) * 100, 2); | ||||
|                 $name       = $rep->periodShow(); | ||||
|                 $envelope[] = [$name, floatval($limit->amount)]; | ||||
|                 $expense[]  = [$name, floatval($spentInRep)]; | ||||
|                 $left[]     = [$name, $pct]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $return = [ | ||||
|             'chart_title' => 'Overview for budget ' . $budget->name, | ||||
|             'subtitle'    => 'All envelopes', | ||||
|             'series'      => [ | ||||
|             'subtitle' => 'All envelopes', | ||||
|             'series' => [ | ||||
|                 [ | ||||
|                     'type' => 'line', | ||||
|                     'yAxis' => 1, | ||||
|                     'name' => 'Amount in envelope', | ||||
|                     'data' => $envelope | ||||
|                 ], | ||||
|                 [ | ||||
|                     'type' => 'column', | ||||
|                     'name' => 'Expenses in envelope', | ||||
|                     'data' => $expense | ||||
|                 ], | ||||
|                 [ | ||||
|                     'type'  => 'line', | ||||
|                     'type' => 'line', | ||||
|                     'yAxis' => 1, | ||||
|                     'name'  => 'Spent percentage for envelope', | ||||
|                     'data'  => $left | ||||
|                     'name' => 'Spent percentage for envelope', | ||||
|                     'data' => $left | ||||
|                 ] | ||||
|  | ||||
|  | ||||
|             ] | ||||
|         ]; | ||||
|  | ||||
| @@ -82,42 +87,40 @@ class ChartController extends BaseController | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method takes a single limit repetition (so a single "envelope") and displays the amount of money spent | ||||
|      * per day and subsequently how much money is left. | ||||
|      * | ||||
|      * @param LimitRepetition $rep | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function budgetLimit(\LimitRepetition $rep) | ||||
|     { | ||||
|         $budget = $rep->limit->budget; | ||||
|         $current = clone $rep->startdate; | ||||
|         $expense = []; | ||||
|         $leftInLimit = []; | ||||
|         $budget             = $rep->limit->budget; | ||||
|         $current            = clone $rep->startdate; | ||||
|         $expense            = []; | ||||
|         $leftInLimit        = []; | ||||
|         $currentLeftInLimit = floatval($rep->limit->amount); | ||||
|         while ($current <= $rep->enddate) { | ||||
|             $spent = \Transaction:: | ||||
|                 leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                 ->leftJoin( | ||||
|                     'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', | ||||
|                     'transaction_journals.id' | ||||
|                 )->where('component_transaction_journal.component_id', '=', $budget->id)->where( | ||||
|                     'transaction_journals.date', $current->format('Y-m-d') | ||||
|                 )->where('amount', '>', 0)->sum('amount'); | ||||
|             $spent = floatval($spent) == 0 ? null : floatval($spent); | ||||
|             $entry = [$current->timestamp * 1000, $spent]; | ||||
|             $expense[] = $entry; | ||||
|             $spent              = $this->_chart->spentOnDay($budget, $current); | ||||
|             $spent              = floatval($spent) == 0 ? null : floatval($spent); | ||||
|             $entry              = [$current->timestamp * 1000, $spent]; | ||||
|             $expense[]          = $entry; | ||||
|             $currentLeftInLimit = $currentLeftInLimit - $spent; | ||||
|             $leftInLimit[] = [$current->timestamp * 1000, $currentLeftInLimit]; | ||||
|             $leftInLimit[]      = [$current->timestamp * 1000, $currentLeftInLimit]; | ||||
|             $current->addDay(); | ||||
|         } | ||||
|  | ||||
|         $return = [ | ||||
|             'chart_title' => 'Overview for budget ' . $budget->name, | ||||
|             'subtitle'    => | ||||
|             'subtitle' => | ||||
|                 'Between ' . $rep->startdate->format('M jS, Y') . ' and ' . $rep->enddate->format('M jS, Y'), | ||||
|             'series'      => [ | ||||
|             'series' => [ | ||||
|                 [ | ||||
|                     'type'  => 'column', | ||||
|                     'name'  => 'Expenses per day', | ||||
|                     'type' => 'column', | ||||
|                     'name' => 'Expenses per day', | ||||
|                     'yAxis' => 1, | ||||
|                     'data'  => $expense | ||||
|                     'data' => $expense | ||||
|                 ], | ||||
|                 [ | ||||
|                     'type' => 'line', | ||||
| @@ -132,180 +135,149 @@ class ChartController extends BaseController | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method takes a budget and gets all transactions in it which haven't got an envelope (limit). | ||||
|      * | ||||
|      * Usually this means that very old and unorganized or very NEW transactions get displayed; there was never an | ||||
|      * envelope or it hasn't been created (yet). | ||||
|      * | ||||
|      * | ||||
|      * @param Budget $budget | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function budgetNoLimits(\Budget $budget) | ||||
|     { | ||||
|         $inRepetitions = []; | ||||
|         foreach ($budget->limits as $limit) { | ||||
|             foreach ($limit->limitrepetitions as $repetition) { | ||||
|                 $set = $budget->transactionjournals()->leftJoin( | ||||
|                     'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|                 )->where('transaction_types.type', 'Withdrawal')->where( | ||||
|                         'date', '>=', $repetition->startdate->format('Y-m-d') | ||||
|                     )->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->get( | ||||
|                         ['transaction_journals.id'] | ||||
|                     ); | ||||
|                 foreach ($set as $item) { | ||||
|                     $inRepetitions[] = $item->id; | ||||
|                 } | ||||
|             } | ||||
|         /* | ||||
|          * Firefly can go about this two ways. Either it finds all transactions which definitely are IN an envelope | ||||
|          * and exclude them or it searches for transactions outside of the range of any of the envelopes there are. | ||||
|          * | ||||
|          * Since either is kinda shitty Firefly uses the first one because it's easier to build. | ||||
|          */ | ||||
|         $inRepetitions = $this->_chart->allJournalsInBudgetEnvelope($budget); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $query = $budget->transactionjournals()->whereNotIn( | ||||
|             'transaction_journals.id', $inRepetitions | ||||
|         )->orderBy('date', 'DESC')->orderBy( | ||||
|                 'transaction_journals.id', 'DESC' | ||||
|             ); | ||||
|         /* | ||||
|          * With this set of id's, Firefly can search for all journals NOT in that set. | ||||
|          * BUT they have to be in the budget (duh). | ||||
|          */ | ||||
|         $set = $this->_chart->journalsNotInSet($budget, $inRepetitions); | ||||
|         /* | ||||
|          * Next step: get all transactions for those journals. | ||||
|          */ | ||||
|         $transactions = $this->_chart->transactionsByJournals($set); | ||||
|  | ||||
|  | ||||
|         $result = $query->get(['transaction_journals.id']); | ||||
|         $set = []; | ||||
|         foreach ($result as $entry) { | ||||
|             $set[] = $entry->id; | ||||
|         } | ||||
|         // all transactions for these journals, grouped by date and SUM | ||||
|         $transactions = \Transaction::whereIn('transaction_journal_id', $set)->leftJoin( | ||||
|             'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' | ||||
|         ) | ||||
|             ->groupBy('transaction_journals.date')->where('amount', '>', 0)->get( | ||||
|                 ['transaction_journals.date', DB::Raw('SUM(`amount`) as `aggregate`')] | ||||
|             ); | ||||
|  | ||||
|  | ||||
|         // this set builds the chart: | ||||
|         /* | ||||
|          *  this set builds the chart: | ||||
|          */ | ||||
|         $expense = []; | ||||
|  | ||||
|         foreach ($transactions as $t) { | ||||
|             $date = new Carbon($t->date); | ||||
|             $date      = new Carbon($t->date); | ||||
|             $expense[] = [$date->timestamp * 1000, floatval($t->aggregate)]; | ||||
|         } | ||||
|         $return = [ | ||||
|             'chart_title' => 'Overview for ' . $budget->name, | ||||
|             'subtitle'    => 'Not organized by an envelope', | ||||
|             'series'      => [ | ||||
|             'subtitle' => 'Not organized by an envelope', | ||||
|             'series' => [ | ||||
|                 [ | ||||
|                     'type' => 'spline', | ||||
|                     'type' => 'column', | ||||
|                     'name' => 'Expenses per day', | ||||
|                     'data' => $expense | ||||
|                 ] | ||||
|  | ||||
|             ] | ||||
|         ]; | ||||
|  | ||||
|         return Response::json($return); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method gets all transactions within a budget within the period set by the current session | ||||
|      * start and end date. It also includes any envelopes which might exist within this period. | ||||
|      * | ||||
|      * @param Budget $budget | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function budgetSession(\Budget $budget) | ||||
|     { | ||||
|         $expense = []; | ||||
|         $repetitionSeries = []; | ||||
|         $current = clone Session::get('start'); | ||||
|         $end = clone Session::get('end'); | ||||
|         while ($current <= $end) { | ||||
|             $spent = \Transaction:: | ||||
|                 leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                 ->leftJoin( | ||||
|                     'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', | ||||
|                     'transaction_journals.id' | ||||
|                 )->where('component_transaction_journal.component_id', '=', $budget->id)->where( | ||||
|                     'transaction_journals.date', $current->format('Y-m-d') | ||||
|                 )->where('amount', '>', 0)->sum('amount'); | ||||
|             $spent = floatval($spent) == 0 ? null : floatval($spent); | ||||
|             if (!is_null($spent)) { | ||||
|                 $expense[] = [$current->timestamp * 1000, $spent]; | ||||
|             } | ||||
|         $series = []; | ||||
|         $end    = clone Session::get('end'); | ||||
|         $start  = clone Session::get('start'); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Expenses per day in the session's period. That's easy. | ||||
|          */ | ||||
|         $expense = []; | ||||
|         $current = clone Session::get('start'); | ||||
|         while ($current <= $end) { | ||||
|             $spent     = $this->_chart->spentOnDay($budget, $current); | ||||
|             $spent     = floatval($spent) == 0 ? null : floatval($spent); | ||||
|             $expense[] = [$current->timestamp * 1000, $spent]; | ||||
|             $current->addDay(); | ||||
|         } | ||||
|  | ||||
|         // find all limit repetitions (for this budget) between start and end. | ||||
|         $start = clone Session::get('start'); | ||||
|         $repetitionSeries[] = [ | ||||
|         $series[] = [ | ||||
|             'type' => 'column', | ||||
|             'name' => 'Expenses per day', | ||||
|             'data' => $expense | ||||
|         ]; | ||||
|         unset($expense, $spent, $current); | ||||
|  | ||||
|         /* | ||||
|          * Find all limit repetitions (for this budget) between start and end. This is | ||||
|          * quite a complex query. | ||||
|          */ | ||||
|         $reps = $this->_chart->limitsInRange($budget, $start, $end); | ||||
|  | ||||
|         /** @var \Limit $limit */ | ||||
|         foreach ($budget->limits as $limit) { | ||||
|             $reps = $limit->limitrepetitions()->where( | ||||
|                 function ($q) use ($start, $end) { | ||||
|                     // startdate is between range | ||||
|                     $q->where( | ||||
|                         function ($q) use ($start, $end) { | ||||
|                             $q->where('startdate', '>=', $start->format('Y-m-d')); | ||||
|                             $q->where('startdate', '<=', $end->format('Y-m-d')); | ||||
|                         } | ||||
|         /* | ||||
|          * For each limitrepetition Firefly creates a serie that contains the amount left in | ||||
|          * the limitrepetition for its entire date-range. Entries are only actually included when they | ||||
|          * fall into the charts date range. | ||||
|          * | ||||
|          * So example: the user has a session date from Jan 15 to Jan 30. The limitrepetition | ||||
|          * starts at 1 Jan until 1 Feb. | ||||
|          * | ||||
|          * Firefly loops from 1 Jan to 1 Feb but only includes Jan 15 / Jan 30. | ||||
|          * But it does keep count of the amount outside of these dates because otherwise the line might be wrong. | ||||
|          */ | ||||
|         /** @var \LimitRepetition $repetition */ | ||||
|         foreach ($reps as $repetition) { | ||||
|             $limitAmount = $repetition->limit->amount; | ||||
|  | ||||
|             // create a serie for the repetition. | ||||
|             $currentSerie = [ | ||||
|                 'type' => 'spline', | ||||
|                 'id' => 'rep-' . $repetition->id, | ||||
|                 'yAxis' => 1, | ||||
|                 'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(), | ||||
|                 'data' => [] | ||||
|             ]; | ||||
|             $current      = clone $repetition->startdate; | ||||
|             while ($current <= $repetition->enddate) { | ||||
|                 if ($current >= $start && $current <= $end) { | ||||
|                     // spent on limit: | ||||
|                     $spentSoFar  = $this->_chart->spentOnLimitRepetitionBetweenDates( | ||||
|                         $repetition, $repetition->startdate, $current | ||||
|                     ); | ||||
|                     $leftInLimit = floatval($limitAmount) - floatval($spentSoFar); | ||||
|  | ||||
|                     // or enddate is between range. | ||||
|                     $q->orWhere( | ||||
|                         function ($q) use ($start, $end) { | ||||
|                             $q->where('enddate', '>=', $start->format('Y-m-d')); | ||||
|                             $q->where('enddate', '<=', $end->format('Y-m-d')); | ||||
|                         } | ||||
|                     ); | ||||
|                     $currentSerie['data'][] = [$current->timestamp * 1000, $leftInLimit]; | ||||
|                 } | ||||
|             ) | ||||
|                 ->get(); | ||||
|             $currentLeftInLimit = floatval($limit->amount); | ||||
|             /** @var \LimitRepetition $repetition */ | ||||
|             foreach ($reps as $repetition) { | ||||
|                 // create a serie for the repetition. | ||||
|                 $currentSerie = [ | ||||
|                     'type'  => 'spline', | ||||
|                     'id'    => 'rep-' . $repetition->id, | ||||
|                     'yAxis' => 1, | ||||
|                     'name'  => 'Envelope in ' . $repetition->periodShow(), | ||||
|                     'data'  => [] | ||||
|                 ]; | ||||
|                 $current = clone $repetition->startdate; | ||||
|                 while ($current <= $repetition->enddate) { | ||||
|                     if ($current >= Session::get('start') && $current <= Session::get('end')) { | ||||
|                         // spent on limit: | ||||
|                         $spentSoFar = \Transaction:: | ||||
|                             leftJoin( | ||||
|                                 'transaction_journals', 'transaction_journals.id', '=', | ||||
|                                 'transactions.transaction_journal_id' | ||||
|                             ) | ||||
|                             ->leftJoin( | ||||
|                                 'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', | ||||
|                                 '=', | ||||
|                                 'transaction_journals.id' | ||||
|                             )->where('component_transaction_journal.component_id', '=', $budget->id)->where( | ||||
|                                 'transaction_journals.date', '>=', $repetition->startdate->format('Y-m-d') | ||||
|                             )->where('transaction_journals.date', '<=', $current->format('Y-m-d'))->where( | ||||
|                                 'amount', '>', 0 | ||||
|                             )->sum('amount'); | ||||
|                         $spent = floatval($spent) == 0 ? null : floatval($spent); | ||||
|                         $currentLeftInLimit = floatval($limit->amount) - floatval($spentSoFar); | ||||
|  | ||||
|                         $currentSerie['data'][] = [$current->timestamp * 1000, $currentLeftInLimit]; | ||||
|                     } | ||||
|                     $current->addDay(); | ||||
|                 } | ||||
|  | ||||
|                 // do something here. | ||||
|                 $repetitionSeries[] = $currentSerie; | ||||
|  | ||||
|                 $current->addDay(); | ||||
|             } | ||||
|  | ||||
|             // do something here. | ||||
|             $series[] = $currentSerie; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $return = [ | ||||
|             'chart_title' => 'Overview for budget ' . $budget->name, | ||||
|             'subtitle'    => | ||||
|             'subtitle' => | ||||
|                 'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format( | ||||
|                     'M jS, Y' | ||||
|                 ), | ||||
|             'series'      => $repetitionSeries | ||||
|             'series' => $series | ||||
|         ]; | ||||
|  | ||||
|         return Response::json($return); | ||||
| @@ -320,14 +292,14 @@ class ChartController extends BaseController | ||||
|     public function categoryShowChart(Category $category) | ||||
|     { | ||||
|         $start = Session::get('start'); | ||||
|         $end = Session::get('end'); | ||||
|         $end   = Session::get('end'); | ||||
|         $range = Session::get('range'); | ||||
|  | ||||
|         $serie = $this->_chart->categoryShowChart($category, $range, $start, $end); | ||||
|         $data = [ | ||||
|         $data  = [ | ||||
|             'chart_title' => $category->name, | ||||
|             'subtitle'    => '<a href="' . route('categories.show', [$category->id]) . '">View more</a>', | ||||
|             'series'      => $serie | ||||
|             'subtitle' => '<a href="' . route('categories.show', [$category->id]) . '">View more</a>', | ||||
|             'series' => $serie | ||||
|         ]; | ||||
|  | ||||
|         return Response::json($data); | ||||
| @@ -344,34 +316,33 @@ class ChartController extends BaseController | ||||
|     { | ||||
|         // get preferences and accounts (if necessary): | ||||
|         $start = Session::get('start'); | ||||
|         $end = Session::get('end'); | ||||
|         $end   = Session::get('end'); | ||||
|  | ||||
|         if (is_null($account)) { | ||||
|             // get, depending on preferences: | ||||
|             /** @var  \Firefly\Helper\Preferences\PreferencesHelperInterface $prefs */ | ||||
|             $prefs = \App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); | ||||
|             $pref = $prefs->get('frontpageAccounts', []); | ||||
|             $pref  = $prefs->get('frontpageAccounts', []); | ||||
|  | ||||
|             /** @var \Firefly\Storage\Account\AccountRepositoryInterface $acct */ | ||||
|             $acct = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|             $acct     = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|             $accounts = $acct->getByIds($pref->data); | ||||
|         } else { | ||||
|             $accounts = [$account]; | ||||
|         } | ||||
|         // loop and get array data. | ||||
|  | ||||
|         $url = count($accounts) == 1 && is_array($accounts) | ||||
|         $url  = count($accounts) == 1 && is_array($accounts) | ||||
|             ? '<a href="' . route('accounts.show', [$account->id]) . '">View more</a>' | ||||
|             : | ||||
|             '<a href="' . route('accounts.index') . '">View more</a>'; | ||||
|         $data = [ | ||||
|             'chart_title' => count($accounts) == 1 ? $accounts[0]->name : 'All accounts', | ||||
|             'subtitle'    => $url, | ||||
|             'series'      => [] | ||||
|             'subtitle' => $url, | ||||
|             'series' => [] | ||||
|         ]; | ||||
|  | ||||
|         foreach ($accounts as $account) { | ||||
|             \Log::debug('Now building series for ' . $account->name); | ||||
|             $data['series'][] = $this->_chart->account($account, $start, $end); | ||||
|         } | ||||
|  | ||||
| @@ -390,7 +361,7 @@ class ChartController extends BaseController | ||||
|     { | ||||
|         $account = $this->_accounts->findByName($name); | ||||
|  | ||||
|         $date = Carbon::createFromDate($year, $month, $day); | ||||
|         $date   = Carbon::createFromDate($year, $month, $day); | ||||
|         $result = $this->_chart->accountDailySummary($account, $date); | ||||
|  | ||||
|         return View::make('charts.info')->with('rows', $result['rows'])->with('sum', $result['sum'])->with( | ||||
| @@ -403,9 +374,102 @@ class ChartController extends BaseController | ||||
|      */ | ||||
|     public function homeBudgets() | ||||
|     { | ||||
|         $start = \Session::get('start'); | ||||
|         $start = Session::get('start'); | ||||
|         $end   = Session::get('end'); | ||||
|         $data  = [ | ||||
|             'labels' => [], | ||||
|             'series' => [ | ||||
|                 [ | ||||
|                     'name' => 'Limit', | ||||
|                     'data' => [] | ||||
|                 ], | ||||
|                 [ | ||||
|                     'name' => 'Spent', | ||||
|                     'data' => [] | ||||
|                 ], | ||||
|             ] | ||||
|         ]; | ||||
|  | ||||
|         // Get all budgets. | ||||
|         $budgets   = \Auth::user()->budgets()->orderBy('name', 'ASC')->get(); | ||||
|         $budgetIds = []; | ||||
|         /** @var \Budget $budget */ | ||||
|         foreach ($budgets as $budget) { | ||||
|             $budgetIds[] = $budget->id; | ||||
|  | ||||
|             // Does the budget have a limit starting on $start? | ||||
|             $rep = \LimitRepetition:: | ||||
|             leftJoin('limits', 'limit_repetitions.limit_id', '=', 'limits.id')->leftJoin( | ||||
|                 'components', 'limits.component_id', '=', 'components.id' | ||||
|             )->where('limit_repetitions.startdate', $start->format('Y-m-d'))->where( | ||||
|                 'components.id', $budget->id | ||||
|             )->first(['limit_repetitions.*']); | ||||
|  | ||||
|             if (is_null($rep)) { | ||||
|                 $limit     = 0.0; | ||||
|                 $id        = null; | ||||
|                 $parameter = 'useSession=true'; | ||||
|             } else { | ||||
|                 $limit     = floatval($rep->amount); | ||||
|                 $id        = $rep->id; | ||||
|                 $parameter = ''; | ||||
|             } | ||||
|  | ||||
|             // Date range to check for expenses made? | ||||
|             if (is_null($rep)) { | ||||
|                 // use the session start and end for our search query | ||||
|                 $expenseStart = Session::get('start'); | ||||
|                 $expenseEnd   = Session::get('end'); | ||||
|  | ||||
|             } else { | ||||
|                 // use the limit's start and end for our search query | ||||
|                 $expenseStart = $rep->startdate; | ||||
|                 $expenseEnd   = $rep->enddate; | ||||
|             } | ||||
|             // How much have we spent on this budget? | ||||
|             $expenses = floatval($budget->transactionjournals()->before($expenseEnd)->after($expenseStart)->lessThan(0)->sum('amount')) * -1; | ||||
|  | ||||
|             // Append to chart: | ||||
|             if ($limit > 0 || $expenses > 0) { | ||||
|                 $data['labels'][]            = $budget->name; | ||||
|                 $data['series'][0]['data'][] = [ | ||||
|                     'y' => $limit, | ||||
|                     'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter | ||||
|                 ]; | ||||
|                 $data['series'][1]['data'][] = [ | ||||
|                     'y' => $expenses, | ||||
|                     'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
|         // Add expenses that have no budget: | ||||
|         $set = \Auth::user()->transactionjournals()->whereNotIn( | ||||
|             'transaction_journals.id', function ($query) use ($start, $end) { | ||||
|                 $query->select('transaction_journals.id')->from('transaction_journals') | ||||
|                       ->leftJoin( | ||||
|                           'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', | ||||
|                           'transaction_journals.id' | ||||
|                       ) | ||||
|                       ->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id') | ||||
|                       ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) | ||||
|                       ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) | ||||
|                       ->where('components.class', 'Budget'); | ||||
|             } | ||||
|         )->before($end)->after($start)->lessThan(0)->transactionTypes(['Withdrawal'])->sum('amount'); | ||||
|  | ||||
|         // This can be debugged by using get(['transaction_journals.*','transactions.amount']); | ||||
|         $data['labels'][]            = 'No budget'; | ||||
|         $data['series'][0]['data'][] = [ | ||||
|             'y' => 0, | ||||
|             'url' => route('budgets.nobudget', 'session') | ||||
|         ]; | ||||
|         $data['series'][1]['data'][] = [ | ||||
|             'y' => floatval($set) * -1, | ||||
|             'url' => route('budgets.nobudget', 'session') | ||||
|         ]; | ||||
|  | ||||
|         return Response::json($data); | ||||
|  | ||||
|         return Response::json($this->_chart->budgets($start)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -414,10 +478,126 @@ class ChartController extends BaseController | ||||
|     public function homeCategories() | ||||
|     { | ||||
|         $start = Session::get('start'); | ||||
|         $end = Session::get('end'); | ||||
|         $end   = Session::get('end'); | ||||
|  | ||||
|         return Response::json($this->_chart->categories($start, $end)); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method checks all recurring transactions, calculates the current "moment" and returns | ||||
|      * a list of yet to be paid (and paid) recurring transactions. This list can be used to show a beautiful chart | ||||
|      * to the end user who will love it and cherish it. | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function homeRecurring() | ||||
|     { | ||||
|         /** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); | ||||
|  | ||||
|         /* | ||||
|          * Set of paid transaction journals. | ||||
|          * Set of unpaid recurring transactions. | ||||
|          */ | ||||
|         $paid   = []; | ||||
|         $unpaid = []; | ||||
|  | ||||
|         /* | ||||
|          * Loop the recurring transactions. | ||||
|          */ | ||||
|  | ||||
|         /** @var \RecurringTransaction $recurring */ | ||||
|         foreach (\Auth::user()->recurringtransactions()->get() as $recurring) { | ||||
|             /* | ||||
|              * Start another loop starting at the $date. | ||||
|              */ | ||||
|             $start = clone $recurring->date; | ||||
|             $end   = Carbon::now(); | ||||
|  | ||||
|             /* | ||||
|              * The jump we make depends on the $repeat_freq | ||||
|              */ | ||||
|             $current = clone $start; | ||||
|  | ||||
|             while ($current <= $end) { | ||||
|                 /* | ||||
|                  * Get end of period for $current: | ||||
|                  */ | ||||
|                 $currentEnd = clone $current; | ||||
|                 $toolkit->endOfPeriod($currentEnd, $recurring->repeat_freq); | ||||
|  | ||||
|                 /* | ||||
|                  * In the current session range? | ||||
|                  */ | ||||
|                 if (\Session::get('end') >= $current and $currentEnd >= \Session::get('start')) { | ||||
|                     /* | ||||
|                      * Lets see if we've already spent money on this recurring transaction (it hath recurred). | ||||
|                      */ | ||||
|                     /** @var TransactionJournal $set */ | ||||
|                     $transaction = \Auth::user()->transactionjournals()->where('recurring_transaction_id', $recurring->id)->after($current)->before($currentEnd)->first(); | ||||
|  | ||||
|                     if(is_null($transaction)) { | ||||
|                         $unpaid[] = $recurring; | ||||
|                     } else { | ||||
|                         $paid[] = $transaction; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 /* | ||||
|                  * Add some time for the next loop! | ||||
|                  */ | ||||
|                 $toolkit->addPeriod($current, $recurring->repeat_freq, intval($recurring->skip)); | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         /* | ||||
|          * Get some colors going. | ||||
|          */ | ||||
|         $unPaidColours = $toolkit->colorRange('AA4643', 'FFFFFF', count($unpaid) == 0 ? 1 : count($unpaid)); | ||||
|         $paidColours   = $toolkit->colorRange('4572A7', 'FFFFFF', count($paid) == 0 ? 1 : count($paid)); | ||||
|  | ||||
|         /* | ||||
|          * The chart serie: | ||||
|          */ | ||||
|         $serie         = [ | ||||
|             'type' => 'pie', | ||||
|             'name' => 'Amount', | ||||
|             'data' => [] | ||||
|         ]; | ||||
|  | ||||
|         /* | ||||
|          * Loop paid and unpaid to make two haves for a pie chart. | ||||
|          */ | ||||
|         /** @var TransactionJournal $entry */ | ||||
|         foreach ($paid as $index => $entry) { | ||||
|             $transactions    = $entry->transactions()->get(); | ||||
|             $amount          = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount)); | ||||
|             $serie['data'][] = [ | ||||
|                 'name' => 'Already paid "'.$entry->description.'"', | ||||
|                 'y' => $amount, | ||||
|                 'url' => route('transactions.show',$entry->id), | ||||
|                 'objType' => 'paid', | ||||
|                 'color' => $paidColours[$index] | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /** @var RecurringTransaction $entry */ | ||||
|         foreach ($unpaid as $index => $entry) { | ||||
|             $amount          = (floatval($entry->amount_max) + floatval($entry->amount_min)) / 2; | ||||
|             $serie['data'][] = [ | ||||
|                 'name' => 'Still due: '.$entry->name, | ||||
|                 'y' => $amount, | ||||
|                 'url' => route('recurring.show',$entry->id), | ||||
|                 'objType' => 'unpaid', | ||||
|                 'color' => $unPaidColours[$index] | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return Response::json([$serie]); | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,26 +1,73 @@ | ||||
| <?php | ||||
| use Carbon\Carbon; | ||||
| use Firefly\Helper\Preferences\PreferencesHelperInterface as PHI; | ||||
| use Firefly\Storage\Account\AccountRepositoryInterface as ARI; | ||||
| use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI; | ||||
| use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI; | ||||
|  | ||||
| /** | ||||
|  * Class HomeController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class HomeController extends BaseController | ||||
| { | ||||
|     protected $_accounts; | ||||
|     protected $_preferences; | ||||
|     protected $_journal; | ||||
|     protected $_reminders; | ||||
|  | ||||
|     public function __construct(ARI $accounts, PHI $preferences, TJRI $journal, RRI $reminders) | ||||
|     /** | ||||
|      * @param ARI $accounts | ||||
|      * @param PHI $preferences | ||||
|      * @param TJRI $journal | ||||
|      */ | ||||
|     public function __construct(ARI $accounts, PHI $preferences, TJRI $journal) | ||||
|     { | ||||
|         $this->_accounts = $accounts; | ||||
|         $this->_accounts    = $accounts; | ||||
|         $this->_preferences = $preferences; | ||||
|         $this->_journal = $journal; | ||||
|         $this->_reminders = $reminders; | ||||
|         $this->_journal     = $journal; | ||||
|     } | ||||
|  | ||||
|     public function jobDev() | ||||
|     { | ||||
|         $fullName = storage_path() . DIRECTORY_SEPARATOR . 'firefly-export-2014-07-23.json'; | ||||
|         \Log::notice('Pushed start job.'); | ||||
|         Queue::push('Firefly\Queue\Import@start', ['file' => $fullName, 'user' => 1]); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * | ||||
|      */ | ||||
|     public function sessionPrev() | ||||
|     { | ||||
|         /** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); | ||||
|         $toolkit->prev(); | ||||
|         return Redirect::back(); | ||||
|         //return Redirect::route('index'); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * | ||||
|      */ | ||||
|     public function sessionNext() | ||||
|     { | ||||
|         /** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); | ||||
|         $toolkit->next(); | ||||
|         return Redirect::back(); | ||||
|         //return Redirect::route('index'); | ||||
|     } | ||||
|  | ||||
|     public function rangeJump($range) | ||||
|     { | ||||
|  | ||||
|         $valid = ['1D', '1W', '1M', '3M', '6M', '1Y',]; | ||||
|  | ||||
|         if (in_array($range, $valid)) { | ||||
|             $this->_preferences->set('viewRange', $range); | ||||
|             Session::forget('range'); | ||||
|         } | ||||
|         return Redirect::back(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -38,16 +85,14 @@ class HomeController extends BaseController | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         Event::fire('limits.check'); | ||||
|         Event::fire('piggybanks.check'); | ||||
|         Event::fire('recurring.check'); | ||||
|  | ||||
|         \Event::fire('limits.check'); | ||||
|         \Event::fire('piggybanks.check'); | ||||
|         \Event::fire('recurring.check'); | ||||
|  | ||||
|  | ||||
|         // count, maybe we need some introducing text to show: | ||||
|         // count, maybe Firefly needs some introducing text to show: | ||||
|         $count = $this->_accounts->count(); | ||||
|         $start = Session::get('start'); | ||||
|         $end = Session::get('end'); | ||||
|         $end   = Session::get('end'); | ||||
|  | ||||
|  | ||||
|         // get the preference for the home accounts to show: | ||||
| @@ -66,21 +111,8 @@ class HomeController extends BaseController | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (count($transactions) % 2 == 0) { | ||||
|             $transactions = array_chunk($transactions, 2); | ||||
|         } elseif (count($transactions) == 1) { | ||||
|             $transactions = array_chunk($transactions, 3); | ||||
|         } else { | ||||
|             $transactions = array_chunk($transactions, 3); | ||||
|         } | ||||
|  | ||||
|         // get the users reminders: | ||||
|  | ||||
|         $reminders =  $this->_reminders->getCurrentRecurringReminders(); | ||||
|  | ||||
|         // build the home screen: | ||||
|         return View::make('index')->with('count', $count)->with('transactions', $transactions)->with( | ||||
|             'reminders', $reminders | ||||
|         ); | ||||
|         return View::make('index')->with('count', $count)->with('transactions', $transactions)->with('title', 'Firefly') | ||||
|                    ->with('subTitle', 'What\'s playing?')->with('mainTitleIcon', 'fa-fire'); | ||||
|     } | ||||
| } | ||||
| @@ -1,41 +1,57 @@ | ||||
| <?php | ||||
|  | ||||
| use Firefly\Storage\Account\AccountRepositoryInterface as ARI; | ||||
| use Firefly\Storage\Budget\BudgetRepositoryInterface as Bud; | ||||
| use Firefly\Storage\Category\CategoryRepositoryInterface as Cat; | ||||
| use Firefly\Storage\Component\ComponentRepositoryInterface as CRI; | ||||
| use Firefly\Helper\Controllers\JsonInterface as JI; | ||||
| use Illuminate\Support\Collection; | ||||
| use LaravelBook\Ardent\Builder; | ||||
|  | ||||
| /** | ||||
|  * Class JsonController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class JsonController extends BaseController | ||||
| { | ||||
|     protected $_accounts; | ||||
|     protected $_components; | ||||
|     protected $_categories; | ||||
|     protected $_budgets; | ||||
|     /** @var \Firefly\Helper\Controllers\JsonInterface $helper */ | ||||
|     protected $helper; | ||||
|  | ||||
|     public function __construct(JI $helper) | ||||
|     { | ||||
|         $this->helper = $helper; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param ARI $accounts | ||||
|      * @param CRI $components | ||||
|      * @param Cat $categories | ||||
|      * @param Bud $budgets | ||||
|      * Returns a list of categories. | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function __construct(ARI $accounts, CRI $components, Cat $categories, Bud $budgets) | ||||
|     public function categories() | ||||
|     { | ||||
|         $this->_components = $components; | ||||
|         $this->_accounts = $accounts; | ||||
|         $this->_categories = $categories; | ||||
|         $this->_budgets = $budgets; | ||||
|         /** @var \Firefly\Storage\Category\EloquentCategoryRepository $categories */ | ||||
|         $categories = App::make('Firefly\Storage\Category\CategoryRepositoryInterface'); | ||||
|         $list       = $categories->get(); | ||||
|         $return     = []; | ||||
|         foreach ($list as $entry) { | ||||
|             $return[] = $entry->name; | ||||
|         } | ||||
|  | ||||
|         return Response::json($return); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a JSON list of all beneficiaries. | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function beneficiaries() | ||||
|     public function expenseAccounts() | ||||
|     { | ||||
|         $list = $this->_accounts->getBeneficiaries(); | ||||
|         $return = []; | ||||
|         /** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */ | ||||
|         $accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|         $list     = $accounts->getOfTypes(['Expense account', 'Beneficiary account']); | ||||
|         $return   = []; | ||||
|         foreach ($list as $entry) { | ||||
|             $return[] = $entry->name; | ||||
|         } | ||||
| @@ -45,18 +61,126 @@ class JsonController extends BaseController | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Responds some JSON for typeahead fields. | ||||
|      * Returns a list of transactions, expenses only, using the given parameters. | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function categories() | ||||
|     public function expenses() | ||||
|     { | ||||
|         $list = $this->_categories->get(); | ||||
|         $return = []; | ||||
|  | ||||
|         /* | ||||
|          * Gets most parameters from the Input::all() array: | ||||
|          */ | ||||
|         $parameters = $this->helper->dataTableParameters(); | ||||
|  | ||||
|         /* | ||||
|          * Add some more parameters to fine tune the query: | ||||
|          */ | ||||
|         $parameters['transactionTypes'] = ['Withdrawal']; | ||||
|         $parameters['amount']           = 'negative'; | ||||
|  | ||||
|         /* | ||||
|          * Get the query: | ||||
|          */ | ||||
|         $query = $this->helper->journalQuery($parameters); | ||||
|  | ||||
|         /* | ||||
|          * Build result set: | ||||
|          */ | ||||
|         $resultSet = $this->helper->journalDataset($parameters, $query); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Build return data: | ||||
|          */ | ||||
|         return Response::json($resultSet); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function recurringjournals(RecurringTransaction $recurringTransaction) | ||||
|     { | ||||
|         $parameters                     = $this->helper->dataTableParameters(); | ||||
|         $parameters['transactionTypes'] = ['Withdrawal']; | ||||
|         $parameters['amount']           = 'negative'; | ||||
|  | ||||
|         $query = $this->helper->journalQuery($parameters); | ||||
|  | ||||
|         $query->where('recurring_transaction_id', $recurringTransaction->id); | ||||
|         $resultSet = $this->helper->journalDataset($parameters, $query); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Build return data: | ||||
|          */ | ||||
|         return Response::json($resultSet); | ||||
|     } | ||||
|  | ||||
|     public function recurring() | ||||
|     { | ||||
|         $parameters = $this->helper->dataTableParameters(); | ||||
|         $query      = $this->helper->recurringTransactionsQuery($parameters); | ||||
|         $resultSet  = $this->helper->recurringTransactionsDataset($parameters, $query); | ||||
|         return Response::json($resultSet); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \Illuminate\Http\JsonResponse|string | ||||
|      */ | ||||
|     public function revenue() | ||||
|     { | ||||
|         $parameters                     = $this->helper->dataTableParameters(); | ||||
|         $parameters['transactionTypes'] = ['Deposit']; | ||||
|         $parameters['amount']           = 'positive'; | ||||
|  | ||||
|         $query     = $this->helper->journalQuery($parameters); | ||||
|         $resultSet = $this->helper->journalDataset($parameters, $query); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Build return data: | ||||
|          */ | ||||
|         return Response::json($resultSet); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a JSON list of all revenue accounts. | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function revenueAccounts() | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */ | ||||
|         $accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|         $list     = $accounts->getOfTypes(['Revenue account']); | ||||
|         $return   = []; | ||||
|         foreach ($list as $entry) { | ||||
|             $return[] = $entry->name; | ||||
|         } | ||||
|  | ||||
|         return Response::json($return); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a list of all transfers. | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function transfers() | ||||
|     { | ||||
|         $parameters                     = $this->helper->dataTableParameters(); | ||||
|         $parameters['transactionTypes'] = ['Transfer']; | ||||
|         $parameters['amount']           = 'positive'; | ||||
|  | ||||
|         $query     = $this->helper->journalQuery($parameters); | ||||
|         $resultSet = $this->helper->journalDataset($parameters, $query); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Build return data: | ||||
|          */ | ||||
|         return Response::json($resultSet); | ||||
|     } | ||||
| }  | ||||
| @@ -5,6 +5,8 @@ use Firefly\Storage\Limit\LimitRepositoryInterface as LRI; | ||||
|  | ||||
| /** | ||||
|  * Class LimitController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class LimitController extends BaseController | ||||
| { | ||||
| @@ -19,7 +21,10 @@ class LimitController extends BaseController | ||||
|     public function __construct(BRI $budgets, LRI $limits) | ||||
|     { | ||||
|         $this->_budgets = $budgets; | ||||
|         $this->_limits = $limits; | ||||
|         $this->_limits  = $limits; | ||||
|  | ||||
|         View::share('title','Envelopes'); | ||||
|         View::share('mainTitleIcon', 'fa-tasks'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -29,18 +34,20 @@ class LimitController extends BaseController | ||||
|      */ | ||||
|     public function create(\Budget $budget = null) | ||||
|     { | ||||
|         $periods = \Config::get('firefly.periods_to_text'); | ||||
|         $periods   = \Config::get('firefly.periods_to_text'); | ||||
|         $prefilled = [ | ||||
|             'startdate'   => \Input::get('startdate') ? : date('Y-m-d'), | ||||
|             'repeat_freq' => \Input::get('repeat_freq') ? : 'monthly', | ||||
|             'budget_id'   => $budget ? $budget->id : null | ||||
|         ]; | ||||
|  | ||||
|         $budgets = $this->_budgets->getAsSelectList(); | ||||
|         /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); | ||||
|         $budgets = $toolkit->makeSelectList($this->_budgets->get()); | ||||
|  | ||||
|         return View::make('limits.create')->with('budgets', $budgets)->with( | ||||
|             'periods', $periods | ||||
|         )->with('prefilled', $prefilled); | ||||
|         )->with('prefilled', $prefilled)->with('subTitle','New envelope'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -50,7 +57,7 @@ class LimitController extends BaseController | ||||
|      */ | ||||
|     public function delete(\Limit $limit) | ||||
|     { | ||||
|         return View::make('limits.delete')->with('limit', $limit); | ||||
|         return View::make('limits.delete')->with('limit', $limit)->with('subTitle','Delete envelope'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -82,12 +89,15 @@ class LimitController extends BaseController | ||||
|      */ | ||||
|     public function edit(Limit $limit) | ||||
|     { | ||||
|         $budgets = $this->_budgets->getAsSelectList(); | ||||
|         /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); | ||||
|  | ||||
|         $budgets    = $toolkit->makeSelectList($this->_budgets->get()); | ||||
|         $periods = \Config::get('firefly.periods_to_text'); | ||||
|  | ||||
|         return View::make('limits.edit')->with('limit', $limit)->with('budgets', $budgets)->with( | ||||
|             'periods', $periods | ||||
|         ); | ||||
|         )->with('subTitle','Edit envelope'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -98,7 +108,7 @@ class LimitController extends BaseController | ||||
|     public function store(Budget $budget = null) | ||||
|     { | ||||
|  | ||||
|         // find a limit with these properties, as we might already have one: | ||||
|         // find a limit with these properties, as Firefly might already have one: | ||||
|         $limit = $this->_limits->store(Input::all()); | ||||
|         if ($limit->validate()) { | ||||
|             Session::flash('success', 'Envelope created!'); | ||||
| @@ -110,7 +120,7 @@ class LimitController extends BaseController | ||||
|             } | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not save new envelope.'); | ||||
|             $budgetId = $budget ? $budget->id : null; | ||||
|             $budgetId   = $budget ? $budget->id : null; | ||||
|             $parameters = [$budgetId, 'from' => Input::get('from')]; | ||||
|  | ||||
|             return Redirect::route('budgets.limits.create', $parameters)->withInput() | ||||
|   | ||||
							
								
								
									
										52
									
								
								app/controllers/MigrateController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								app/controllers/MigrateController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Class MigrateController | ||||
|  */ | ||||
| class MigrateController extends BaseController | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         return View::make('migrate.index')->with('index', 'Migration')->with('title','Migrate')-> | ||||
|             with('subTitle','From Firefly II to Firefly III'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function upload() | ||||
|     { | ||||
|         if (Input::hasFile('file') && Input::file('file')->isValid()) { | ||||
|             $path     = storage_path(); | ||||
|             $fileName = 'firefly-iii-import-' . date('Y-m-d-H-i') . '.json'; | ||||
|             $fullName = $path . DIRECTORY_SEPARATOR . $fileName; | ||||
|             if (Input::file('file')->move($path, $fileName)) { | ||||
|                 // so now Firefly pushes something in a queue and does something with it! Yay! | ||||
|                 \Log::debug('Pushed a job to start the import.'); | ||||
|                 Queue::push('Firefly\Queue\Import@start', ['file' => $fullName, 'user' => \Auth::user()->id]); | ||||
|                 if (Config::get('queue.default') == 'sync') { | ||||
|                     Session::flash('success', 'Your data has been imported!'); | ||||
|                 } else { | ||||
|                     Session::flash( | ||||
|                         'success', | ||||
|                         'The import job has been queued. Please be patient. Data will appear slowly. Please be patient.' | ||||
|                     ); | ||||
|                 } | ||||
|  | ||||
|                 return Redirect::route('index'); | ||||
|             } | ||||
|             Session::flash('error', 'Could not save file to storage.'); | ||||
|             return Redirect::route('migrate.index'); | ||||
|  | ||||
|         } else { | ||||
|             Session::flash('error', 'Please upload a file.'); | ||||
|             return Redirect::route('migrate.index'); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| }  | ||||
| @@ -7,12 +7,16 @@ use Firefly\Storage\Piggybank\PiggybankRepositoryInterface as PRI; | ||||
| /** | ||||
|  * Class PiggybankController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||
|  * @SuppressWarnings(PHPMD.TooManyMethods) | ||||
|  * | ||||
|  */ | ||||
| class PiggybankController extends BaseController | ||||
| { | ||||
|  | ||||
|     protected $_repository; | ||||
|     protected $_accounts; | ||||
|     protected $_repository; | ||||
|  | ||||
|     /** | ||||
|      * @param PRI $repository | ||||
| @@ -21,16 +25,18 @@ class PiggybankController extends BaseController | ||||
|     public function __construct(PRI $repository, ARI $accounts) | ||||
|     { | ||||
|         $this->_repository = $repository; | ||||
|         $this->_accounts = $accounts; | ||||
|         $this->_accounts   = $accounts; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Piggybank $piggyBank | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function addMoney(Piggybank $piggyBank) | ||||
|     { | ||||
|         $what = 'add'; | ||||
|         $maxAdd = $this->_repository->leftOnAccount($piggyBank->account); | ||||
|         $what      = 'add'; | ||||
|         $maxAdd    = $this->_repository->leftOnAccount($piggyBank->account); | ||||
|         $maxRemove = null; | ||||
|  | ||||
|         return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with( | ||||
| @@ -43,10 +49,20 @@ class PiggybankController extends BaseController | ||||
|      */ | ||||
|     public function createPiggybank() | ||||
|     { | ||||
|         $periods = Config::get('firefly.piggybank_periods'); | ||||
|         $accounts = $this->_accounts->getActiveDefaultAsSelectList(); | ||||
|         /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); | ||||
|  | ||||
|         return View::make('piggybanks.create-piggybank')->with('accounts', $accounts)->with('periods', $periods); | ||||
|  | ||||
|         $periods  = Config::get('firefly.piggybank_periods'); | ||||
|         $list     = $this->_accounts->getActiveDefault(); | ||||
|         $accounts = $toolkit->makeSelectList($list); | ||||
|  | ||||
|         View::share('title', 'Piggy banks'); | ||||
|         View::share('subTitle', 'Create new'); | ||||
|         View::share('mainTitleIcon', 'fa-sort-amount-asc'); | ||||
|  | ||||
|         return View::make('piggybanks.create-piggybank')->with('accounts', $accounts) | ||||
|             ->with('periods', $periods); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -54,8 +70,16 @@ class PiggybankController extends BaseController | ||||
|      */ | ||||
|     public function createRepeated() | ||||
|     { | ||||
|         $periods = Config::get('firefly.piggybank_periods'); | ||||
|         $accounts = $this->_accounts->getActiveDefaultAsSelectList(); | ||||
|         /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); | ||||
|  | ||||
|         $periods  = Config::get('firefly.piggybank_periods'); | ||||
|         $list     = $this->_accounts->getActiveDefault(); | ||||
|         $accounts = $toolkit->makeSelectList($list); | ||||
|  | ||||
|         View::share('title', 'Repeated expenses'); | ||||
|         View::share('subTitle', 'Create new'); | ||||
|         View::share('mainTitleIcon', 'fa-rotate-right'); | ||||
|  | ||||
|         return View::make('piggybanks.create-repeated')->with('accounts', $accounts)->with('periods', $periods); | ||||
|     } | ||||
| @@ -67,6 +91,15 @@ class PiggybankController extends BaseController | ||||
|      */ | ||||
|     public function delete(Piggybank $piggyBank) | ||||
|     { | ||||
|         View::share('subTitle', 'Delete "' . $piggyBank->name . '"'); | ||||
|         if ($piggyBank->repeats == 1) { | ||||
|             View::share('title', 'Repeated expenses'); | ||||
|             View::share('mainTitleIcon', 'fa-rotate-right'); | ||||
|         } else { | ||||
|             View::share('title', 'Piggy banks'); | ||||
|             View::share('mainTitleIcon', 'fa-sort-amount-asc'); | ||||
|         } | ||||
|  | ||||
|         return View::make('piggybanks.delete')->with('piggybank', $piggyBank); | ||||
|     } | ||||
|  | ||||
| @@ -78,11 +111,18 @@ class PiggybankController extends BaseController | ||||
|     public function destroy(Piggybank $piggyBank) | ||||
|     { | ||||
|         Event::fire('piggybanks.destroy', [$piggyBank]); | ||||
|         if ($piggyBank->repeats == 1) { | ||||
|             $route   = 'piggybanks.index.repeated'; | ||||
|             $message = 'Repeated expense'; | ||||
|         } else { | ||||
|             $route   = 'piggybanks.index.piggybanks'; | ||||
|             $message = 'Piggybank'; | ||||
|         } | ||||
|         $this->_repository->destroy($piggyBank); | ||||
|  | ||||
|         Session::flash('success', 'Piggy bank deleted.'); | ||||
|         Session::flash('success', $message . ' deleted.'); | ||||
|  | ||||
|         return Redirect::route('piggybanks.index'); | ||||
|         return Redirect::route($route); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -92,12 +132,28 @@ class PiggybankController extends BaseController | ||||
|      */ | ||||
|     public function edit(Piggybank $piggyBank) | ||||
|     { | ||||
|         $accounts = $this->_accounts->getActiveDefaultAsSelectList(); | ||||
|         $periods = Config::get('firefly.piggybank_periods'); | ||||
|         /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); | ||||
|  | ||||
|         $list     = $this->_accounts->getActiveDefault(); | ||||
|         $accounts = $toolkit->makeSelectList($list); | ||||
|         $periods  = Config::get('firefly.piggybank_periods'); | ||||
|  | ||||
|  | ||||
|         View::share('subTitle', 'Edit "' . $piggyBank->name . '"'); | ||||
|  | ||||
|  | ||||
|         if ($piggyBank->repeats == 1) { | ||||
|             View::share('title', 'Repeated expenses'); | ||||
|             View::share('mainTitleIcon', 'fa-rotate-left'); | ||||
|  | ||||
|             return View::make('piggybanks.edit-repeated')->with('piggybank', $piggyBank)->with('accounts', $accounts) | ||||
|                 ->with('periods', $periods); | ||||
|         } else { | ||||
|             // piggy bank. | ||||
|             View::share('title', 'Piggy banks'); | ||||
|             View::share('mainTitleIcon', 'fa-sort-amount-asc'); | ||||
|  | ||||
|             return View::make('piggybanks.edit-piggybank')->with('piggybank', $piggyBank)->with('accounts', $accounts) | ||||
|                 ->with('periods', $periods); | ||||
|         } | ||||
| @@ -105,35 +161,6 @@ class PiggybankController extends BaseController | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         $countRepeating = $this->_repository->countRepeating(); | ||||
|         $countNonRepeating = $this->_repository->countNonrepeating(); | ||||
|  | ||||
|         $piggybanks = $this->_repository->get(); | ||||
|  | ||||
|         // get the accounts with each piggy bank and check their balance; we might need to | ||||
|         // show the user a correction. | ||||
|  | ||||
|         $accounts = []; | ||||
|         /** @var \Piggybank $piggybank */ | ||||
|         foreach ($piggybanks as $piggybank) { | ||||
|             $account = $piggybank->account; | ||||
|             $id = $account->id; | ||||
|             if (!isset($accounts[$id])) { | ||||
|                 $accounts[$id] = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return View::make('piggybanks.index')->with('piggybanks', $piggybanks) | ||||
|             ->with('countRepeating', $countRepeating) | ||||
|             ->with('countNonRepeating', $countNonRepeating) | ||||
|             ->with('accounts', $accounts); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Piggybank $piggyBank | ||||
|      * | ||||
| @@ -158,7 +185,7 @@ class PiggybankController extends BaseController | ||||
|                 } | ||||
|                 break; | ||||
|             case 'remove': | ||||
|                 $rep = $piggyBank->currentRelevantRep(); | ||||
|                 $rep       = $piggyBank->currentRelevantRep(); | ||||
|                 $maxRemove = $rep->currentamount; | ||||
|                 if (round($amount, 2) <= round($maxRemove, 2)) { | ||||
|                     Session::flash('success', 'Amount updated!'); | ||||
| @@ -169,17 +196,58 @@ class PiggybankController extends BaseController | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|         if($piggyBank->repeats == 1) { | ||||
|             $route   = 'piggybanks.index.repeated'; | ||||
|  | ||||
|         return Redirect::route('piggybanks.index'); | ||||
|         } else { | ||||
|             $route   = 'piggybanks.index.piggybanks'; | ||||
|         } | ||||
|         return Redirect::route($route); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function piggybanks() | ||||
|     { | ||||
|         $countRepeating    = $this->_repository->countRepeating(); | ||||
|         $countNonRepeating = $this->_repository->countNonrepeating(); | ||||
|  | ||||
|         $piggybanks = $this->_repository->get(); | ||||
|  | ||||
|         // get the accounts with each piggy bank and check their balance; Fireflyy might needs to | ||||
|         // show the user a correction. | ||||
|  | ||||
|         $accounts = []; | ||||
|         /** @var \Piggybank $piggybank */ | ||||
|         foreach ($piggybanks as $piggybank) { | ||||
|             $account = $piggybank->account; | ||||
|             $id      = $account->id; | ||||
|             if (!isset($accounts[$id])) { | ||||
|                 $account->leftOnAccount = $this->_repository->leftOnAccount($account); | ||||
|                 $accounts[$id]          = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         View::share('title', 'Piggy banks'); | ||||
|         View::share('subTitle', 'Save for big expenses'); | ||||
|         View::share('mainTitleIcon', 'fa-sort-amount-asc'); | ||||
|  | ||||
|         return View::make('piggybanks.index')->with('piggybanks', $piggybanks) | ||||
|             ->with('countRepeating', $countRepeating) | ||||
|             ->with('countNonRepeating', $countNonRepeating) | ||||
|             ->with('accounts', $accounts); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Piggybank $piggyBank | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function removeMoney(Piggybank $piggyBank) | ||||
|     { | ||||
|         $what = 'remove'; | ||||
|         $maxAdd = $this->_repository->leftOnAccount($piggyBank->account); | ||||
|         $what      = 'remove'; | ||||
|         $maxAdd    = $this->_repository->leftOnAccount($piggyBank->account); | ||||
|         $maxRemove = $piggyBank->currentRelevantRep()->currentamount; | ||||
|  | ||||
|         return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with( | ||||
| @@ -187,13 +255,60 @@ class PiggybankController extends BaseController | ||||
|         )->with('piggybank', $piggyBank); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function repeated() | ||||
|     { | ||||
|         $countRepeating    = $this->_repository->countRepeating(); | ||||
|         $countNonRepeating = $this->_repository->countNonrepeating(); | ||||
|  | ||||
|         $piggybanks = $this->_repository->get(); | ||||
|  | ||||
|         // get the accounts with each piggy bank and check their balance; Fireflyy might needs to | ||||
|         // show the user a correction. | ||||
|  | ||||
|         $accounts = []; | ||||
|         /** @var \Piggybank $piggybank */ | ||||
|         foreach ($piggybanks as $piggybank) { | ||||
|             $account = $piggybank->account; | ||||
|             $id      = $account->id; | ||||
|             if (!isset($accounts[$id])) { | ||||
|                 $account->leftOnAccount = $this->_repository->leftOnAccount($account); | ||||
|                 $accounts[$id]          = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         View::share('title', 'Repeated expenses'); | ||||
|         View::share('subTitle', 'Save for returning bills'); | ||||
|         View::share('mainTitleIcon', 'fa-rotate-left'); | ||||
|  | ||||
|  | ||||
|         return View::make('piggybanks.index')->with('piggybanks', $piggybanks) | ||||
|             ->with('countRepeating', $countRepeating) | ||||
|             ->with('countNonRepeating', $countNonRepeating) | ||||
|             ->with('accounts', $accounts); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function show(Piggybank $piggyBank) | ||||
|     { | ||||
|         $leftOnAccount = $this->_repository->leftOnAccount($piggyBank->account); | ||||
|         $balance = $piggyBank->account->balance(); | ||||
|         $balance       = $piggyBank->account->balance(); | ||||
|  | ||||
|         View::share('subTitle', $piggyBank->name); | ||||
|  | ||||
|         if ($piggyBank->repeats == 1) { | ||||
|             // repeated expense. | ||||
|             View::share('title', 'Repeated expenses'); | ||||
|             View::share('mainTitleIcon', 'fa-rotate-left'); | ||||
|         } else { | ||||
|             // piggy bank. | ||||
|             View::share('title', 'Piggy banks'); | ||||
|             View::share('mainTitleIcon', 'fa-sort-amount-asc'); | ||||
|         } | ||||
|  | ||||
|         return View::make('piggybanks.show')->with('piggyBank', $piggyBank)->with('leftOnAccount', $leftOnAccount) | ||||
|             ->with('balance', $balance); | ||||
| @@ -208,17 +323,17 @@ class PiggybankController extends BaseController | ||||
|         unset($data['_token']); | ||||
|  | ||||
|         // extend the data array with the settings needed to create a piggy bank: | ||||
|         $data['repeats'] = 0; | ||||
|         $data['repeats']   = 0; | ||||
|         $data['rep_times'] = 1; | ||||
|         $data['rep_every'] = 1; | ||||
|         $data['order'] = 0; | ||||
|         $data['order']     = 0; | ||||
|  | ||||
|         $piggyBank = $this->_repository->store($data); | ||||
|         if (!is_null($piggyBank->id)) { | ||||
|             Session::flash('success', 'New piggy bank "' . $piggyBank->name . '" created!'); | ||||
|             Event::fire('piggybanks.store', [$piggyBank]); | ||||
|  | ||||
|             return Redirect::route('piggybanks.index'); | ||||
|             return Redirect::route('piggybanks.index.piggybanks'); | ||||
|  | ||||
|  | ||||
|         } else { | ||||
| @@ -240,14 +355,13 @@ class PiggybankController extends BaseController | ||||
|  | ||||
|         // extend the data array with the settings needed to create a repeated: | ||||
|         $data['repeats'] = 1; | ||||
|         $data['order'] = 0; | ||||
|         $data['order']   = 0; | ||||
|  | ||||
|         $piggyBank = $this->_repository->store($data); | ||||
|         if ($piggyBank->id) { | ||||
|             Session::flash('success', 'New piggy bank "' . $piggyBank->name . '" created!'); | ||||
|             Event::fire('piggybanks.store', [$piggyBank]); | ||||
|  | ||||
|             return Redirect::route('piggybanks.index'); | ||||
|             return Redirect::route('piggybanks.index.repeated'); | ||||
|  | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not save piggy bank: ' . $piggyBank->errors()->first()); | ||||
| @@ -258,16 +372,27 @@ class PiggybankController extends BaseController | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Piggybank $piggyBank | ||||
|      * | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse | ||||
|      */ | ||||
|     public function update(Piggybank $piggyBank) | ||||
|     { | ||||
|         $piggyBank = $this->_repository->update($piggyBank, Input::all()); | ||||
|         if ($piggyBank->validate()) { | ||||
|             Session::flash('success', 'Piggy bank "' . $piggyBank->name . '" updated.'); | ||||
|             if ($piggyBank->repeats == 1) { | ||||
|                 $route   = 'piggybanks.index.repeated'; | ||||
|                 $message = 'Repeated expense'; | ||||
|             } else { | ||||
|                 $route   = 'piggybanks.index.piggybanks'; | ||||
|                 $message = 'Piggy bank'; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             Session::flash('success', $message . ' "' . $piggyBank->name . '" updated.'); | ||||
|             Event::fire('piggybanks.update', [$piggyBank]); | ||||
|  | ||||
|             return Redirect::route('piggybanks.index'); | ||||
|             return Redirect::route($route); | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not update piggy bank: ' . $piggyBank->errors()->first()); | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,8 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI; | ||||
|  | ||||
| /** | ||||
|  * Class PreferencesController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class PreferencesController extends BaseController | ||||
| { | ||||
| @@ -18,8 +20,10 @@ class PreferencesController extends BaseController | ||||
|     public function __construct(ARI $accounts, PHI $preferences) | ||||
|     { | ||||
|  | ||||
|         $this->_accounts = $accounts; | ||||
|         $this->_accounts    = $accounts; | ||||
|         $this->_preferences = $preferences; | ||||
|         View::share('title','Preferences'); | ||||
|         View::share('mainTitleIcon','fa-gear'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -29,7 +33,7 @@ class PreferencesController extends BaseController | ||||
|     { | ||||
|         $accounts = $this->_accounts->getDefault(); | ||||
|  | ||||
|         $viewRange = $this->_preferences->get('viewRange', '1M'); | ||||
|         $viewRange      = $this->_preferences->get('viewRange', '1M'); | ||||
|         $viewRangeValue = $viewRange->data; | ||||
|  | ||||
|         // pref: | ||||
|   | ||||
| @@ -22,6 +22,9 @@ class ProfileController extends BaseController | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         View::share('title', 'Profile'); | ||||
|         View::share('subTitle', Auth::user()->email); | ||||
|         View::share('mainTitleIcon', 'fa-user'); | ||||
|         return View::make('profile.index'); | ||||
|     } | ||||
|  | ||||
| @@ -30,6 +33,9 @@ class ProfileController extends BaseController | ||||
|      */ | ||||
|     public function changePassword() | ||||
|     { | ||||
|         View::share('title', Auth::user()->email); | ||||
|         View::share('subTitle', 'Change your password'); | ||||
|         View::share('mainTitleIcon', 'fa-user'); | ||||
|         return View::make('profile.change-password'); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,30 @@ | ||||
| <?php | ||||
|  | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface as RTR; | ||||
| use Firefly\Helper\Controllers\RecurringInterface as RI; | ||||
|  | ||||
| /** | ||||
|  * Class RecurringController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class RecurringController extends BaseController | ||||
| { | ||||
|     protected $_repository; | ||||
|     protected $_helper; | ||||
|  | ||||
|     /** | ||||
|      * @param RTR $repository | ||||
|      * @param RI $helper | ||||
|      */ | ||||
|     public function __construct(RTR $repository) | ||||
|     public function __construct(RTR $repository, RI $helper) | ||||
|     { | ||||
|         $this->_repository = $repository; | ||||
|         $this->_helper     = $helper; | ||||
|  | ||||
|         View::share('title', 'Recurring transactions'); | ||||
|         View::share('mainTitleIcon', 'fa-rotate-right'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -22,6 +32,7 @@ class RecurringController extends BaseController | ||||
|      */ | ||||
|     public function create() | ||||
|     { | ||||
|         View::share('subTitle', 'Create new'); | ||||
|         $periods = \Config::get('firefly.periods_to_text'); | ||||
|  | ||||
|         return View::make('recurring.create')->with('periods', $periods); | ||||
| @@ -34,6 +45,7 @@ class RecurringController extends BaseController | ||||
|      */ | ||||
|     public function delete(RecurringTransaction $recurringTransaction) | ||||
|     { | ||||
|         View::share('subTitle', 'Delete "' . $recurringTransaction->name . '"'); | ||||
|         return View::make('recurring.delete')->with('recurringTransaction', $recurringTransaction); | ||||
|     } | ||||
|  | ||||
| @@ -44,7 +56,7 @@ class RecurringController extends BaseController | ||||
|      */ | ||||
|     public function destroy(RecurringTransaction $recurringTransaction) | ||||
|     { | ||||
|         Event::fire('recurring.destroy', [$recurringTransaction]); | ||||
|         //Event::fire('recurring.destroy', [$recurringTransaction]); | ||||
|         $result = $this->_repository->destroy($recurringTransaction); | ||||
|         if ($result === true) { | ||||
|             Session::flash('success', 'The recurring transaction was deleted.'); | ||||
| @@ -65,6 +77,8 @@ class RecurringController extends BaseController | ||||
|     { | ||||
|         $periods = \Config::get('firefly.periods_to_text'); | ||||
|  | ||||
|         View::share('subTitle', 'Edit "' . $recurringTransaction->name . '"'); | ||||
|  | ||||
|         return View::make('recurring.edit')->with('periods', $periods)->with( | ||||
|             'recurringTransaction', $recurringTransaction | ||||
|         ); | ||||
| @@ -75,9 +89,7 @@ class RecurringController extends BaseController | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         $list = $this->_repository->get(); | ||||
|  | ||||
|         return View::make('recurring.index')->with('list', $list); | ||||
|         return View::make('recurring.index'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -85,53 +97,142 @@ class RecurringController extends BaseController | ||||
|      */ | ||||
|     public function show(RecurringTransaction $recurringTransaction) | ||||
|     { | ||||
|         View::share('subTitle', $recurringTransaction->name); | ||||
|         return View::make('recurring.show')->with('recurring', $recurringTransaction); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse | ||||
|      * @param RecurringTransaction $recurringTransaction | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function rescan(RecurringTransaction $recurringTransaction) | ||||
|     { | ||||
|         if (intval($recurringTransaction->active) == 0) { | ||||
|             Session::flash('warning', 'Inactive recurring transactions cannot be scanned.'); | ||||
|             return Redirect::back(); | ||||
|         } | ||||
|         // do something! | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $repo */ | ||||
|         $repo = App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|         $set  = $repo->get(); | ||||
|  | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         foreach ($set as $journal) { | ||||
|             Event::fire('recurring.rescan', [$recurringTransaction, $journal]); | ||||
|         } | ||||
|         Session::flash('success', 'Rescanned everything.'); | ||||
|         return Redirect::back(); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public function store() | ||||
|     { | ||||
|         $recurringTransaction = $this->_repository->store(Input::all()); | ||||
|         if ($recurringTransaction->validate()) { | ||||
|             Session::flash('success', 'Recurring transaction "' . $recurringTransaction->name . '" saved!'); | ||||
|             Event::fire('recurring.store', [$recurringTransaction]); | ||||
|             if (Input::get('create') == '1') { | ||||
|                 return Redirect::route('recurring.create')->withInput(); | ||||
|             } else { | ||||
|                 return Redirect::route('recurring.index'); | ||||
|             } | ||||
|         } else { | ||||
|             Session::flash( | ||||
|                 'error', 'Could not save the recurring transaction: ' . $recurringTransaction->errors()->first() | ||||
|             ); | ||||
|         $data = Input::except(['_token', 'post_submit_action']); | ||||
|         switch (Input::get('post_submit_action')) { | ||||
|             default: | ||||
|                 throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); | ||||
|                 break; | ||||
|             case 'store': | ||||
|             case 'create_another': | ||||
|                 /* | ||||
|                  * Try to store: | ||||
|                  */ | ||||
|                 $messageBag = $this->_repository->store($data); | ||||
|  | ||||
|             return Redirect::route('recurring.create')->withInput()->withErrors($recurringTransaction->errors()); | ||||
|                 /* | ||||
|                  * Failure! | ||||
|                  */ | ||||
|                 if ($messageBag->count() > 0) { | ||||
|                     Session::flash('error', 'Could not save recurring transaction: ' . $messageBag->first()); | ||||
|                     return Redirect::route('recurring.create')->withInput()->withErrors($messageBag); | ||||
|                 } | ||||
|  | ||||
|                 /* | ||||
|                  * Success! | ||||
|                  */ | ||||
|                 Session::flash('success', 'Recurring transaction "' . e(Input::get('name')) . '" saved!'); | ||||
|  | ||||
|                 /* | ||||
|                  * Redirect to original location or back to the form. | ||||
|                  */ | ||||
|                 if (Input::get('post_submit_action') == 'create_another') { | ||||
|                     return Redirect::route('recurring.create')->withInput(); | ||||
|                 } else { | ||||
|                     return Redirect::route('recurring.index'); | ||||
|                 } | ||||
|                 break; | ||||
|             case 'validate_only': | ||||
|                 $messageBags = $this->_helper->validate($data); | ||||
|  | ||||
|                 Session::flash('warnings', $messageBags['warnings']); | ||||
|                 Session::flash('successes', $messageBags['successes']); | ||||
|                 Session::flash('errors', $messageBags['errors']); | ||||
|                 return Redirect::route('recurring.create')->withInput(); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param RecurringTransaction $recurringTransaction | ||||
|      */ | ||||
|     public function update(RecurringTransaction $recurringTransaction) | ||||
|     { | ||||
|         /** @var \RecurringTransaction $recurringTransaction */ | ||||
|         $recurringTransaction = $this->_repository->update($recurringTransaction, Input::all()); | ||||
|         if ($recurringTransaction->errors()->count() == 0) { | ||||
|             Session::flash('success', 'The recurring transaction has been updated.'); | ||||
|             Event::fire('recurring.update', [$recurringTransaction]); | ||||
|         $data = Input::except(['_token', 'post_submit_action']); | ||||
|         switch (Input::get('post_submit_action')) { | ||||
|             case 'update': | ||||
|             case 'return_to_edit': | ||||
|                 $messageBag = $this->_repository->update($recurringTransaction, $data); | ||||
|                 if ($messageBag->count() == 0) { | ||||
|                     // has been saved, return to index: | ||||
|                     Session::flash('success', 'Recurring transaction updated!'); | ||||
|  | ||||
|             return Redirect::route('recurring.index'); | ||||
|         } else { | ||||
|             Session::flash( | ||||
|                 'error', 'Could not update the recurring transaction: ' . $recurringTransaction->errors()->first() | ||||
|             ); | ||||
|                     if (Input::get('post_submit_action') == 'return_to_edit') { | ||||
|                         return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput(); | ||||
|                     } else { | ||||
|                         return Redirect::route('recurring.index'); | ||||
|                     } | ||||
|                 } else { | ||||
|                     Session::flash('error', 'Could not update recurring transaction: ' . $messageBag->first()); | ||||
|  | ||||
|             return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput()->withErrors( | ||||
|                 $recurringTransaction->errors() | ||||
|             ); | ||||
|                     return Redirect::route('transactions.edit', $recurringTransaction->id)->withInput() | ||||
|                                    ->withErrors($messageBag); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 break; | ||||
|             case 'validate_only': | ||||
|                 $data        = Input::all(); | ||||
|                 $data['id']  = $recurringTransaction->id; | ||||
|                 $messageBags = $this->_helper->validate($data); | ||||
|  | ||||
|                 Session::flash('warnings', $messageBags['warnings']); | ||||
|                 Session::flash('successes', $messageBags['successes']); | ||||
|                 Session::flash('errors', $messageBags['errors']); | ||||
|                 return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput(); | ||||
|  | ||||
|                 break; | ||||
|             // update | ||||
|             default: | ||||
|                 throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|  | ||||
| //        /** @var \RecurringTransaction $recurringTransaction */ | ||||
| //        $recurringTransaction = $this->_repository->update($recurringTransaction, Input::all()); | ||||
| //        if ($recurringTransaction->errors()->count() == 0) { | ||||
| //            Session::flash('success', 'The recurring transaction has been updated.'); | ||||
| //            //Event::fire('recurring.update', [$recurringTransaction]); | ||||
| // | ||||
| //            return Redirect::route('recurring.index'); | ||||
| //        } else { | ||||
| //            Session::flash( | ||||
| //                'error', 'Could not update the recurring transaction: ' . $recurringTransaction->errors()->first() | ||||
| //            ); | ||||
| // | ||||
| //            return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput()->withErrors( | ||||
| //                $recurringTransaction->errors() | ||||
| //            ); | ||||
| //        } | ||||
|     } | ||||
| } | ||||
| @@ -1,89 +1,10 @@ | ||||
| <?php | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI; | ||||
|  | ||||
| /** | ||||
|  * Class ReminderController | ||||
|  * | ||||
|  */ | ||||
| class ReminderController extends BaseController | ||||
| { | ||||
|  | ||||
|     protected $_repository; | ||||
|  | ||||
|     public function __construct(RRI $repository) | ||||
|     { | ||||
|         $this->_repository = $repository; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Reminder $reminder | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function dismiss(\Reminder $reminder) | ||||
|     { | ||||
|         $reminder = $this->_repository->deactivate($reminder); | ||||
|  | ||||
|         return Response::json($reminder->id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the reminders currently active for the modal dialog. | ||||
|      */ | ||||
|     public function modalDialog() | ||||
|     { | ||||
|         $today = new Carbon; | ||||
|         $reminders = $this->_repository->get(); | ||||
|  | ||||
|         /** @var \Reminder $reminder */ | ||||
|         foreach ($reminders as $index => $reminder) { | ||||
|             if (\Session::has('dismissal-' . $reminder->id)) { | ||||
|                 $time = \Session::get('dismissal-' . $reminder->id); | ||||
|                 if ($time >= $today) { | ||||
|                     unset($reminders[$index]); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return View::make('reminders.popup')->with('reminders', $reminders); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Reminder $reminder | ||||
|      * | ||||
|      * @return \Illuminate\Http\JsonResponse | ||||
|      */ | ||||
|     public function postpone(\Reminder $reminder) | ||||
|     { | ||||
|         $now = new Carbon; | ||||
|         $now->addDay(); | ||||
|         Session::put('dismissal-' . $reminder->id, $now); | ||||
|  | ||||
|         return Response::json($reminder->id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Reminder $reminder | ||||
|      */ | ||||
|     public function redirect(\Reminder $reminder) | ||||
|     { | ||||
|         if ($reminder instanceof PiggybankReminder) { | ||||
|             // fields to prefill: | ||||
|             $parameters = [ | ||||
|                 'account_to_id' => $reminder->piggybank->account->id, | ||||
|                 'amount'        => round($reminder->amountToSave(), 2), | ||||
|                 'description'   => 'Money for ' . $reminder->piggybank->name, | ||||
|                 'piggybank_id'  => $reminder->piggybank->id, | ||||
|                 'reminder_id'   => $reminder->id | ||||
|             ]; | ||||
|  | ||||
|             return Redirect::to( | ||||
|                 route('transactions.create', ['what' => 'transfer']) . '?' . http_build_query($parameters) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| }  | ||||
| } | ||||
| @@ -12,7 +12,7 @@ class ReportController extends BaseController | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|  | ||||
|         return View::make('reports.index')->with('title','Reports')->with('mainTitleIcon','fa-line-chart'); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,16 +1,50 @@ | ||||
| <?php | ||||
|  | ||||
| use Firefly\Helper\Controllers\SearchInterface as SI; | ||||
|  | ||||
| /** | ||||
|  * Class SearchController | ||||
|  */ | ||||
| class SearchController extends BaseController | ||||
| { | ||||
|     protected $_helper; | ||||
|  | ||||
|     public function __construct(SI $helper) | ||||
|     { | ||||
|         $this->_helper = $helper; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * Results always come in the form of an array [results, count, fullCount] | ||||
|      */ | ||||
|     public function index() | ||||
|     { | ||||
|         $subTitle = null; | ||||
|         $rawQuery = null; | ||||
|         $result = []; | ||||
|         if (!is_null(Input::get('q'))) { | ||||
|             $rawQuery = trim(Input::get('q')); | ||||
|             $words    = explode(' ', $rawQuery); | ||||
|             $subTitle = 'Results for "' . e($rawQuery) . '"'; | ||||
|  | ||||
|             $transactions = $this->_helper->searchTransactions($words); | ||||
|             $accounts     = $this->_helper->searchAccounts($words); | ||||
|             $categories   = $this->_helper->searchCategories($words); | ||||
|             $budgets      = $this->_helper->searchBudgets($words); | ||||
|             $tags         = $this->_helper->searchTags($words); | ||||
|             $result = [ | ||||
|                 'transactions' => $transactions, | ||||
|                 'accounts' => $accounts, | ||||
|                 'categories' => $categories, | ||||
|                 'budgets' => $budgets, | ||||
|                 'tags' => $tags | ||||
|             ]; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return View::make('search.index')->with('title', 'Search')->with('subTitle', $subTitle)->with( | ||||
|             'mainTitleIcon', 'fa-search' | ||||
|         )->with('query', $rawQuery)->with('result',$result); | ||||
|     } | ||||
| } | ||||
| @@ -1,61 +1,103 @@ | ||||
| <?php | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Firefly\Helper\Controllers\TransactionInterface as TI; | ||||
| use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Class TransactionController | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  * | ||||
|  */ | ||||
| class TransactionController extends BaseController | ||||
| { | ||||
|  | ||||
|     protected $_helper; | ||||
|     protected $_repository; | ||||
|  | ||||
|     /** | ||||
|      * Construct a new transaction controller with two of the most often used helpers. | ||||
|      * | ||||
|      * @param TJRI $repository | ||||
|      * @param TI   $helper | ||||
|      */ | ||||
|     public function __construct(TJRI $repository) | ||||
|     public function __construct(TJRI $repository, TI $helper) | ||||
|     { | ||||
|         $this->_repository = $repository; | ||||
|         $this->_helper = $helper; | ||||
|         View::share('title', 'Transactions'); | ||||
|         View::share('mainTitleIcon', 'fa-repeat'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shows the view helping the user to create a new transaction journal. | ||||
|      * | ||||
|      * @param string $what | ||||
|      * | ||||
|      * @return \Illuminate\View\View | ||||
|      */ | ||||
|     public function create($what = 'deposit') | ||||
|     { | ||||
|         // get accounts with names and id's. | ||||
|         /* | ||||
|          * The repositories we need: | ||||
|          */ | ||||
|         /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); | ||||
|  | ||||
|         /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */ | ||||
|         $accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|         $accounts = $accountRepository->getActiveDefaultAsSelectList(); | ||||
|  | ||||
|         // get budgets as a select list. | ||||
|         /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */ | ||||
|         $budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); | ||||
|         $budgets = $budgetRepository->getAsSelectList(); | ||||
|  | ||||
|         /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ | ||||
|         $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); | ||||
|  | ||||
|         // get asset accounts with names and id's. | ||||
|         $assetAccounts = $toolkit->makeSelectList($accountRepository->getActiveDefault()); | ||||
|  | ||||
|         // get budgets as a select list. | ||||
|         $budgets = $toolkit->makeSelectList($budgetRepository->get()); | ||||
|         $budgets[0] = '(no budget)'; | ||||
|  | ||||
|         // get the piggy banks. | ||||
|         /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ | ||||
|         $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); | ||||
|         $piggies = $piggyRepository->get(); | ||||
|         $piggies = $toolkit->makeSelectList($piggyRepository->get()); | ||||
|         $piggies[0] = '(no piggy bank)'; | ||||
|  | ||||
|         return View::make('transactions.create')->with('accounts', $accounts)->with('budgets', $budgets)->with( | ||||
|         /* | ||||
|          * respond to a possible given values in the URL. | ||||
|          */ | ||||
|         $prefilled = Session::has('prefilled') ? Session::get('prefilled') : []; | ||||
|         $respondTo = ['account_id', 'account_from_id']; | ||||
|         foreach ($respondTo as $r) { | ||||
|             if (!is_null(Input::get($r))) { | ||||
|                 $prefilled[$r] = Input::get($r); | ||||
|             } | ||||
|         } | ||||
|         Session::put('prefilled', $prefilled); | ||||
|  | ||||
|         return View::make('transactions.create')->with('accounts', $assetAccounts)->with('budgets', $budgets)->with( | ||||
|             'what', $what | ||||
|         )->with('piggies', $piggies); | ||||
|         )->with('piggies', $piggies)->with('subTitle', 'Add a new ' . $what); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shows the form that allows a user to delete a transaction journal. | ||||
|      * | ||||
|      * @param TransactionJournal $transactionJournal | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function delete(TransactionJournal $transactionJournal) | ||||
|     { | ||||
|         return View::make('transactions.delete')->with('journal', $transactionJournal); | ||||
|         $type = strtolower($transactionJournal->transactionType->type); | ||||
|  | ||||
|         return View::make('transactions.delete')->with('journal', $transactionJournal)->with( | ||||
|             'subTitle', 'Delete ' . $type . ' "' . $transactionJournal->description . '"' | ||||
|         ); | ||||
|  | ||||
|  | ||||
|     } | ||||
| @@ -68,103 +110,142 @@ class TransactionController extends BaseController | ||||
|      */ | ||||
|     public function destroy(TransactionJournal $transactionJournal) | ||||
|     { | ||||
|         $type = $transactionJournal->transactionType->type; | ||||
|         $transactionJournal->delete(); | ||||
|  | ||||
|         return Redirect::route('transactions.index'); | ||||
|  | ||||
|         switch ($type) { | ||||
|             case 'Withdrawal': | ||||
|                 return Redirect::route('transactions.expenses'); | ||||
|                 break; | ||||
|             case 'Deposit': | ||||
|                 return Redirect::route('transactions.revenue'); | ||||
|                 break; | ||||
|             case 'Transfer': | ||||
|                 return Redirect::route('transactions.transfers'); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shows the view to edit a transaction. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function edit(TransactionJournal $journal) | ||||
|     { | ||||
|         /* | ||||
|          * All the repositories we need: | ||||
|          */ | ||||
|         /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ | ||||
|         $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); | ||||
|  | ||||
|         /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */ | ||||
|         $accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */ | ||||
|         $budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ | ||||
|         $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); | ||||
|  | ||||
|         // type is useful for display: | ||||
|         $what = strtolower($journal->transactiontype->type); | ||||
|  | ||||
|         // some lists prefilled: | ||||
|         // get accounts with names and id's. | ||||
|         /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */ | ||||
|         $accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|         $accounts = $accountRepository->getActiveDefaultAsSelectList(); | ||||
|         // get asset accounts with names and id's. | ||||
|         $accounts = $toolkit->makeSelectList($accountRepository->getActiveDefault()); | ||||
|  | ||||
|         // get budgets as a select list. | ||||
|         /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */ | ||||
|         $budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); | ||||
|         $budgets = $budgetRepository->getAsSelectList(); | ||||
|         $budgets = $toolkit->makeSelectList($budgetRepository->get()); | ||||
|         $budgets[0] = '(no budget)'; | ||||
|  | ||||
|         /* | ||||
|          * Get all piggy banks plus (if any) the relevant piggy bank. Since just one | ||||
|          * of the transactions in the journal has this field, it should all fill in nicely. | ||||
|          */ | ||||
|         // get the piggy banks. | ||||
|         /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ | ||||
|         $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); | ||||
|         $piggies = $piggyRepository->get(); | ||||
|         // piggy bank id? | ||||
|         $piggyBankId = null; | ||||
|         $piggies = $toolkit->makeSelectList($piggyRepository->get()); | ||||
|         $piggies[0] = '(no piggy bank)'; | ||||
|         $piggyBankId = 0; | ||||
|         foreach ($journal->transactions as $t) { | ||||
|             $piggyBankId = $t->piggybank_id; | ||||
|             if (!is_null($t->piggybank_id)) { | ||||
|                 $piggyBankId = $t->piggybank_id; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // data to properly display form: | ||||
|         $data = [ | ||||
|         /* | ||||
|          * Data to properly display the edit form. | ||||
|          */ | ||||
|         $prefilled = [ | ||||
|             'date'         => $journal->date->format('Y-m-d'), | ||||
|             'category'     => '', | ||||
|             'budget_id'    => 0, | ||||
|             'piggybank_id' => $piggyBankId | ||||
|         ]; | ||||
|  | ||||
|         /* | ||||
|          * Fill in the category. | ||||
|          */ | ||||
|         $category = $journal->categories()->first(); | ||||
|         if (!is_null($category)) { | ||||
|             $data['category'] = $category->name; | ||||
|             $prefilled['category'] = $category->name; | ||||
|         } | ||||
|         switch ($journal->transactiontype->type) { | ||||
|             case 'Withdrawal': | ||||
|                 $data['account_id'] = $journal->transactions[0]->account->id; | ||||
|                 $data['beneficiary'] = $journal->transactions[1]->account->name; | ||||
|                 $data['amount'] = floatval($journal->transactions[1]->amount); | ||||
|  | ||||
|         /* | ||||
|          * Switch on the type of transaction edited by the user and fill in other | ||||
|          * relevant fields: | ||||
|          */ | ||||
|         switch ($what) { | ||||
|             case 'withdrawal': | ||||
|                 $prefilled['account_id'] = $journal->transactions[0]->account->id; | ||||
|                 $prefilled['expense_account'] = $journal->transactions[1]->account->name; | ||||
|                 $prefilled['amount'] = floatval($journal->transactions[1]->amount); | ||||
|                 $budget = $journal->budgets()->first(); | ||||
|                 if (!is_null($budget)) { | ||||
|                     $data['budget_id'] = $budget->id; | ||||
|                     $prefilled['budget_id'] = $budget->id; | ||||
|                 } | ||||
|                 break; | ||||
|             case 'Deposit': | ||||
|                 $data['account_id'] = $journal->transactions[1]->account->id; | ||||
|                 $data['beneficiary'] = $journal->transactions[0]->account->name; | ||||
|                 $data['amount'] = floatval($journal->transactions[1]->amount); | ||||
|             case 'deposit': | ||||
|                 $prefilled['account_id'] = $journal->transactions[1]->account->id; | ||||
|                 $prefilled['revenue_account'] = $journal->transactions[0]->account->name; | ||||
|                 $prefilled['amount'] = floatval($journal->transactions[1]->amount); | ||||
|                 break; | ||||
|             case 'Transfer': | ||||
|                 $data['account_from_id'] = $journal->transactions[1]->account->id; | ||||
|                 $data['account_to_id'] = $journal->transactions[0]->account->id; | ||||
|                 $data['amount'] = floatval($journal->transactions[1]->amount); | ||||
|             case 'transfer': | ||||
|                 $prefilled['account_from_id'] = $journal->transactions[1]->account->id; | ||||
|                 $prefilled['account_to_id'] = $journal->transactions[0]->account->id; | ||||
|                 $prefilled['amount'] = floatval($journal->transactions[1]->amount); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Show the view. | ||||
|          */ | ||||
|         return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts)->with( | ||||
|             'what', $what | ||||
|         )->with('budgets', $budgets)->with('data', $data)->with('piggies', $piggies); | ||||
|         )->with('budgets', $budgets)->with('data', $prefilled)->with('piggies', $piggies)->with( | ||||
|             'subTitle', 'Edit ' . $what . ' "' . $journal->description . '"' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return $this|\Illuminate\View\View | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function index() | ||||
|     public function expenses() | ||||
|     { | ||||
|         $start = is_null(Input::get('startdate')) ? null : new Carbon(Input::get('startdate')); | ||||
|         $end = is_null(Input::get('enddate')) ? null : new Carbon(Input::get('enddate')); | ||||
|         if ($start <= $end && !is_null($start) && !is_null($end)) { | ||||
|             $journals = $this->_repository->paginate(25, $start, $end); | ||||
|             $filtered = true; | ||||
|             $filters = ['start' => $start, 'end' => $end]; | ||||
|         } else { | ||||
|             $journals = $this->_repository->paginate(25); | ||||
|             $filtered = false; | ||||
|             $filters = null; | ||||
|         } | ||||
|         return View::make('transactions.list')->with('subTitle', 'Expenses')->with( | ||||
|             'subTitleIcon', 'fa-long-arrow-left' | ||||
|         )->with('what', 'expenses'); | ||||
|     } | ||||
|  | ||||
|  | ||||
|         return View::make('transactions.index')->with('journals', $journals)->with('filtered', $filtered)->with( | ||||
|             'filters', $filters | ||||
|         ); | ||||
|     /** | ||||
|      * @return $this | ||||
|      */ | ||||
|     public function revenue() | ||||
|     { | ||||
|         return View::make('transactions.list')->with('subTitle', 'Revenue')->with( | ||||
|             'subTitleIcon', 'fa-long-arrow-right' | ||||
|         )->with('what', 'revenue'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -174,63 +255,122 @@ class TransactionController extends BaseController | ||||
|      */ | ||||
|     public function show(TransactionJournal $journal) | ||||
|     { | ||||
|         return View::make('transactions.show')->with('journal', $journal); | ||||
|         return View::make('transactions.show')->with('journal', $journal)->with( | ||||
|             'subTitle', $journal->transactionType->type . ' "' . $journal->description . '"' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $what | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function store($what) | ||||
|     { | ||||
|         $journal = $this->_repository->store($what, Input::all()); | ||||
|         if ($journal->validate()) { | ||||
|             Session::flash('success', 'Transaction "' . $journal->description . '" saved!'); | ||||
|         /* | ||||
|          * Collect data to process: | ||||
|          */ | ||||
|         $data = Input::except(['_token']); | ||||
|         $data['what'] = $what; | ||||
|  | ||||
|             // if reminder present, deactivate it: | ||||
|             if (Input::get('reminder')) { | ||||
|                 /** @var \Firefly\Storage\Reminder\ReminderRepositoryInterface $reminders */ | ||||
|                 $reminders = App::make('Firefly\Storage\Reminder\ReminderRepositoryInterface'); | ||||
|                 $reminder = $reminders->find(Input::get('reminder')); | ||||
|                 $reminders->deactivate($reminder); | ||||
|             } | ||||
|         switch (Input::get('post_submit_action')) { | ||||
|             case 'store': | ||||
|             case 'create_another': | ||||
|                 /* | ||||
|                  * Try to store: | ||||
|                  */ | ||||
|                 $messageBag = $this->_helper->store($data); | ||||
|  | ||||
|             // trigger the creation for recurring transactions. | ||||
|                 /* | ||||
|                  * Failure! | ||||
|                  */ | ||||
|                 if ($messageBag->count() > 0) { | ||||
|                     Session::flash('error', 'Could not save transaction: ' . $messageBag->first()); | ||||
|                     return Redirect::route('transactions.create', [$what])->withInput()->withErrors($messageBag); | ||||
|                 } | ||||
|  | ||||
|             if (Input::get('create') == '1') { | ||||
|                 /* | ||||
|                  * Success! | ||||
|                  */ | ||||
|                 Session::flash('success', 'Transaction "' . e(Input::get('description')) . '" saved!'); | ||||
|  | ||||
|                 /* | ||||
|                  * Redirect to original location or back to the form. | ||||
|                  */ | ||||
|                 if (Input::get('post_submit_action') == 'create_another') { | ||||
|                     return Redirect::route('transactions.create', $what)->withInput(); | ||||
|                 } else { | ||||
|                     return Redirect::route('transactions.index.' . $what); | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|             case 'validate_only': | ||||
|                 $messageBags = $this->_helper->validate($data); | ||||
|  | ||||
|                 Session::flash('warnings', $messageBags['warnings']); | ||||
|                 Session::flash('successes', $messageBags['successes']); | ||||
|                 Session::flash('errors', $messageBags['errors']); | ||||
|                 return Redirect::route('transactions.create', [$what])->withInput(); | ||||
|             } else { | ||||
|                 return Redirect::route('transactions.index'); | ||||
|             } | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not save transaction: ' . $journal->errors()->first()); | ||||
|  | ||||
|             return Redirect::route('transactions.create', [$what])->withInput()->withErrors( | ||||
|                 $journal->errors() | ||||
|             ); | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function transfers() | ||||
|     { | ||||
|         return View::make('transactions.list')->with('subTitle', 'Transfers')->with( | ||||
|             'subTitleIcon', 'fa-arrows-h' | ||||
|         )->with('what', 'transfers'); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      * | ||||
|      * @return $this|\Illuminate\Http\RedirectResponse | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function update(TransactionJournal $journal) | ||||
|     { | ||||
|         $journal = $this->_repository->update($journal, Input::all()); | ||||
|         if ($journal->validate()) { | ||||
|             // has been saved, return to index: | ||||
|             Session::flash('success', 'Transaction updated!'); | ||||
|         switch (Input::get('post_submit_action')) { | ||||
|             case 'update': | ||||
|             case 'return_to_edit': | ||||
|                 $what = strtolower($journal->transactionType->type); | ||||
|                 $messageBag = $this->_helper->update($journal, Input::all()); | ||||
|                 if ($messageBag->count() == 0) { | ||||
|                     // has been saved, return to index: | ||||
|                     Session::flash('success', 'Transaction updated!'); | ||||
|                     Event::fire('journals.update', [$journal]); | ||||
|  | ||||
|             return Redirect::route('transactions.index'); | ||||
|         } else { | ||||
|             Session::flash('error', 'Could not update transaction: ' . $journal->errors()->first()); | ||||
|                     if (Input::get('post_submit_action') == 'return_to_edit') { | ||||
|                         return Redirect::route('transactions.edit', $journal->id)->withInput(); | ||||
|                     } else { | ||||
|                         return Redirect::route('transactions.index.' . $what); | ||||
|                     } | ||||
|                 } else { | ||||
|                     Session::flash('error', 'Could not update transaction: ' . $journal->errors()->first()); | ||||
|  | ||||
|             return Redirect::route('transactions.edit', $journal->id)->withInput()->withErrors($journal->errors()); | ||||
|                     return Redirect::route('transactions.edit', $journal->id)->withInput()->withErrors( | ||||
|                         $journal->errors() | ||||
|                     ); | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|             case 'validate_only': | ||||
|                 $data = Input::all(); | ||||
|                 $data['what'] = strtolower($journal->transactionType->type); | ||||
|                 $messageBags = $this->_helper->validate($data); | ||||
|  | ||||
|                 Session::flash('warnings', $messageBags['warnings']); | ||||
|                 Session::flash('successes', $messageBags['successes']); | ||||
|                 Session::flash('errors', $messageBags['errors']); | ||||
|                 return Redirect::route('transactions.edit', $journal->id)->withInput(); | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,7 @@ class UserController extends BaseController | ||||
|      */ | ||||
|     public function __construct(URI $user, EHI $email) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|         $this->user  = $user; | ||||
|         $this->email = $email; | ||||
|  | ||||
|     } | ||||
| @@ -41,14 +41,12 @@ class UserController extends BaseController | ||||
|     public function postLogin() | ||||
|     { | ||||
|         $rememberMe = Input::get('remember_me') == '1'; | ||||
|         $data = [ | ||||
|         $data       = [ | ||||
|             'email'    => Input::get('email'), | ||||
|             'password' => Input::get('password') | ||||
|         ]; | ||||
|         $result = Auth::attempt($data, $rememberMe); | ||||
|         $result     = Auth::attempt($data, $rememberMe); | ||||
|         if ($result) { | ||||
|             Session::flash('success', 'Logged in!'); | ||||
|  | ||||
|             return Redirect::route('index'); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -11,22 +11,6 @@ use Illuminate\Database\Schema\Blueprint; | ||||
| class CreateAccountTypesTable extends Migration | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Run the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function up() | ||||
|     { | ||||
|         Schema::create( | ||||
|             'account_types', function (Blueprint $table) { | ||||
|                 $table->increments('id'); | ||||
|                 $table->timestamps(); | ||||
|                 $table->string('description', 50); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reverse the migrations. | ||||
|      * | ||||
| @@ -37,4 +21,23 @@ class CreateAccountTypesTable extends Migration | ||||
|         Schema::drop('account_types'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function up() | ||||
|     { | ||||
|         Schema::create( | ||||
|               'account_types', function (Blueprint $table) { | ||||
|                       $table->increments('id'); | ||||
|                       $table->timestamps(); | ||||
|                       $table->string('type', 50); | ||||
|                       $table->boolean('editable'); | ||||
|  | ||||
|                       $table->unique('type'); | ||||
|                   } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -25,8 +25,8 @@ class CreateRecurringTransactionsTable extends Migration | ||||
|                 $table->integer('user_id')->unsigned(); | ||||
|                 $table->string('name', 50); | ||||
|                 $table->string('match', 255); | ||||
|                 $table->decimal('amount_max', 10, 2); | ||||
|                 $table->decimal('amount_min', 10, 2); | ||||
|                 $table->decimal('amount_max', 10, 2); | ||||
|                 $table->date('date'); | ||||
|                 $table->boolean('active'); | ||||
| 
 | ||||
| @@ -24,6 +24,7 @@ class CreateTransactionJournalsTable extends Migration | ||||
|                 $table->timestamps(); | ||||
|                 $table->integer('user_id')->unsigned(); | ||||
|                 $table->integer('transaction_type_id')->unsigned(); | ||||
|                 $table->integer('recurring_transaction_id')->unsigned()->nullable(); | ||||
|                 $table->integer('transaction_currency_id')->unsigned(); | ||||
|                 $table->string('description', 255)->nullable(); | ||||
|                 $table->boolean('completed'); | ||||
| @@ -34,6 +35,11 @@ class CreateTransactionJournalsTable extends Migration | ||||
|                     ->references('id')->on('transaction_types') | ||||
|                     ->onDelete('cascade'); | ||||
|  | ||||
|                 // connect transaction journals to recurring transactions | ||||
|                 $table->foreign('recurring_transaction_id') | ||||
|                     ->references('id')->on('recurring_transactions') | ||||
|                     ->onDelete('set null'); | ||||
|  | ||||
|                 // connect transaction journals to transaction currencies | ||||
|                 $table->foreign('transaction_currency_id') | ||||
|                     ->references('id')->on('transaction_currencies') | ||||
|   | ||||
| @@ -27,26 +27,11 @@ class CreateRemindersTable extends Migration | ||||
|             'reminders', function (Blueprint $table) { | ||||
|                 $table->increments('id'); | ||||
|                 $table->timestamps(); | ||||
|                 $table->string('class', 40); | ||||
|                 $table->integer('piggybank_id')->unsigned()->nullable(); | ||||
|                 $table->integer('recurring_transaction_id')->unsigned()->nullable(); | ||||
|                 $table->integer('user_id')->unsigned(); | ||||
|                 $table->date('startdate'); | ||||
|                 $table->date('enddate'); | ||||
|                 $table->date('enddate')->nullable(); | ||||
|                 $table->boolean('active'); | ||||
|  | ||||
|  | ||||
|                 // connect reminders to piggy banks. | ||||
|                 $table->foreign('piggybank_id') | ||||
|                     ->references('id')->on('piggybanks') | ||||
|                     ->onDelete('set null'); | ||||
|  | ||||
|                 // connect reminders to recurring transactions. | ||||
|                 $table->foreign('recurring_transaction_id') | ||||
|                     ->references('id')->on('recurring_transactions') | ||||
|                     ->onDelete('set null'); | ||||
|  | ||||
|  | ||||
|                 // connect reminders to users | ||||
|                 $table->foreign('user_id') | ||||
|                     ->references('id')->on('users') | ||||
|   | ||||
| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
|  | ||||
| class CreateImportmapsTable extends Migration { | ||||
|  | ||||
| 	/** | ||||
| 	 * Run the migrations. | ||||
| 	 * | ||||
| 	 * @return void | ||||
| 	 */ | ||||
| 	public function up() | ||||
| 	{ | ||||
| 		Schema::create('importmaps', function(Blueprint $table) | ||||
| 		{ | ||||
| 			$table->increments('id'); | ||||
| 			$table->timestamps(); | ||||
|             $table->integer('user_id')->unsigned(); | ||||
|             $table->string('file',500); | ||||
|             $table->integer('totaljobs')->unsigned(); | ||||
|             $table->integer('jobsdone')->unsigned(); | ||||
|  | ||||
|             // connect maps to users | ||||
|             $table->foreign('user_id') | ||||
|                   ->references('id')->on('users') | ||||
|                   ->onDelete('cascade'); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Reverse the migrations. | ||||
| 	 * | ||||
| 	 * @return void | ||||
| 	 */ | ||||
| 	public function down() | ||||
| 	{ | ||||
| 		Schema::drop('importmaps'); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
|  | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
|  | ||||
| class CreateImportentriesTable extends Migration { | ||||
|  | ||||
| 	/** | ||||
| 	 * Run the migrations. | ||||
| 	 * | ||||
| 	 * @return void | ||||
| 	 */ | ||||
| 	public function up() | ||||
| 	{ | ||||
| 		Schema::create('importentries', function(Blueprint $table) | ||||
| 		{ | ||||
| 			$table->increments('id'); | ||||
| 			$table->timestamps(); | ||||
|             $table->string('class',200); | ||||
|             $table->integer('importmap_id')->unsigned(); | ||||
|             $table->integer('old')->unsigned(); | ||||
|             $table->integer('new')->unsigned(); | ||||
|  | ||||
|             // map import entries to import map. | ||||
|             // connect accounts to account_types | ||||
|             $table->foreign('importmap_id') | ||||
|                   ->references('id')->on('importmaps') | ||||
|                   ->onDelete('cascade'); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Reverse the migrations. | ||||
| 	 * | ||||
| 	 * @return void | ||||
| 	 */ | ||||
| 	public function down() | ||||
| 	{ | ||||
| 		Schema::drop('importentries'); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| <?php | ||||
|  | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
|  | ||||
| class CreateFailedJobsTable extends Migration | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Reverse the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function down() | ||||
|     { | ||||
|         Schema::drop('failed_jobs'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function up() | ||||
|     { | ||||
|         Schema::create('failed_jobs', function (Blueprint $table) { | ||||
|             $table->increments('id'); | ||||
|             $table->text('connection'); | ||||
|             $table->text('queue'); | ||||
|             $table->text('payload'); | ||||
|             $table->timestamp('failed_at'); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -11,16 +11,29 @@ class AccountTypeSeeder extends Seeder | ||||
|         DB::table('account_types')->delete(); | ||||
|  | ||||
|         AccountType::create( | ||||
|             ['description' => 'Default account'] | ||||
|             ['type' => 'Default account', 'editable' => true] | ||||
|         ); | ||||
|         AccountType::create( | ||||
|             ['description' => 'Cash account'] | ||||
|             ['type' => 'Cash account', 'editable' => false] | ||||
|         ); | ||||
|         AccountType::create( | ||||
|             ['description' => 'Initial balance account'] | ||||
|             ['type' => 'Asset account', 'editable' => true] | ||||
|         ); | ||||
|         AccountType::create( | ||||
|             ['description' => 'Beneficiary account'] | ||||
|             ['type' => 'Expense account', 'editable' => true] | ||||
|         ); | ||||
|         AccountType::create( | ||||
|             ['type' => 'Revenue account', 'editable' => true] | ||||
|         ); | ||||
|         AccountType::create( | ||||
|             ['type' => 'Initial balance account', 'editable' => false] | ||||
|         ); | ||||
|         AccountType::create( | ||||
|             ['type' => 'Beneficiary account', 'editable' => true] | ||||
|         ); | ||||
|  | ||||
|         AccountType::create( | ||||
|             ['type' => 'Import account', 'editable' => false] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -6,10 +6,11 @@ App::before( | ||||
|     function ($request) { | ||||
|  | ||||
|         if (Auth::check()) { | ||||
|             /** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */ | ||||
|             $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); | ||||
|             $toolkit->getDateRange($request); | ||||
|             $toolkit->getReminders(); | ||||
|  | ||||
|             $toolkit->getDateRange(); | ||||
|             $toolkit->checkImportJobs(); | ||||
|             Event::fire('recurring.verify'); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -1,104 +1,105 @@ | ||||
| <?php | ||||
|  | ||||
| return array( | ||||
| return [ | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| Validation Language Lines | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| The following language lines contain the default error messages used by | ||||
| 	| the validator class. Some of these rules have multiple versions such | ||||
| 	| as the size rules. Feel free to tweak each of these messages here. | ||||
| 	| | ||||
| 	*/ | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Validation Language Lines | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | The following language lines contain the default error messages used by | ||||
|     | the validator class. Some of these rules have multiple versions such | ||||
|     | as the size rules. Feel free to tweak each of these messages here. | ||||
|     | | ||||
|     */ | ||||
|  | ||||
| 	"accepted"             => "The :attribute must be accepted.", | ||||
| 	"active_url"           => "The :attribute is not a valid URL.", | ||||
| 	"after"                => "The :attribute must be a date after :date.", | ||||
| 	"alpha"                => "The :attribute may only contain letters.", | ||||
| 	"alpha_dash"           => "The :attribute may only contain letters, numbers, and dashes.", | ||||
| 	"alpha_num"            => "The :attribute may only contain letters and numbers.", | ||||
| 	"array"                => "The :attribute must be an array.", | ||||
| 	"before"               => "The :attribute must be a date before :date.", | ||||
| 	"between"              => array( | ||||
| 		"numeric" => "The :attribute must be between :min and :max.", | ||||
| 		"file"    => "The :attribute must be between :min and :max kilobytes.", | ||||
| 		"string"  => "The :attribute must be between :min and :max characters.", | ||||
| 		"array"   => "The :attribute must have between :min and :max items.", | ||||
| 	), | ||||
| 	"confirmed"            => "The :attribute confirmation does not match.", | ||||
| 	"date"                 => "The :attribute is not a valid date.", | ||||
| 	"date_format"          => "The :attribute does not match the format :format.", | ||||
| 	"different"            => "The :attribute and :other must be different.", | ||||
| 	"digits"               => "The :attribute must be :digits digits.", | ||||
| 	"digits_between"       => "The :attribute must be between :min and :max digits.", | ||||
| 	"email"                => "The :attribute must be a valid email address.", | ||||
| 	"exists"               => "The selected :attribute is invalid.", | ||||
| 	"image"                => "The :attribute must be an image.", | ||||
| 	"in"                   => "The selected :attribute is invalid.", | ||||
| 	"integer"              => "The :attribute must be an integer.", | ||||
| 	"ip"                   => "The :attribute must be a valid IP address.", | ||||
| 	"max"                  => array( | ||||
| 		"numeric" => "The :attribute may not be greater than :max.", | ||||
| 		"file"    => "The :attribute may not be greater than :max kilobytes.", | ||||
| 		"string"  => "The :attribute may not be greater than :max characters.", | ||||
| 		"array"   => "The :attribute may not have more than :max items.", | ||||
| 	), | ||||
| 	"mimes"                => "The :attribute must be a file of type: :values.", | ||||
| 	"min"                  => array( | ||||
| 		"numeric" => "The :attribute must be at least :min.", | ||||
| 		"file"    => "The :attribute must be at least :min kilobytes.", | ||||
| 		"string"  => "The :attribute must be at least :min characters.", | ||||
| 		"array"   => "The :attribute must have at least :min items.", | ||||
| 	), | ||||
| 	"not_in"               => "The selected :attribute is invalid.", | ||||
| 	"numeric"              => "The :attribute must be a number.", | ||||
| 	"regex"                => "The :attribute format is invalid.", | ||||
| 	"required"             => "The :attribute field is required.", | ||||
| 	"required_if"          => "The :attribute field is required when :other is :value.", | ||||
| 	"required_with"        => "The :attribute field is required when :values is present.", | ||||
| 	"required_with_all"    => "The :attribute field is required when :values is present.", | ||||
| 	"required_without"     => "The :attribute field is required when :values is not present.", | ||||
| 	"required_without_all" => "The :attribute field is required when none of :values are present.", | ||||
| 	"same"                 => "The :attribute and :other must match.", | ||||
| 	"size"                 => array( | ||||
| 		"numeric" => "The :attribute must be :size.", | ||||
| 		"file"    => "The :attribute must be :size kilobytes.", | ||||
| 		"string"  => "The :attribute must be :size characters.", | ||||
| 		"array"   => "The :attribute must contain :size items.", | ||||
| 	), | ||||
| 	"unique"               => "The :attribute has already been taken.", | ||||
| 	"url"                  => "The :attribute format is invalid.", | ||||
|     "accepted"             => "The :attribute must be accepted.", | ||||
|     "active_url"           => "The :attribute is not a valid URL.", | ||||
|     "after"                => "The :attribute must be a date after :date.", | ||||
|     "alpha"                => "The :attribute may only contain letters.", | ||||
|     "alpha_dash"           => "The :attribute may only contain letters, numbers, and dashes.", | ||||
|     "alpha_num"            => "The :attribute may only contain letters and numbers.", | ||||
|     "array"                => "The :attribute must be an array.", | ||||
|     "before"               => "The :attribute must be a date before :date.", | ||||
|     "between"              => array( | ||||
|         "numeric" => "The :attribute must be between :min and :max.", | ||||
|         "file"    => "The :attribute must be between :min and :max kilobytes.", | ||||
|         "string"  => "The :attribute must be between :min and :max characters.", | ||||
|         "array"   => "The :attribute must have between :min and :max items.", | ||||
|     ), | ||||
|     "confirmed"            => "The :attribute confirmation does not match.", | ||||
|     "date"                 => "The :attribute is not a valid date.", | ||||
|     "date_format"          => "The :attribute does not match the format :format.", | ||||
|     "different"            => "The :attribute and :other must be different.", | ||||
|     "digits"               => "The :attribute must be :digits digits.", | ||||
|     "digits_between"       => "The :attribute must be between :min and :max digits.", | ||||
|     "email"                => "The :attribute must be a valid email address.", | ||||
|     "exists"               => "The selected :attribute is invalid.", | ||||
|     "image"                => "The :attribute must be an image.", | ||||
|     "in"                   => "The selected :attribute is invalid.", | ||||
|     "integer"              => "The :attribute must be an integer.", | ||||
|     "ip"                   => "The :attribute must be a valid IP address.", | ||||
|     "max"                  => array( | ||||
|         "numeric" => "The :attribute may not be greater than :max.", | ||||
|         "file"    => "The :attribute may not be greater than :max kilobytes.", | ||||
|         "string"  => "The :attribute may not be greater than :max characters.", | ||||
|         "array"   => "The :attribute may not have more than :max items.", | ||||
|     ), | ||||
|     "mimes"                => "The :attribute must be a file of type: :values.", | ||||
|     "min"                  => array( | ||||
|         "numeric" => "The :attribute must be at least :min.", | ||||
|         "file"    => "The :attribute must be at least :min kilobytes.", | ||||
|         "string"  => "The :attribute must be at least :min characters.", | ||||
|         "array"   => "The :attribute must have at least :min items.", | ||||
|     ), | ||||
|     "not_in"               => "The selected :attribute is invalid.", | ||||
|     "numeric"              => "The :attribute must be a number.", | ||||
|     "regex"                => "The :attribute format is invalid.", | ||||
|     "required"             => "The :attribute field is required.", | ||||
|     "required_if"          => "The :attribute field is required when :other is :value.", | ||||
|     "required_with"        => "The :attribute field is required when :values is present.", | ||||
|     "required_with_all"    => "The :attribute field is required when :values is present.", | ||||
|     "required_without"     => "The :attribute field is required when :values is not present.", | ||||
|     "required_without_all" => "The :attribute field is required when none of :values are present.", | ||||
|     "same"                 => "The :attribute and :other must match.", | ||||
|     "size"                 => array( | ||||
|         "numeric" => "The :attribute must be :size.", | ||||
|         "file"    => "The :attribute must be :size kilobytes.", | ||||
|         "string"  => "The :attribute must be :size characters.", | ||||
|         "array"   => "The :attribute must contain :size items.", | ||||
|     ), | ||||
|     "unique"               => "The :attribute has already been taken.", | ||||
|     "url"                  => "The :attribute format is invalid.", | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| Custom Validation Language Lines | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| Here you may specify custom validation messages for attributes using the | ||||
| 	| convention "attribute.rule" to name the lines. This makes it quick to | ||||
| 	| specify a specific custom language line for a given attribute rule. | ||||
| 	| | ||||
| 	*/ | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Custom Validation Language Lines | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | Here you may specify custom validation messages for attributes using the | ||||
|     | convention "attribute.rule" to name the lines. This makes it quick to | ||||
|     | specify a specific custom language line for a given attribute rule. | ||||
|     | | ||||
|     */ | ||||
|  | ||||
| 	'custom' => array( | ||||
| 		'attribute-name' => array( | ||||
| 			'rule-name' => 'custom-message', | ||||
| 		), | ||||
| 	), | ||||
|     'custom'               => array( | ||||
|         'attribute-name' => array( | ||||
|             'rule-name' => 'custom-message', | ||||
|         ), | ||||
|     ), | ||||
|     'alphabasic'                  => 'The :attribute field must consist of basic alphanumeric characters.', | ||||
|  | ||||
| 	/* | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| Custom Validation Attributes | ||||
| 	|-------------------------------------------------------------------------- | ||||
| 	| | ||||
| 	| The following language lines are used to swap attribute place-holders | ||||
| 	| with something more reader friendly such as E-Mail Address instead | ||||
| 	| of "email". This simply helps us make messages a little cleaner. | ||||
| 	| | ||||
| 	*/ | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Custom Validation Attributes | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | The following language lines are used to swap attribute place-holders | ||||
|     | with something more reader friendly such as E-Mail Address instead | ||||
|     | of "email". This simply helps us make messages a little cleaner. | ||||
|     | | ||||
|     */ | ||||
|  | ||||
| 	'attributes' => array(), | ||||
|     'attributes'           => array(), | ||||
|  | ||||
| ); | ||||
| ]; | ||||
|   | ||||
| @@ -1,7 +1,4 @@ | ||||
| <?php | ||||
|  | ||||
|  | ||||
|  | ||||
| namespace Firefly\Database; | ||||
|  | ||||
| use LaravelBook\Ardent\Ardent; | ||||
| @@ -73,7 +70,7 @@ abstract class SingleTableInheritanceEntity extends Ardent | ||||
|         // newEloquentBuilder() was added in 4.1 | ||||
|         $builder = $this->newEloquentBuilder($this->newBaseQueryBuilder()); | ||||
|  | ||||
|         // Once we have the query builders, we will set the model instances so the | ||||
|         // Once Firefly has the query builders, it will set the model instances so the | ||||
|         // builder can easily access any information it may need from the model | ||||
|         // while it is constructing and executing various queries against it. | ||||
|         $builder->setModel($this)->with($this->with); | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Class MigrationException | ||||
|  * | ||||
|  * @package Firefly\Helper | ||||
|  */ | ||||
| class MigrationException extends \Exception | ||||
| { | ||||
|  | ||||
| }  | ||||
							
								
								
									
										11
									
								
								app/lib/Firefly/Exception/ValidationException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/lib/Firefly/Exception/ValidationException.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <?php | ||||
| namespace Firefly\Exception; | ||||
|  | ||||
| /** | ||||
|  * Class ValidationException | ||||
|  * | ||||
|  * @package Firefly\Exception | ||||
|  */ | ||||
| class ValidationException extends \Exception { | ||||
|  | ||||
| }  | ||||
							
								
								
									
										327
									
								
								app/lib/Firefly/Form/Form.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								app/lib/Firefly/Form/Form.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,327 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Form; | ||||
|  | ||||
|  | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| class Form | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param null $value | ||||
|      * @param array $options | ||||
|      * @return string | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public static function ffInteger($name, $value = null, array $options = []) | ||||
|     { | ||||
|         $options['step'] = '1'; | ||||
|         return self::ffInput('number', $name, $value, $options); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public static function ffCheckbox($name, $value = 1, $checked = null, $options = []) | ||||
|     { | ||||
|         $options['checked'] = $checked ? true : null; | ||||
|         return self::ffInput('checkbox', $name, $value, $options); | ||||
|     } | ||||
|  | ||||
|     public static function ffAmount($name, $value = null, array $options = []) | ||||
|     { | ||||
|         $options['step'] = 'any'; | ||||
|         $options['min'] = '0.01'; | ||||
|         return self::ffInput('amount', $name, $value, $options); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param null $value | ||||
|      * @param array $options | ||||
|      * @return string | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public static function ffDate($name, $value = null, array $options = []) | ||||
|     { | ||||
|         return self::ffInput('date', $name, $value, $options); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param null $value | ||||
|      * @param array $options | ||||
|      * @return string | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public static function ffTags($name, $value = null, array $options = []) | ||||
|     { | ||||
|         $options['data-role'] = 'tagsinput'; | ||||
|         return self::ffInput('text', $name, $value, $options); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param array $list | ||||
|      * @param null $selected | ||||
|      * @param array $options | ||||
|      * @return string | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public static function ffSelect($name, array $list = [], $selected = null, array $options = []) | ||||
|     { | ||||
|         return self::ffInput('select', $name, $selected, $options, $list); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param null $value | ||||
|      * @param array $options | ||||
|      * @return string | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public static function ffText($name, $value = null, array $options = array()) | ||||
|     { | ||||
|         return self::ffInput('text', $name, $value, $options); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public static function label($name) | ||||
|     { | ||||
|         $labels = [ | ||||
|             'amount_min' => 'Amount (min)', | ||||
|             'amount_max' => 'Amount (max)', | ||||
|             'match' => 'Matches on', | ||||
|             'repeat_freq' => 'Repetition', | ||||
|             'account_from_id' => 'Account from', | ||||
|             'account_to_id' => 'Account to', | ||||
|             'account_id' => 'Asset account' | ||||
|         ]; | ||||
|  | ||||
|         return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name)); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return buttons for update/validate/return. | ||||
|      * | ||||
|      * @param $type | ||||
|      * @param $name | ||||
|      */ | ||||
|     public static function ffOptionsList($type, $name) | ||||
|     { | ||||
|         $previousValue = \Input::old('post_submit_action'); | ||||
|         $previousValue = is_null($previousValue) ? 'store' : $previousValue; | ||||
|         /* | ||||
|          * Store. | ||||
|          */ | ||||
|         $store = ''; | ||||
|         switch ($type) { | ||||
|             case 'create': | ||||
|                 $store = '<div class="form-group"><label for="default" class="col-sm-4 control-label">Store</label>'; | ||||
|                 $store .= '<div class="col-sm-8"><div class="radio"><label>'; | ||||
|                 $store .= \Form::radio('post_submit_action', 'store', $previousValue == 'store'); | ||||
|                 $store .= 'Store ' . $name . '</label></div></div></div>'; | ||||
|                 break; | ||||
|             case 'update': | ||||
|                 $store = '<div class="form-group"><label for="default" class="col-sm-4 control-label">Store</label>'; | ||||
|                 $store .= '<div class="col-sm-8"><div class="radio"><label>'; | ||||
|                 $store .= \Form::radio('post_submit_action', 'update', $previousValue == 'store'); | ||||
|                 $store .= 'Update ' . $name . '</label></div></div></div>'; | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot create ffOptionsList for option (store) ' . $type); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * validate is always the same: | ||||
|          */ | ||||
|         $validate = '<div class="form-group"><label for="validate_only" class="col-sm-4 control-label">Validate only'; | ||||
|         $validate .= '</label><div class="col-sm-8"><div class="radio"><label>'; | ||||
|         $validate .= \Form::radio('post_submit_action', 'validate_only', $previousValue == 'validate_only'); | ||||
|         $validate .= 'Only validate, do not save</label></div></div></div>'; | ||||
|  | ||||
|         /* | ||||
|          * Store & return: | ||||
|          */ | ||||
|         switch ($type) { | ||||
|             case 'create': | ||||
|                 $return = '<div class="form-group"><label for="return_to_form" class="col-sm-4 control-label">'; | ||||
|                 $return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>'; | ||||
|                 $return .= \Form::radio('post_submit_action','create_another', $previousValue == 'create_another'); | ||||
|                 $return .= 'After storing, return here to create another one.</label></div></div></div>'; | ||||
|                 break; | ||||
|             case 'update': | ||||
|                 $return = '<div class="form-group"><label for="return_to_edit" class="col-sm-4 control-label">'; | ||||
|                 $return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>'; | ||||
|                 $return .= \Form::radio('post_submit_action','return_to_edit', $previousValue == 'return_to_edit'); | ||||
|                 $return .= 'After updating, return here.</label></div></div></div>'; | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot create ffOptionsList for option (store+return) ' . $type); | ||||
|                 break; | ||||
|         } | ||||
|         return $store.$validate.$return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $type | ||||
|      * @param $name | ||||
|      * @param null $value | ||||
|      * @param array $options | ||||
|      * @param array $list | ||||
|      * @return string | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public static function ffInput($type, $name, $value = null, array $options = array(), $list = []) | ||||
|     { | ||||
|         /* | ||||
|          * add some defaults to this method: | ||||
|          */ | ||||
|         $options['class'] = 'form-control'; | ||||
|         $options['id'] = 'ffInput_' . $name; | ||||
|         $options['autocomplete'] = 'off'; | ||||
|         $label = self::label($name); | ||||
|         /* | ||||
|          * Make label and placeholder look nice. | ||||
|          */ | ||||
|         $options['placeholder'] = ucfirst($name); | ||||
|  | ||||
|         /* | ||||
|          * Get prefilled value: | ||||
|          */ | ||||
|         if(\Session::has('prefilled')) { | ||||
|             $prefilled = \Session::get('prefilled'); | ||||
|             $value = isset($prefilled[$name]) && is_null($value) ? $prefilled[$name] : $value; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Get the value. | ||||
|          */ | ||||
|         if (!is_null(\Input::old($name))) { | ||||
|             /* | ||||
|              * Old value overrules $value. | ||||
|              */ | ||||
|             $value = \Input::old($name); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Get errors, warnings and successes from session: | ||||
|          */ | ||||
|         /** @var MessageBag $errors */ | ||||
|         $errors = \Session::get('errors'); | ||||
|  | ||||
|         /** @var MessageBag $warnings */ | ||||
|         $warnings = \Session::get('warnings'); | ||||
|  | ||||
|         /** @var MessageBag $successes */ | ||||
|         $successes = \Session::get('successes'); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * If errors, add some more classes. | ||||
|          */ | ||||
|         switch (true) { | ||||
|             case (!is_null($errors) && $errors->has($name)): | ||||
|                 $classes = 'form-group has-error has-feedback'; | ||||
|                 break; | ||||
|             case (!is_null($warnings) && $warnings->has($name)): | ||||
|                 $classes = 'form-group has-warning has-feedback'; | ||||
|                 break; | ||||
|             case (!is_null($successes) && $successes->has($name)): | ||||
|                 $classes = 'form-group has-success has-feedback'; | ||||
|                 break; | ||||
|             default: | ||||
|                 $classes = 'form-group'; | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Add some HTML. | ||||
|          */ | ||||
|         $html = '<div class="' . $classes . '">'; | ||||
|         $html .= '<label for="' . $options['id'] . '" class="col-sm-4 control-label">' . $label . '</label>'; | ||||
|         $html .= '<div class="col-sm-8">'; | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Switch input type: | ||||
|          */ | ||||
|         unset($options['label']); | ||||
|         switch ($type) { | ||||
|             case 'text': | ||||
|                 $html .= \Form::input('text', $name, $value, $options); | ||||
|                 break; | ||||
|             case 'amount': | ||||
|                 $html .= '<div class="input-group"><div class="input-group-addon">€</div>'; | ||||
|                 $html .= \Form::input('number', $name, $value, $options); | ||||
|                 $html .= '</div>'; | ||||
|                 break; | ||||
|             case 'number': | ||||
|                 $html .= \Form::input('number', $name, $value, $options); | ||||
|                 break; | ||||
|             case 'checkbox': | ||||
|                 $checked = $options['checked']; | ||||
|                 unset($options['checked'], $options['placeholder'], $options['autocomplete'], $options['class']); | ||||
|                 $html .= '<div class="checkbox"><label>'; | ||||
|                 $html .= \Form::checkbox($name, $value, $checked, $options); | ||||
|                 $html .= '</label></div>'; | ||||
|  | ||||
|  | ||||
|                 break; | ||||
|             case 'date': | ||||
|                 $html .= \Form::input('date', $name, $value, $options); | ||||
|                 break; | ||||
|             case 'select': | ||||
|                 $html .= \Form::select($name, $list, $value, $options); | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot handle type "' . $type . '" in FFFormBuilder.'); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * If errors, respond to them: | ||||
|          */ | ||||
|  | ||||
|         if (!is_null($errors)) { | ||||
|             if ($errors->has($name)) { | ||||
|                 $html .= '<span class="glyphicon glyphicon-remove form-control-feedback"></span>'; | ||||
|                 $html .= '<p class="text-danger">' . e($errors->first($name)) . '</p>'; | ||||
|             } | ||||
|         } | ||||
|         unset($errors); | ||||
|         /* | ||||
|          * If warnings, respond to them: | ||||
|          */ | ||||
|  | ||||
|         if (!is_null($warnings)) { | ||||
|             if ($warnings->has($name)) { | ||||
|                 $html .= '<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>'; | ||||
|                 $html .= '<p class="text-warning">' . e($warnings->first($name)) . '</p>'; | ||||
|             } | ||||
|         } | ||||
|         unset($warnings); | ||||
|  | ||||
|         /* | ||||
|          * If successes, respond to them: | ||||
|          */ | ||||
|  | ||||
|         if (!is_null($successes)) { | ||||
|             if ($successes->has($name)) { | ||||
|                 $html .= '<span class="glyphicon glyphicon-ok form-control-feedback"></span>'; | ||||
|                 $html .= '<p class="text-success">' . e($successes->first($name)) . '</p>'; | ||||
|             } | ||||
|         } | ||||
|         unset($successes); | ||||
|  | ||||
|         $html .= '</div>'; | ||||
|         $html .= '</div>'; | ||||
|  | ||||
|         return $html; | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -2,8 +2,6 @@ | ||||
|  | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class Account | ||||
|  * | ||||
| @@ -11,166 +9,129 @@ use Illuminate\Database\Eloquent\Collection; | ||||
|  */ | ||||
| class Account implements AccountInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return array|mixed | ||||
|      */ | ||||
|     public function index(Collection $accounts) | ||||
|     { | ||||
|  | ||||
|         $list = [ | ||||
|             'personal'      => [], | ||||
|             'beneficiaries' => [], | ||||
|             'initial'       => [], | ||||
|             'cash'          => [] | ||||
|         ]; | ||||
|         foreach ($accounts as $account) { | ||||
|  | ||||
|             switch ($account->accounttype->description) { | ||||
|                 case 'Default account': | ||||
|                     $list['personal'][] = $account; | ||||
|                     break; | ||||
|                 case 'Cash account': | ||||
|                     $list['cash'][] = $account; | ||||
|                     break; | ||||
|                 case 'Initial balance account': | ||||
|                     $list['initial'][] = $account; | ||||
|                     break; | ||||
|                 case 'Beneficiary account': | ||||
|                     $list['beneficiaries'][] = $account; | ||||
|                     break; | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $list; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @return \TransactionJournal|null | ||||
|      */ | ||||
|     public function openingBalanceTransaction(\Account $account) | ||||
|     { | ||||
|         $transactionType = \TransactionType::where('type', 'Opening balance')->first(); | ||||
|  | ||||
|         return \TransactionJournal:: | ||||
|             with( | ||||
|                 ['transactions' => function ($q) { | ||||
|                         $q->orderBy('amount', 'ASC'); | ||||
|                     }] | ||||
|             )->where('transaction_type_id', $transactionType->id) | ||||
|             ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|             ->where('transactions.account_id', $account->id)->first(['transaction_journals.*']); | ||||
|  | ||||
|         return \TransactionJournal::withRelevantData() | ||||
|                                   ->accountIs($account) | ||||
|                                   ->leftJoin('transaction_types', 'transaction_types.id', '=', | ||||
|                 'transaction_journals.transaction_type_id') | ||||
|                                   ->where('transaction_types.type', 'Opening balance') | ||||
|                                   ->first(['transaction_journals.*']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * @param          $perPage | ||||
|      * Since it is entirely possible the database is messed up somehow it might be that a transaction | ||||
|      * journal has only one transaction. This is mainly caused by wrong deletions and other artefacts from the past. | ||||
|      * | ||||
|      * @return mixed|void | ||||
|      * If it is the case, Firefly removes $item and continues like nothing ever happened. This will however, | ||||
|      * mess up some statisics but it's decided everybody should learn to live with that. | ||||
|      * | ||||
|      * Firefly might be needing some cleanup routine in the future. | ||||
|      * | ||||
|      * For now, Firefly simply warns the user of this. | ||||
|      * | ||||
|      * @param \Account $account | ||||
|      * @param $perPage | ||||
|      * | ||||
|      * @return array|mixed | ||||
|      * @throws \Firefly\Exception\FireflyException | ||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||
|      */ | ||||
|     public function show(\Account $account, $perPage) | ||||
|     { | ||||
|         $start = \Session::get('start'); | ||||
|         $end = \Session::get('end'); | ||||
|         $end   = \Session::get('end'); | ||||
|         $stats = [ | ||||
|             'budgets'    => [], | ||||
|             'categories' => [], | ||||
|             'accounts'   => [] | ||||
|             'accounts' => [] | ||||
|         ]; | ||||
|         $items = []; | ||||
|  | ||||
|  | ||||
|         // build a query: | ||||
|         $query = \TransactionJournal::with( | ||||
|             ['transactions'                        => function ($q) { | ||||
|                     $q->orderBy('amount', 'ASC'); | ||||
|                 }, 'transactiontype', 'components' => function ($q) { | ||||
|                     $q->orderBy('class'); | ||||
|                 }, 'transactions.account.accounttype'] | ||||
|         )->orderBy('date', 'DESC')->leftJoin( | ||||
|                 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' | ||||
|             )->where('transactions.account_id', $account->id)->where('date', '>=', $start->format('Y-m-d'))->where( | ||||
|                 'date', '<=', $end->format('Y-m-d') | ||||
|             )->orderBy('transaction_journals.id', 'DESC'); | ||||
|         $query = \TransactionJournal::withRelevantData() | ||||
|                                     ->defaultSorting() | ||||
|                                     ->accountIs($account) | ||||
|                                     ->after($start) | ||||
|                                     ->before($end); | ||||
|         // filter some: | ||||
|         switch (\Input::get('type')) { | ||||
|             case 'transactions': | ||||
|                 $query->transactionTypes(['Deposit', 'Withdrawal']); | ||||
|                 break; | ||||
|             case 'transfers': | ||||
|                 $query->transactionTypes(['Transfer']); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         switch (\Input::get('show')) { | ||||
|             case 'expenses': | ||||
|             case 'out': | ||||
|                 $query->lessThan(0); | ||||
|                 break; | ||||
|             case 'income': | ||||
|             case 'in': | ||||
|                 $query->moreThan(0); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // build paginator: | ||||
|         $totalItems = $query->count(); | ||||
|         $page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1; | ||||
|         $skip = ($page - 1) * $perPage; | ||||
|         $result = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']); | ||||
|         // in the mean time, build list of categories, budgets and other accounts: | ||||
|         $page       = max(1, intval(\Input::get('page'))); | ||||
|         $skip       = ($page - 1) * $perPage; | ||||
|         $result     = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']); | ||||
|  | ||||
|  | ||||
|         // get the relevant budgets, categories and accounts from this list: | ||||
|         /** @var $item \TransactionJournal */ | ||||
|         foreach ($result as $item) { | ||||
|             $items[] = $item; | ||||
|         foreach ($result as $index => $item) { | ||||
|  | ||||
|             foreach ($item->components as $component) { | ||||
|                 if ($component->class == 'Budget') { | ||||
|                     $stats['budgets'][$component->id] = $component; | ||||
|                 } | ||||
|                 if ($component->class == 'Category') { | ||||
|                     $stats['categories'][$component->id] = $component; | ||||
|                 } | ||||
|                 $stats[$component->class][$component->id] = $component; | ||||
|             } | ||||
|             $fromAccount = $item->transactions[0]->account; | ||||
|             $toAccount = $item->transactions[1]->account; | ||||
|  | ||||
|             if (count($item->transactions) < 2) { | ||||
|                 \Session::flash('warning', 'Some transactions are incomplete; they will not be shown.'); | ||||
|                 unset($result[$index]); | ||||
|                 continue; | ||||
|             } | ||||
|             $items[]                             = $item; | ||||
|             $fromAccount                         = $item->transactions[0]->account; | ||||
|             $toAccount                           = $item->transactions[1]->account; | ||||
|             $stats['accounts'][$fromAccount->id] = $fromAccount; | ||||
|             $stats['accounts'][$toAccount->id] = $toAccount; | ||||
|             $stats['accounts'][$toAccount->id]   = $toAccount; | ||||
|         } | ||||
|         unset($result, $page); | ||||
|         $paginator = \Paginator::make($items, $totalItems, $perPage); | ||||
|  | ||||
|         // statistics | ||||
|         $stats['period']['in'] = floatval( | ||||
|             \Transaction::where('account_id', $account->id)->where('amount', '>', 0)->leftJoin( | ||||
|                 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' | ||||
|             )->leftJoin( | ||||
|                     'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|                 )->whereIn('transaction_types.type', ['Deposit', 'Withdrawal'])->where( | ||||
|                     'transaction_journals.date', '>=', $start->format('Y-m-d') | ||||
|                 )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') | ||||
|         ); | ||||
|         unset($result, $page, $item, $fromAccount, $toAccount); | ||||
|  | ||||
|  | ||||
|         $stats['period']['out'] = floatval( | ||||
|             \Transaction::where('account_id', $account->id)->where('amount', '<', 0)->leftJoin( | ||||
|                 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' | ||||
|             )->leftJoin( | ||||
|                     'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|                 )->whereIn('transaction_types.type', ['Deposit', 'Withdrawal'])->where( | ||||
|                     'transaction_journals.date', '>=', $start->format('Y-m-d') | ||||
|                 )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') | ||||
|         ); | ||||
|         $stats['period']['diff'] = $stats['period']['in'] + $stats['period']['out']; | ||||
|         // statistics (transactions) | ||||
|         $trIn   = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0) | ||||
|                                        ->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')); | ||||
|         $trOut  = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0) | ||||
|                                        ->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')); | ||||
|         $trDiff = $trIn + $trOut; | ||||
|  | ||||
|         $stats['period']['t_in'] = floatval( | ||||
|             \Transaction::where('account_id', $account->id)->where('amount', '>', 0)->leftJoin( | ||||
|                 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' | ||||
|             )->leftJoin( | ||||
|                     'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|                 )->where('transaction_types.type', 'Transfer')->where( | ||||
|                     'transaction_journals.date', '>=', $start->format('Y-m-d') | ||||
|                 )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') | ||||
|         ); | ||||
|         // statistics (transfers) | ||||
|         $trfIn   = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0) | ||||
|                                         ->transactionTypes(['Transfer'])->sum('transactions.amount')); | ||||
|         $trfOut  = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0) | ||||
|                                         ->transactionTypes(['Transfer'])->sum('transactions.amount')); | ||||
|         $trfDiff = $trfIn + $trfOut; | ||||
|  | ||||
|         $stats['period']['t_out'] = floatval( | ||||
|             \Transaction::where('account_id', $account->id)->where('amount', '<', 0)->leftJoin( | ||||
|                 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' | ||||
|             )->leftJoin( | ||||
|                     'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|                 )->where('transaction_types.type', 'Transfer')->where( | ||||
|                     'transaction_journals.date', '>=', $start->format('Y-m-d') | ||||
|                 )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') | ||||
|         ); | ||||
|  | ||||
|         $stats['period']['t_diff'] = $stats['period']['t_in'] + $stats['period']['t_out']; | ||||
|         $stats['period'] = [ | ||||
|             'in'     => $trIn, | ||||
|             'out'    => $trOut, | ||||
|             'diff'   => $trDiff, | ||||
|             't_in'   => $trfIn, | ||||
|             't_out'  => $trfOut, | ||||
|             't_diff' => $trfDiff | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         $return = [ | ||||
|             'journals'   => $paginator, | ||||
| @@ -178,7 +139,5 @@ class Account implements AccountInterface | ||||
|         ]; | ||||
|  | ||||
|         return $return; | ||||
|  | ||||
|  | ||||
|     } | ||||
| }  | ||||
| @@ -12,15 +12,6 @@ use Illuminate\Database\Eloquent\Collection; | ||||
| interface AccountInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Build the index: | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function index(Collection $accounts); | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * | ||||
|   | ||||
| @@ -13,6 +13,11 @@ class Budget implements BudgetInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * First, loop all budgets, all of their limits and all repetitions to get an overview per period | ||||
|      * and some basic information about that repetition's data. | ||||
|      * | ||||
|      * | ||||
|      * | ||||
|      * @param Collection $budgets | ||||
|      * | ||||
|      * @return mixed|void | ||||
| @@ -21,32 +26,30 @@ class Budget implements BudgetInterface | ||||
|     { | ||||
|         $return = []; | ||||
|  | ||||
|         /** @var \Budget $budget */ | ||||
|         foreach ($budgets as $budget) { | ||||
|  | ||||
|             /** @var \Limit $limit */ | ||||
|             foreach ($budget->limits as $limit) { | ||||
|  | ||||
|                 /** @var \LimitRepetition $rep */ | ||||
|                 foreach ($limit->limitrepetitions as $rep) { | ||||
|                     $periodOrder = $rep->periodOrder(); | ||||
|                     $period = $rep->periodShow(); | ||||
|                     $return[$periodOrder] = isset($return[$periodOrder]) | ||||
|                         ? $return[$periodOrder] | ||||
|                         : ['date'       => $period, | ||||
|                            'dateObject' => $rep->startdate, | ||||
|                            'start'      => $rep->startdate, | ||||
|                            'end'        => $rep->enddate, | ||||
|                            'budget_id'  => $limit->budget_id]; | ||||
|                 /** @var \LimitRepetition $repetition */ | ||||
|                 foreach ($limit->limitrepetitions as $repetition) { | ||||
|                     $repetition->left = $repetition->leftInRepetition(); | ||||
|                     $periodOrder      = $repetition->periodOrder(); | ||||
|                     $period           = $repetition->periodShow(); | ||||
|                     if (!isset($return[$periodOrder])) { | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // put all the budgets under their respective date: | ||||
|         foreach ($budgets as $budget) { | ||||
|             foreach ($budget->limits as $limit) { | ||||
|                 foreach ($limit->limitrepetitions as $rep) { | ||||
|                     $rep->left = $rep->left(); | ||||
|                         $return[$periodOrder] = [ | ||||
|                             'date'             => $period, | ||||
|                             'start'            => $repetition->startdate, | ||||
|                             'end'              => $repetition->enddate, | ||||
|                             'budget_id'        => $budget->id, | ||||
|                             'limitrepetitions' => [$repetition] | ||||
|                         ]; | ||||
|                     } else { | ||||
|                         $return[$periodOrder]['limitrepetitions'][] = $repetition; | ||||
|                     } | ||||
|  | ||||
|                     $month = $rep->periodOrder(); | ||||
|                     $return[$month]['limitrepetitions'][] = $rep; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -56,30 +59,24 @@ class Budget implements BudgetInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a repetition (complex because of user check) | ||||
|      * and then get the transactions in it. | ||||
|      * @param         $repetitionId | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function organizeRepetition($repetitionId) | ||||
|     public function organizeRepetition(\LimitRepetition $repetition) | ||||
|     { | ||||
|         $result = []; | ||||
|         $repetition = \LimitRepetition::with('limit', 'limit.budget')->leftJoin( | ||||
|             'limits', 'limit_repetitions.limit_id', '=', 'limits.id' | ||||
|         )->leftJoin('components', 'limits.component_id', '=', 'components.id')->where( | ||||
|                 'components.user_id', \Auth::user()->id | ||||
|             ) | ||||
|             ->where('limit_repetitions.id', $repetitionId)->first(['limit_repetitions.*']); | ||||
|  | ||||
|         // get transactions: | ||||
|         $set = $repetition->limit->budget->transactionjournals()->with( | ||||
|             'transactions', 'transactions.account', 'components', 'transactiontype' | ||||
|         )->leftJoin( | ||||
|                 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|             )->where('transaction_types.type', 'Withdrawal')->where( | ||||
|                 'date', '>=', $repetition->startdate->format('Y-m-d') | ||||
|             )->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->orderBy( | ||||
|                 'id', 'DESC' | ||||
|             )->get(['transaction_journals.*']); | ||||
|         $set = $repetition->limit->budget | ||||
|             ->transactionjournals() | ||||
|             ->withRelevantData() | ||||
|             ->transactionTypes(['Withdrawal']) | ||||
|             ->after($repetition->startdate) | ||||
|             ->before($repetition->enddate) | ||||
|             ->defaultSorting() | ||||
|             ->get(['transaction_journals.*']); | ||||
|  | ||||
|         $result[0] = [ | ||||
|             'date'            => $repetition->periodShow(), | ||||
| @@ -93,8 +90,10 @@ class Budget implements BudgetInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * | ||||
|      * @param \Budget $budget | ||||
|      * @param bool    $useSessionDates | ||||
|      * @param bool $useSessionDates | ||||
|      * | ||||
|      * @return array|mixed | ||||
|      * @throws \Firefly\Exception\FireflyException | ||||
| @@ -102,15 +101,15 @@ class Budget implements BudgetInterface | ||||
|     public function organizeRepetitions(\Budget $budget, $useSessionDates = false) | ||||
|     { | ||||
|         $sessionStart = \Session::get('start'); | ||||
|         $sessionEnd = \Session::get('end'); | ||||
|         $sessionEnd   = \Session::get('end'); | ||||
|  | ||||
|         $result = []; | ||||
|         $result       = []; | ||||
|         $inRepetition = []; | ||||
|  | ||||
|         // get the limits: | ||||
|         if ($useSessionDates) { | ||||
|             $limits = $budget->limits()->where('startdate', '>=', $sessionStart->format('Y-m-d'))->where( | ||||
|                 'startdate', '<=', $sessionEnd->format('Y-m-d') | ||||
|                              'startdate', '<=', $sessionEnd->format('Y-m-d') | ||||
|             )->get(); | ||||
|         } else { | ||||
|             $limits = $budget->limits; | ||||
| @@ -119,7 +118,7 @@ class Budget implements BudgetInterface | ||||
|         /** @var \Limit $limit */ | ||||
|         foreach ($limits as $limit) { | ||||
|             foreach ($limit->limitrepetitions as $repetition) { | ||||
|                 $order = $repetition->periodOrder(); | ||||
|                 $order          = $repetition->periodOrder(); | ||||
|                 $result[$order] = [ | ||||
|                     'date'            => $repetition->periodShow(), | ||||
|                     'limitrepetition' => $repetition, | ||||
| @@ -127,16 +126,14 @@ class Budget implements BudgetInterface | ||||
|                     'journals'        => [], | ||||
|                     'paginated'       => false | ||||
|                 ]; | ||||
|                 $transactions = []; | ||||
|                 $set = $budget->transactionjournals()->with( | ||||
|                     'transactions', 'transactions.account', 'components', 'transactiontype' | ||||
|                 )->leftJoin( | ||||
|                         'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|                     )->where('transaction_types.type', 'Withdrawal')->where( | ||||
|                         'date', '>=', $repetition->startdate->format('Y-m-d') | ||||
|                     )->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->orderBy( | ||||
|                         'id', 'DESC' | ||||
|                     )->get(['transaction_journals.*']); | ||||
|                 $transactions   = []; | ||||
|                 $set            = $budget->transactionjournals() | ||||
|                                          ->withRelevantData() | ||||
|                                          ->transactionTypes(['Withdrawal']) | ||||
|                                          ->after($repetition->startdate) | ||||
|                                          ->before($repetition->enddate) | ||||
|                                          ->defaultSorting() | ||||
|                                          ->get(['transaction_journals.*']); | ||||
|                 foreach ($set as $entry) { | ||||
|                     $transactions[] = $entry; | ||||
|                     $inRepetition[] = $entry->id; | ||||
| @@ -146,30 +143,17 @@ class Budget implements BudgetInterface | ||||
|  | ||||
|         } | ||||
|         if ($useSessionDates === false) { | ||||
|             $query = $budget->transactionjournals()->withRelevantData()->defaultSorting(); | ||||
|             if (count($inRepetition) > 0) { | ||||
|                 $query = $budget->transactionjournals()->with( | ||||
|                     'transactions', 'transactions.account', 'components', 'transactiontype', | ||||
|                     'transactions.account.accounttype' | ||||
|                 )->whereNotIn( | ||||
|                         'transaction_journals.id', $inRepetition | ||||
|                     )->orderBy('date', 'DESC')->orderBy( | ||||
|                         'transaction_journals.id', 'DESC' | ||||
|                     ); | ||||
|             } else { | ||||
|                 $query = $budget->transactionjournals()->with( | ||||
|                     'transactions', 'transactions.account', 'components', 'transactiontype', | ||||
|                     'transactions.account.accounttype' | ||||
|                 )->orderBy('date', 'DESC')->orderBy( | ||||
|                         'transaction_journals.id', 'DESC' | ||||
|                     ); | ||||
|                 $query->whereNotIn('transaction_journals.id', $inRepetition); | ||||
|             } | ||||
|  | ||||
|             // build paginator: | ||||
|             $perPage = 25; | ||||
|             $perPage    = 25; | ||||
|             $totalItems = $query->count(); | ||||
|             $page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1; | ||||
|             $skip = ($page - 1) * $perPage; | ||||
|             $set = $query->skip($skip)->take($perPage)->get(); | ||||
|             $page       = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1; | ||||
|             $skip       = ($page - 1) * $perPage; | ||||
|             $set        = $query->skip($skip)->take($perPage)->get(); | ||||
|  | ||||
|             // stupid paginator! | ||||
|             $items = []; | ||||
| @@ -177,9 +161,12 @@ class Budget implements BudgetInterface | ||||
|             foreach ($set as $item) { | ||||
|                 $items[] = $item; | ||||
|             } | ||||
|             $paginator = \Paginator::make($items, $totalItems, $perPage); | ||||
|             $result['0000'] = ['date'     => 'Not in an envelope', 'limit' => null, 'paginated' => true, | ||||
|                                'journals' => $paginator]; | ||||
|             $paginator      = \Paginator::make($items, $totalItems, $perPage); | ||||
|             $result['0000'] = [ | ||||
|                 'date'      => 'Not in an envelope', | ||||
|                 'limit'     => null, | ||||
|                 'paginated' => true, | ||||
|                 'journals'  => $paginator]; | ||||
|         } | ||||
|         krsort($result); | ||||
|  | ||||
| @@ -196,13 +183,12 @@ class Budget implements BudgetInterface | ||||
|         $inRepetitions = []; | ||||
|         foreach ($budget->limits as $limit) { | ||||
|             foreach ($limit->limitrepetitions as $repetition) { | ||||
|                 $set = $budget->transactionjournals()->leftJoin( | ||||
|                     'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' | ||||
|                 )->where('transaction_types.type', 'Withdrawal')->where( | ||||
|                         'date', '>=', $repetition->startdate->format('Y-m-d') | ||||
|                     )->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->get( | ||||
|                         ['transaction_journals.id'] | ||||
|                     ); | ||||
|                 $set = $budget->transactionjournals() | ||||
|                               ->transactionTypes(['Withdrawal']) | ||||
|                               ->after($repetition->startdate) | ||||
|                               ->before($repetition->enddate) | ||||
|                               ->defaultSorting() | ||||
|                               ->get(['transaction_journals.id']); | ||||
|                 foreach ($set as $item) { | ||||
|                     $inRepetitions[] = $item->id; | ||||
|                 } | ||||
| @@ -210,21 +196,17 @@ class Budget implements BudgetInterface | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $query = $budget->transactionjournals()->with( | ||||
|             'transactions', 'transactions.account', 'components', 'transactiontype', | ||||
|             'transactions.account.accounttype' | ||||
|         )->whereNotIn( | ||||
|                 'transaction_journals.id', $inRepetitions | ||||
|             )->orderBy('date', 'DESC')->orderBy( | ||||
|                 'transaction_journals.id', 'DESC' | ||||
|             ); | ||||
|         $query = $budget->transactionjournals() | ||||
|                         ->withRelevantData() | ||||
|                         ->whereNotIn('transaction_journals.id', $inRepetitions) | ||||
|                         ->defaultSorting(); | ||||
|  | ||||
|         // build paginator: | ||||
|         $perPage = 25; | ||||
|         $perPage    = 25; | ||||
|         $totalItems = $query->count(); | ||||
|         $page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1; | ||||
|         $skip = ($page - 1) * $perPage; | ||||
|         $set = $query->skip($skip)->take($perPage)->get(); | ||||
|         $page       = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1; | ||||
|         $skip       = ($page - 1) * $perPage; | ||||
|         $set        = $query->skip($skip)->take($perPage)->get(); | ||||
|  | ||||
|         // stupid paginator! | ||||
|         $items = []; | ||||
| @@ -233,8 +215,12 @@ class Budget implements BudgetInterface | ||||
|             $items[] = $item; | ||||
|         } | ||||
|         $paginator = \Paginator::make($items, $totalItems, $perPage); | ||||
|         $result = [0 => ['date'     => 'Not in an envelope', 'limit' => null, 'paginated' => true, | ||||
|                          'journals' => $paginator]]; | ||||
|         $result    = [0 => [ | ||||
|             'date'      => 'Not in an envelope', | ||||
|             'limit'     => null, | ||||
|             'paginated' => true, | ||||
|             'journals'  => $paginator | ||||
|         ]]; | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|   | ||||
| @@ -23,7 +23,7 @@ interface BudgetInterface | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function organizeRepetition($repetitionId); | ||||
|     public function organizeRepetition(\LimitRepetition $repetition); | ||||
|  | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -4,6 +4,7 @@ namespace Firefly\Helper\Controllers; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class Chart | ||||
| @@ -23,14 +24,21 @@ class Chart implements ChartInterface | ||||
|     public function account(\Account $account, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $current = clone $start; | ||||
|         $today = new Carbon; | ||||
|         $return = ['name' => $account->name, 'id' => $account->id, 'data' => []]; | ||||
|         $today   = new Carbon; | ||||
|         $return  = [ | ||||
|             'name'          => $account->name, | ||||
|             'id'            => $account->id, | ||||
|             'type'          => 'spline', | ||||
|             'pointStart'    => $start->timestamp * 1000, | ||||
|             'pointInterval' => 24 * 3600 * 1000, // one day | ||||
|             'data'          => [] | ||||
|         ]; | ||||
|  | ||||
|         while ($current <= $end) { | ||||
|             if ($current > $today) { | ||||
|                 $return['data'][] = [$current->timestamp * 1000, $account->predict(clone $current)]; | ||||
|                 $return['data'][] = $account->predict(clone $current); | ||||
|             } else { | ||||
|                 $return['data'][] = [$current->timestamp * 1000, $account->balance(clone $current)]; | ||||
|                 $return['data'][] = $account->balance(clone $current); | ||||
|             } | ||||
|  | ||||
|             $current->addDay(); | ||||
| @@ -94,92 +102,6 @@ class Chart implements ChartInterface | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function budgets(Carbon $start) | ||||
|     { | ||||
|         // grab all budgets in the time period, like the index does: | ||||
|         // get the budgets for this period: | ||||
|  | ||||
|         $data = []; | ||||
|  | ||||
|         $budgets = \Auth::user()->budgets()->with( | ||||
|             ['limits'                        => function ($q) { | ||||
|                     $q->orderBy('limits.startdate', 'ASC'); | ||||
|                 }, 'limits.limitrepetitions' => function ($q) use ($start) { | ||||
|                     $q->orderBy('limit_repetitions.startdate', 'ASC'); | ||||
|                     $q->where('startdate', $start->format('Y-m-d')); | ||||
|                 }] | ||||
|         )->orderBy('name', 'ASC')->get(); | ||||
|         $limitInPeriod = ''; | ||||
|         $spentInPeriod = ''; | ||||
|  | ||||
|         foreach ($budgets as $budget) { | ||||
|             $budget->count = 0; | ||||
|             foreach ($budget->limits as $limit) { | ||||
|                 /** @var $rep \LimitRepetition */ | ||||
|                 foreach ($limit->limitrepetitions as $index => $rep) { | ||||
|                     if ($index == 0) { | ||||
|                         $limitInPeriod = 'Envelope for ' . $rep->periodShow(); | ||||
|                         $spentInPeriod = 'Spent in ' . $rep->periodShow(); | ||||
|                     } | ||||
|                     $rep->left = $rep->left(); | ||||
|                     // overspent: | ||||
|                     if ($rep->left < 0) { | ||||
|                         $rep->spent = ($rep->left * -1) + $rep->amount; | ||||
|                         $rep->overspent = $rep->left * -1; | ||||
|                         $total = $rep->spent + $rep->overspent; | ||||
|                         $rep->spent_pct = round(($rep->spent / $total) * 100); | ||||
|                         $rep->overspent_pct = 100 - $rep->spent_pct; | ||||
|                     } else { | ||||
|                         $rep->spent = $rep->amount - $rep->left; | ||||
|                         $rep->spent_pct = round(($rep->spent / $rep->amount) * 100); | ||||
|                         $rep->left_pct = 100 - $rep->spent_pct; | ||||
|  | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|                 $budget->count += count($limit->limitrepetitions); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $data['series'] = [ | ||||
|             [ | ||||
|                 'name' => $limitInPeriod, | ||||
|                 'data' => [] | ||||
|             ], | ||||
|             [ | ||||
|                 'name' => $spentInPeriod, | ||||
|                 'data' => [] | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         foreach ($budgets as $budget) { | ||||
|             if ($budget->count > 0) { | ||||
|                 $data['labels'][] = wordwrap($budget->name, 12, "<br>"); | ||||
|             } | ||||
|             foreach ($budget->limits as $limit) { | ||||
|                 foreach ($limit->limitrepetitions as $rep) { | ||||
|                     //0: envelope for period: | ||||
|                     $amount = floatval($rep->amount); | ||||
|                     $spent = $rep->spent; | ||||
|                     $color = $spent > $amount ? '#FF0000' : null; | ||||
|                     $data['series'][0]['data'][] = ['y' => $amount, 'id' => 'amount-' . $rep->id]; | ||||
|                     $data['series'][1]['data'][] = ['y' => $rep->spent, 'color' => $color, 'id' => 'spent-' . $rep->id]; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
| @@ -211,10 +133,10 @@ class Chart implements ChartInterface | ||||
|                     . ' transactions!'); | ||||
|             } | ||||
|             $transaction = $journal->transactions[0]; | ||||
|             $amount = floatval($transaction->amount); | ||||
|             $amount      = floatval($transaction->amount); | ||||
|  | ||||
|             // get budget from journal: | ||||
|             $category = $journal->categories()->first(); | ||||
|             $category     = $journal->categories()->first(); | ||||
|             $categoryName = is_null($category) ? '(no category)' : $category->name; | ||||
|  | ||||
|             $result[$categoryName] = isset($result[$categoryName]) ? $result[$categoryName] + floatval($amount) | ||||
| @@ -335,7 +257,7 @@ class Chart implements ChartInterface | ||||
|  | ||||
|  | ||||
|             // get sum for current range: | ||||
|             $journals = \TransactionJournal:: | ||||
|             $journals   = \TransactionJournal:: | ||||
|                 with( | ||||
|                     ['transactions' => function ($q) { | ||||
|                             $q->where('amount', '>', 0); | ||||
| @@ -360,7 +282,7 @@ class Chart implements ChartInterface | ||||
|                         . ' transactions!'); | ||||
|                 } | ||||
|                 $transaction = $journal->transactions[0]; | ||||
|                 $amount = floatval($transaction->amount); | ||||
|                 $amount      = floatval($transaction->amount); | ||||
|                 $currentSum += $amount; | ||||
|  | ||||
|             } | ||||
| @@ -401,4 +323,153 @@ class Chart implements ChartInterface | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * @param Carbon  $date | ||||
|      * | ||||
|      * @return float|null | ||||
|      */ | ||||
|     public function spentOnDay(\Budget $budget, Carbon $date) | ||||
|     { | ||||
|         return floatval( | ||||
|             \Transaction:: | ||||
|                 leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                 ->leftJoin( | ||||
|                     'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', | ||||
|                     'transaction_journals.id' | ||||
|                 )->where('component_transaction_journal.component_id', '=', $budget->id)->where( | ||||
|                     'transaction_journals.date', $date->format('Y-m-d') | ||||
|                 )->where('amount', '>', 0)->sum('amount') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * | ||||
|      * @return int[] | ||||
|      */ | ||||
|     public function allJournalsInBudgetEnvelope(\Budget $budget) | ||||
|     { | ||||
|         $inRepetitions = []; | ||||
|  | ||||
|         foreach ($budget->limits as $limit) { | ||||
|             foreach ($limit->limitrepetitions as $repetition) { | ||||
|                 $set = $budget | ||||
|                     ->transactionjournals() | ||||
|                     ->transactionTypes(['Withdrawal']) | ||||
|                     ->after($repetition->startdate) | ||||
|                     ->before($repetition->enddate) | ||||
|                     ->get(['transaction_journals.id']); | ||||
|  | ||||
|                 foreach ($set as $item) { | ||||
|                     $inRepetitions[] = $item->id; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return $inRepetitions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * @param array   $ids | ||||
|      * | ||||
|      * @return mixed|void | ||||
|      */ | ||||
|     public function journalsNotInSet(\Budget $budget, array $ids) | ||||
|     { | ||||
|         $query = $budget->transactionjournals() | ||||
|             ->whereNotIn('transaction_journals.id', $ids) | ||||
|             ->orderBy('date', 'DESC') | ||||
|             ->orderBy('transaction_journals.id', 'DESC'); | ||||
|  | ||||
|         $result = $query->get(['transaction_journals.id']); | ||||
|         $set    = []; | ||||
|         foreach ($result as $entry) { | ||||
|             $set[] = $entry->id; | ||||
|         } | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $set | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function transactionsByJournals(array $set) | ||||
|     { | ||||
|         $transactions = \Transaction::whereIn('transaction_journal_id', $set) | ||||
|             ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|             ->groupBy('transaction_journals.date') | ||||
|             ->where('amount', '>', 0)->get(['transaction_journals.date', \DB::Raw('SUM(`amount`) as `aggregate`')]); | ||||
|         return $transactions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all limit (LimitRepetitions) for a budget falling in a certain date range. | ||||
|      * | ||||
|      * @param \Budget $budget | ||||
|      * @param Carbon  $start | ||||
|      * @param Carbon  $end | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $reps = new Collection; | ||||
|         /** @var \Limit $limit */ | ||||
|         foreach ($budget->limits as $limit) { | ||||
|             $set = $limit->limitrepetitions()->where( | ||||
|                 function ($q) use ($start, $end) { | ||||
|                     // startdate is between range | ||||
|                     $q->where( | ||||
|                         function ($q) use ($start, $end) { | ||||
|                             $q->where('startdate', '>=', $start->format('Y-m-d')); | ||||
|                             $q->where('startdate', '<=', $end->format('Y-m-d')); | ||||
|                         } | ||||
|                     ); | ||||
|  | ||||
|                     // or enddate is between range. | ||||
|                     $q->orWhere( | ||||
|                         function ($q) use ($start, $end) { | ||||
|                             $q->where('enddate', '>=', $start->format('Y-m-d')); | ||||
|                             $q->where('enddate', '<=', $end->format('Y-m-d')); | ||||
|                         } | ||||
|                     ); | ||||
|                 } | ||||
|             )->get(); | ||||
|  | ||||
|             $reps = $reps->merge($set); | ||||
|         } | ||||
|         return $reps; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Firefly checks how much money has been spend on the limitrepetition (aka: the current envelope) in | ||||
|      * the period denoted. Aka, the user has a certain amount of money in an envelope and wishes to know how | ||||
|      * much he has spent between the dates entered. This date range can be a partial match with the date range | ||||
|      * of the envelope or no match at all. | ||||
|      * | ||||
|      * @param \LimitRepetition $repetition | ||||
|      * @param Carbon           $start | ||||
|      * @param Carbon           $end | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         return floatval( | ||||
|             \Transaction:: | ||||
|                 leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                 ->leftJoin( | ||||
|                     'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', | ||||
|                     'transaction_journals.id' | ||||
|                 )->where('component_transaction_journal.component_id', '=', $repetition->limit->budget->id)->where( | ||||
|                     'transaction_journals.date', '>=', $start->format('Y-m-d') | ||||
|                 )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->where( | ||||
|                     'amount', '>', 0 | ||||
|                 )->sum('amount') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -30,13 +30,6 @@ interface ChartInterface | ||||
|      */ | ||||
|     public function categories(Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function budgets(Carbon $start); | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * @param Carbon   $date | ||||
| @@ -54,4 +47,62 @@ interface ChartInterface | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function categoryShowChart(\Category $category, $range, Carbon $start, Carbon $end); | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * @param Carbon  $date | ||||
|      * | ||||
|      * @return float|null | ||||
|      */ | ||||
|     public function spentOnDay(\Budget $budget, Carbon $date); | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * | ||||
|      * @return int[] | ||||
|      */ | ||||
|     public function allJournalsInBudgetEnvelope(\Budget $budget); | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * @param array   $ids | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function journalsNotInSet(\Budget $budget, array $ids); | ||||
|  | ||||
|     /** | ||||
|      * @param array $set | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function transactionsByJournals(array $set); | ||||
|  | ||||
|     /** | ||||
|      * Get all limit (LimitRepetitions) for a budget falling in a certain date range. | ||||
|      * | ||||
|      * @param \Budget $budget | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end); | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Firefly checks how much money has been spend on the limitrepetition (aka: the current envelope) in | ||||
|      * the period denoted. Aka, the user has a certain amount of money in an envelope and wishes to know how | ||||
|      * much he has spent between the dates entered. This date range can be a partial match with the date range | ||||
|      * of the envelope or no match at all. | ||||
|      * | ||||
|      * @param \LimitRepetition $repetition | ||||
|      * @param Carbon           $start | ||||
|      * @param Carbon           $end | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end); | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										403
									
								
								app/lib/Firefly/Helper/Controllers/Json.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								app/lib/Firefly/Helper/Controllers/Json.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,403 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Created by PhpStorm. | ||||
|  * User: sander | ||||
|  * Date: 27/09/14 | ||||
|  * Time: 07:39 | ||||
|  */ | ||||
|  | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
| use LaravelBook\Ardent\Builder; | ||||
|  | ||||
| /** | ||||
|  * Class Json | ||||
|  * | ||||
|  * @package Firefly\Helper\Controllers | ||||
|  */ | ||||
| class Json implements JsonInterface | ||||
| { | ||||
|     /** | ||||
|      * Grabs all the parameters entered by the DataTables JQuery plugin and creates | ||||
|      * a nice array to be used by the other methods. It's also cleaning up and what-not. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function dataTableParameters() | ||||
|     { | ||||
|         /* | ||||
|          * Process all parameters! | ||||
|          */ | ||||
|         if (intval(\Input::get('length')) < 0) { | ||||
|             $length = 10000; // we get them all if no length is defined. | ||||
|         } else { | ||||
|             $length = intval(\Input::get('length')); | ||||
|         } | ||||
|         $parameters = [ | ||||
|             'start' => intval(\Input::get('start')), | ||||
|             'length' => $length, | ||||
|             'draw' => intval(\Input::get('draw')), | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Columns: | ||||
|          */ | ||||
|         if (!is_null(\Input::get('columns')) && is_array(\Input::get('columns'))) { | ||||
|             foreach (\Input::get('columns') as $column) { | ||||
|                 $parameters['columns'][] = [ | ||||
|                     'data' => $column['data'], | ||||
|                     'name' => $column['name'], | ||||
|                     'searchable' => $column['searchable'] == 'true' ? true : false, | ||||
|                     'orderable' => $column['orderable'] == 'true' ? true : false, | ||||
|                     'search' => [ | ||||
|                         'value' => $column['search']['value'], | ||||
|                         'regex' => $column['search']['regex'] == 'true' ? true : false, | ||||
|                     ] | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Sorting. | ||||
|          */ | ||||
|         $parameters['orderOnAccount'] = false; | ||||
|         if (!is_null(\Input::get('order')) && is_array(\Input::get('order'))) { | ||||
|             foreach (\Input::get('order') as $order) { | ||||
|                 $columnIndex           = intval($order['column']); | ||||
|                 $columnName            = $parameters['columns'][$columnIndex]['name']; | ||||
|                 $parameters['order'][] = [ | ||||
|                     'name' => $columnName, | ||||
|                     'dir' => strtoupper($order['dir']) | ||||
|                 ]; | ||||
|                 if ($columnName == 'to' || $columnName == 'from') { | ||||
|                     $parameters['orderOnAccount'] = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         /* | ||||
|          * Search parameters: | ||||
|          */ | ||||
|         $parameters['search'] = [ | ||||
|             'value' => '', | ||||
|             'regex' => false | ||||
|         ]; | ||||
|         if (!is_null(\Input::get('search')) && is_array(\Input::get('search'))) { | ||||
|             $search               = \Input::get('search'); | ||||
|             $parameters['search'] = [ | ||||
|                 'value' => $search['value'], | ||||
|                 'regex' => $search['regex'] == 'true' ? true : false | ||||
|             ]; | ||||
|         } | ||||
|         return $parameters; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Do some sorting, counting and ordering on the query and return a nicely formatted array | ||||
|      * that can be used by the DataTables JQuery plugin. | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      * @param Builder $query | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function journalDataset(array $parameters, Builder $query) | ||||
|     { | ||||
|         /* | ||||
|          * Count query: | ||||
|          */ | ||||
|         $count = $query->count(); | ||||
|  | ||||
|         /* | ||||
|          * Update the selection: | ||||
|          */ | ||||
|  | ||||
|         $query->take($parameters['length']); | ||||
|         if ($parameters['start'] > 0) { | ||||
|             $query->skip($parameters['start']); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Input search parameters: | ||||
|          */ | ||||
|         $filtered = $count; | ||||
|         if (strlen($parameters['search']['value']) > 0) { | ||||
|             $query->where('transaction_journals.description', 'LIKE', '%' . e($parameters['search']['value']) . '%'); | ||||
|             $filtered = $query->count(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Build return array: | ||||
|          */ | ||||
|         $data = [ | ||||
|             'draw' => $parameters['draw'], | ||||
|             'recordsTotal' => $count, | ||||
|             'recordsFiltered' => $filtered, | ||||
|             'data' => [], | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         /* | ||||
|          * Get paginated result set: | ||||
|          */ | ||||
|         if ($parameters['orderOnAccount'] === true) { | ||||
|             /** @var Collection $set */ | ||||
|             $set = $query->get( | ||||
|                 [ | ||||
|                     'transaction_journals.*', | ||||
|                     't1.amount', | ||||
|                     't1.account_id AS from_id', | ||||
|                     'a1.name AS from', | ||||
|                     't2.account_id AS to_id', | ||||
|                     'a2.name AS to', | ||||
|                 ] | ||||
|             ); | ||||
|         } else { | ||||
|             /** @var Collection $set */ | ||||
|             $set = $query->get( | ||||
|                 [ | ||||
|                     'transaction_journals.*', | ||||
|                     'transactions.amount', | ||||
|                 ] | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Loop set and create entries to return. | ||||
|          */ | ||||
|         /** @var \TransactionJournal $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $from      = $entry->transactions[0]->account; | ||||
|             $to        = $entry->transactions[1]->account; | ||||
|             $budget    = $entry->budgets()->first(); | ||||
|             $category  = $entry->categories()->first(); | ||||
|             $recurring = $entry->recurringTransaction()->first(); | ||||
|             $arr       = [ | ||||
|                 'date' => $entry->date->format('j F Y'), | ||||
|                 'description' => [ | ||||
|                     'description' => $entry->description, | ||||
|                     'url' => route('transactions.show', $entry->id) | ||||
|                 ], | ||||
|                 'amount' => floatval($entry->amount), | ||||
|                 'from' => ['name' => $from->name, 'url' => route('accounts.show', $from->id)], | ||||
|                 'to' => ['name' => $to->name, 'url' => route('accounts.show', $to->id)], | ||||
|                 'components' => [ | ||||
|                     'budget_id' => 0, | ||||
|                     'budget_url' => '', | ||||
|                     'budget_name' => '', | ||||
|                     'category_id' => 0, | ||||
|                     'category_url' => '', | ||||
|                     'category_name' => '' | ||||
|                 ], | ||||
|                 'id' => [ | ||||
|                     'edit' => route('transactions.edit', $entry->id), | ||||
|                     'delete' => route('transactions.delete', $entry->id) | ||||
|                 ] | ||||
|             ]; | ||||
|             if ($budget) { | ||||
|                 $arr['components']['budget_id']   = $budget->id; | ||||
|                 $arr['components']['budget_name'] = $budget->name; | ||||
|                 $arr['components']['budget_url']  = route('budgets.show', $budget->id); | ||||
|             } | ||||
|             if ($category) { | ||||
|                 $arr['components']['category_id']   = $category->id; | ||||
|                 $arr['components']['category_name'] = $category->name; | ||||
|                 $arr['components']['category_url']  = route('categories.show', $category->id); | ||||
|             } | ||||
|             if ($recurring) { | ||||
|                 $arr['components']['recurring_id']   = $recurring->id; | ||||
|                 $arr['components']['recurring_name'] = e($recurring->name); | ||||
|                 $arr['components']['recurring_url']  = route('recurring.show', $recurring->id); | ||||
|             } | ||||
|  | ||||
|             $data['data'][] = $arr; | ||||
|  | ||||
|         } | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Builds most of the query required to grab transaction journals from the database. | ||||
|      * This is useful because all three pages showing different kinds of transactions use | ||||
|      * the exact same query with only slight differences. | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      * | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function journalQuery(array $parameters) | ||||
|     { | ||||
|         /* | ||||
|          * We need the following vars to fine tune the query: | ||||
|          */ | ||||
|         if ($parameters['amount'] == 'negative') { | ||||
|             $operator        = '<'; | ||||
|             $operatorNegated = '>'; | ||||
|             $function        = 'lessThan'; | ||||
|         } else { | ||||
|             $operator        = '>'; | ||||
|             $operatorNegated = '<'; | ||||
|             $function        = 'moreThan'; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Build query: | ||||
|          */ | ||||
|         $query = \TransactionJournal::transactionTypes($parameters['transactionTypes'])->withRelevantData(); | ||||
|         $query->where('user_id', \Auth::user()->id); | ||||
|         $query->where('completed', 1); | ||||
|         /* | ||||
|          * This is complex. Join `transactions` twice, once for the "to" account and once for the | ||||
|          * "from" account. Then get the amount from one of these (depends on type). | ||||
|          * | ||||
|          * Only need to do this when there's a sort order for "from" or "to". | ||||
|          * | ||||
|          * Also need the table prefix for this to work. | ||||
|          */ | ||||
|         if ($parameters['orderOnAccount'] === true) { | ||||
|             $connection = \Config::get('database.default'); | ||||
|             $prefix     = \Config::get('database.connections.' . $connection . '.prefix'); | ||||
|             // left join first table for "from" account: | ||||
|             $query->leftJoin( | ||||
|                 'transactions AS ' . $prefix . 't1', function ($join) use ($operator) { | ||||
|                     $join->on('t1.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                          ->on('t1.amount', $operator, \DB::Raw(0)); | ||||
|                 } | ||||
|             ); | ||||
|             // left join second table for "to" account: | ||||
|             $query->leftJoin( | ||||
|                 'transactions AS ' . $prefix . 't2', function ($join) use ($operatorNegated) { | ||||
|                     $join->on('t2.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                          ->on('t2.amount', $operatorNegated, \DB::Raw(0)); | ||||
|                 } | ||||
|             ); | ||||
|  | ||||
|             // also join accounts twice to get the account's name, which we need for sorting. | ||||
|             $query->leftJoin('accounts as ' . $prefix . 'a1', 'a1.id', '=', 't1.account_id'); | ||||
|             $query->leftJoin('accounts as ' . $prefix . 'a2', 'a2.id', '=', 't2.account_id'); | ||||
|         } else { | ||||
|             // less complex | ||||
|             $query->$function(0); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Add sort parameters to query: | ||||
|          */ | ||||
|         if (isset($parameters['order']) && count($parameters['order']) > 0) { | ||||
|             foreach ($parameters['order'] as $order) { | ||||
|                 $query->orderBy($order['name'], $order['dir']); | ||||
|             } | ||||
|         } else { | ||||
|             $query->defaultSorting(); | ||||
|         } | ||||
|         return $query; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Do some sorting, counting and ordering on the query and return a nicely formatted array | ||||
|      * that can be used by the DataTables JQuery plugin. | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      * @param Builder $query | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function recurringTransactionsDataset(array $parameters, Builder $query) | ||||
|     { | ||||
|         /* | ||||
|  * Count query: | ||||
|  */ | ||||
|         $count = $query->count(); | ||||
|  | ||||
|         /* | ||||
|          * Update the selection: | ||||
|          */ | ||||
|  | ||||
|         $query->take($parameters['length']); | ||||
|         if ($parameters['start'] > 0) { | ||||
|             $query->skip($parameters['start']); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Input search parameters: | ||||
|          */ | ||||
|         $filtered = $count; | ||||
|         if (strlen($parameters['search']['value']) > 0) { | ||||
|             $query->where('recurring_transactions.description', 'LIKE', '%' . e($parameters['search']['value']) . '%'); | ||||
|             $filtered = $query->count(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Build return array: | ||||
|          */ | ||||
|         $data = [ | ||||
|             'draw' => $parameters['draw'], | ||||
|             'recordsTotal' => $count, | ||||
|             'recordsFiltered' => $filtered, | ||||
|             'data' => [], | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         /* | ||||
|          * Get paginated result set: | ||||
|          */ | ||||
|         /** @var Collection $set */ | ||||
|         $set = $query->get( | ||||
|             [ | ||||
|                 'recurring_transactions.*', | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         /* | ||||
|          * Loop set and create entries to return. | ||||
|          */ | ||||
|         foreach ($set as $entry) { | ||||
|             $set = [ | ||||
|  | ||||
|                 'name' => ['name' => $entry->name, 'url' => route('recurring.show', $entry->id)], | ||||
|                 'match' => explode(' ', $entry->match), | ||||
|                 'amount_max' => floatval($entry->amount_max), | ||||
|                 'amount_min' => floatval($entry->amount_min), | ||||
|                 'date' => $entry->date->format('j F Y'), | ||||
|                 'active' => intval($entry->active), | ||||
|                 'automatch' => intval($entry->automatch), | ||||
|                 'repeat_freq' => $entry->repeat_freq, | ||||
|                 'id' => [ | ||||
|                     'edit' => route('recurring.edit', $entry->id), | ||||
|                     'delete' => route('recurring.delete', $entry->id) | ||||
|                 ] | ||||
|             ]; | ||||
|             if (intval($entry->skip) > 0) { | ||||
|                 $set['repeat_freq'] = $entry->repeat_freq . ' (skip ' . $entry->skip . ')'; | ||||
|             } | ||||
|             $data['data'][] = $set; | ||||
|  | ||||
|  | ||||
|         } | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a query that will pick up all recurring transactions from the database. | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      * | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function recurringTransactionsQuery(array $parameters) | ||||
|     { | ||||
|         $query = \RecurringTransaction::where('user_id', \Auth::user()->id); | ||||
|  | ||||
|         if (isset($parameters['order']) && count($parameters['order']) > 0) { | ||||
|             foreach ($parameters['order'] as $order) { | ||||
|                 $query->orderBy($order['name'], $order['dir']); | ||||
|             } | ||||
|         } else { | ||||
|             $query->orderBy('name', 'ASC'); | ||||
|         } | ||||
|         return $query; | ||||
|     } | ||||
| }  | ||||
							
								
								
									
										64
									
								
								app/lib/Firefly/Helper/Controllers/JsonInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								app/lib/Firefly/Helper/Controllers/JsonInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
| use LaravelBook\Ardent\Builder; | ||||
|  | ||||
| /** | ||||
|  * Interface JsonInterface | ||||
|  * | ||||
|  * @package Firefly\Helper\Controllers | ||||
|  */ | ||||
| interface JsonInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * Grabs all the parameters entered by the DataTables JQuery plugin and creates | ||||
|      * a nice array to be used by the other methods. It's also cleaning up and what-not. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function dataTableParameters(); | ||||
|  | ||||
|     /** | ||||
|      * Do some sorting, counting and ordering on the query and return a nicely formatted array | ||||
|      * that can be used by the DataTables JQuery plugin. | ||||
|      * | ||||
|      * @param array   $parameters | ||||
|      * @param Builder $query | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function journalDataset(array $parameters, Builder $query); | ||||
|  | ||||
|     /** | ||||
|      * Builds most of the query required to grab transaction journals from the database. | ||||
|      * This is useful because all three pages showing different kinds of transactions use | ||||
|      * the exact same query with only slight differences. | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      * | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function journalQuery(array $parameters); | ||||
|  | ||||
|     /** | ||||
|      * Do some sorting, counting and ordering on the query and return a nicely formatted array | ||||
|      * that can be used by the DataTables JQuery plugin. | ||||
|      * | ||||
|      * @param array   $parameters | ||||
|      * @param Builder $query | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function recurringTransactionsDataset(array $parameters, Builder $query); | ||||
|  | ||||
|     /** | ||||
|      * Create a query that will pick up all recurring transactions from the database. | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      * | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function recurringTransactionsQuery(array $parameters); | ||||
| }  | ||||
							
								
								
									
										120
									
								
								app/lib/Firefly/Helper/Controllers/Recurring.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								app/lib/Firefly/Helper/Controllers/Recurring.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Exception; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| class Recurring implements RecurringInterface | ||||
| { | ||||
|     /** | ||||
|      * Returns messages about the validation. | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function validate(array $data) | ||||
|     { | ||||
|         $errors = new MessageBag; | ||||
|         $warnings = new MessageBag; | ||||
|         $successes = new MessageBag; | ||||
|  | ||||
|         /* | ||||
|          * Name: | ||||
|          */ | ||||
|         if (strlen($data['name']) == 0) { | ||||
|             $errors->add('name', 'The name should not be this short.'); | ||||
|         } | ||||
|         if (strlen($data['name']) > 250) { | ||||
|             $errors->add('name', 'The name should not be this long.'); | ||||
|         } | ||||
|         if (!   isset($data['id'])) { | ||||
|             $count = \Auth::user()->recurringtransactions()->whereName($data['name'])->count(); | ||||
|         } else { | ||||
|             $count = \Auth::user()->recurringtransactions()->whereName($data['name'])->where('id', '!=', $data['id'])->count(); | ||||
|         } | ||||
|         if ($count > 0) { | ||||
|             $errors->add('name', 'A recurring transaction with this name already exists.'); | ||||
|         } | ||||
|         if (count($errors->get('name')) == 0) { | ||||
|             $successes->add('name', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Match | ||||
|          */ | ||||
|         if (count(explode(',', $data['match'])) > 10) { | ||||
|             $warnings->add('match', 'This many matches is pretty pointless'); | ||||
|         } | ||||
|         if (strlen($data['match']) == 0) { | ||||
|             $errors->add('match', 'Cannot match on nothing.'); | ||||
|         } | ||||
|         if (count($errors->get('match')) == 0) { | ||||
|             $successes->add('match', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Amount | ||||
|          */ | ||||
|         if (floatval($data['amount_max']) == 0 && floatval($data['amount_min']) == 0) { | ||||
|             $errors->add('amount_min', 'Amount max and min cannot both be zero.'); | ||||
|             $errors->add('amount_max', 'Amount max and min cannot both be zero.'); | ||||
|         } | ||||
|  | ||||
|         if (floatval($data['amount_max']) < floatval($data['amount_min'])) { | ||||
|             $errors->add('amount_max', 'Amount max must be more than amount min.'); | ||||
|         } | ||||
|  | ||||
|         if (floatval($data['amount_min']) > floatval($data['amount_max'])) { | ||||
|             $errors->add('amount_max', 'Amount min must be less than amount max.'); | ||||
|         } | ||||
|         if (count($errors->get('amount_min')) == 0) { | ||||
|             $successes->add('amount_min', 'OK!'); | ||||
|         } | ||||
|         if (count($errors->get('amount_max')) == 0) { | ||||
|             $successes->add('amount_max', 'OK!'); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Date | ||||
|          */ | ||||
|         try { | ||||
|             $date = new Carbon($data['date']); | ||||
|         } catch (Exception $e) { | ||||
|             $errors->add('date', 'The date entered was invalid'); | ||||
|         } | ||||
|         if (strlen($data['date']) == 0) { | ||||
|             $errors->add('date', 'The date entered was invalid'); | ||||
|         } | ||||
|         if (!$errors->has('date')) { | ||||
|             $successes->add('date', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         $successes->add('active', 'OK!'); | ||||
|         $successes->add('automatch', 'OK!'); | ||||
|  | ||||
|         if (intval($data['skip']) < 0) { | ||||
|             $errors->add('skip', 'Cannot be below zero.'); | ||||
|         } else if (intval($data['skip']) > 31) { | ||||
|             $errors->add('skip', 'Cannot be above 31.'); | ||||
|         } | ||||
|         if (count($errors->get('skip')) == 0) { | ||||
|             $successes->add('skip', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         $set = \Config::get('firefly.budget_periods'); | ||||
|         if (!in_array($data['repeat_freq'], $set)) { | ||||
|             $errors->add('repeat_freq', 'Invalid value.'); | ||||
|         } | ||||
|         if (count($errors->get('repeat_freq')) == 0) { | ||||
|             $successes->add('repeat_freq', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; | ||||
|  | ||||
|     } | ||||
| }  | ||||
							
								
								
									
										15
									
								
								app/lib/Firefly/Helper/Controllers/RecurringInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/lib/Firefly/Helper/Controllers/RecurringInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
|  | ||||
| interface RecurringInterface { | ||||
|     /** | ||||
|      * Returns messages about the validation. | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function validate(array $data); | ||||
| }  | ||||
							
								
								
									
										101
									
								
								app/lib/Firefly/Helper/Controllers/Search.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								app/lib/Firefly/Helper/Controllers/Search.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| <?php | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class Search | ||||
|  * | ||||
|  * @package Firefly\Helper\Controllers | ||||
|  */ | ||||
| class Search implements SearchInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function searchTransactions(array $words) | ||||
|     { | ||||
|         return \Auth::user()->transactionjournals()->withRelevantData()->where( | ||||
|             function ($q) use ($words) { | ||||
|                 foreach ($words as $word) { | ||||
|                     $q->orWhere('description', 'LIKE', '%' . e($word) . '%'); | ||||
|                 } | ||||
|             } | ||||
|         )->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function searchAccounts(array $words) | ||||
|     { | ||||
|         return \Auth::user()->accounts()->with('accounttype')->where( | ||||
|             function ($q) use ($words) { | ||||
|                 foreach ($words as $word) { | ||||
|                     $q->orWhere('name', 'LIKE', '%' . e($word) . '%'); | ||||
|                 } | ||||
|             } | ||||
|         )->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function searchCategories(array $words) | ||||
|     { | ||||
|         /** @var Collection $set */ | ||||
|         $set = \Auth::user()->categories()->get(); | ||||
|         $newSet = $set->filter( | ||||
|             function (\Category $c) use ($words) { | ||||
|                 $found = 0; | ||||
|                 foreach ($words as $word) { | ||||
|                     if (!(strpos(strtolower($c->name), strtolower($word)) === false)) { | ||||
|                         $found++; | ||||
|                     } | ||||
|                 } | ||||
|                 return $found > 0; | ||||
|             } | ||||
|         ); | ||||
|         return $newSet; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function searchBudgets(array $words) | ||||
|     { | ||||
|         /** @var Collection $set */ | ||||
|         $set = \Auth::user()->budgets()->get(); | ||||
|         $newSet = $set->filter( | ||||
|             function (\Budget $b) use ($words) { | ||||
|                 $found = 0; | ||||
|                 foreach ($words as $word) { | ||||
|                     if (!(strpos(strtolower($b->name), strtolower($word)) === false)) { | ||||
|                         $found++; | ||||
|                     } | ||||
|                 } | ||||
|                 return $found > 0; | ||||
|             } | ||||
|         ); | ||||
|         return $newSet; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function searchTags(array $words) | ||||
|     { | ||||
|         return new Collection; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										36
									
								
								app/lib/Firefly/Helper/Controllers/SearchInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/lib/Firefly/Helper/Controllers/SearchInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
| /** | ||||
|  * Interface SearchInterface | ||||
|  * | ||||
|  * @package Firefly\Helper\Controllers | ||||
|  */ | ||||
| interface SearchInterface | ||||
| { | ||||
|     /** | ||||
|      * @param array $words | ||||
|      */ | ||||
|     public function searchTransactions(array $words); | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      */ | ||||
|     public function searchAccounts(array $words); | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      */ | ||||
|     public function searchCategories(array $words); | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      */ | ||||
|     public function searchBudgets(array $words); | ||||
|  | ||||
|     /** | ||||
|      * @param array $words | ||||
|      */ | ||||
|     public function searchTags(array $words); | ||||
|  | ||||
| } | ||||
							
								
								
									
										485
									
								
								app/lib/Firefly/Helper/Controllers/Transaction.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										485
									
								
								app/lib/Firefly/Helper/Controllers/Transaction.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,485 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Controllers; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Exception; | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Firefly\Storage\Account\AccountRepositoryInterface as ARI; | ||||
| use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI; | ||||
| use Firefly\Storage\Category\CategoryRepositoryInterface as CRI; | ||||
| use Firefly\Storage\Piggybank\PiggybankRepositoryInterface as PRI; | ||||
| use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Class Transaction | ||||
|  * | ||||
|  * @package Firefly\Helper\Controllers | ||||
|  */ | ||||
| class Transaction implements TransactionInterface | ||||
| { | ||||
|     protected $_user = null; | ||||
|  | ||||
|     /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $_journals */ | ||||
|     protected $_journals; | ||||
|  | ||||
|     /** @var \Firefly\Storage\Category\CategoryRepositoryInterface $_categories */ | ||||
|     protected $_categories; | ||||
|  | ||||
|     /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $_budgets */ | ||||
|     protected $_budgets; | ||||
|  | ||||
|     /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $_piggybanks */ | ||||
|     protected $_piggybanks; | ||||
|  | ||||
|     /** @var \Firefly\Storage\Account\AccountRepositoryInterface $_accounts */ | ||||
|     protected $_accounts; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param TJRI $journals | ||||
|      * @param CRI $categories | ||||
|      * @param BRI $budgets | ||||
|      * @param PRI $piggybanks | ||||
|      * @param ARI $accounts | ||||
|      */ | ||||
|     public function __construct(TJRI $journals, CRI $categories, BRI $budgets, PRI $piggybanks, ARI $accounts) | ||||
|     { | ||||
|         $this->_journals = $journals; | ||||
|         $this->_categories = $categories; | ||||
|         $this->_budgets = $budgets; | ||||
|         $this->_piggybanks = $piggybanks; | ||||
|         $this->_accounts = $accounts; | ||||
|         $this->overruleUser(\Auth::user()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * | ||||
|      * @return mixed|void | ||||
|      */ | ||||
|     public function overruleUser(\User $user) | ||||
|     { | ||||
|         $this->_user = $user; | ||||
|         $this->_journals->overruleUser($user); | ||||
|         $this->_categories->overruleUser($user); | ||||
|         $this->_budgets->overruleUser($user); | ||||
|         $this->_piggybanks->overruleUser($user); | ||||
|         $this->_accounts->overruleUser($user); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \TransactionJournal $journal | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return MessageBag|\TransactionJournal | ||||
|      */ | ||||
|     public function update(\TransactionJournal $journal, array $data) | ||||
|     { | ||||
|         /* | ||||
|          * Update the journal using the repository. | ||||
|          */ | ||||
|         $journal = $this->_journals->update($journal, $data); | ||||
|  | ||||
|         /* | ||||
|          * If invalid, return the message bag: | ||||
|          */ | ||||
|         if (!$journal->validate()) { | ||||
|             return $journal->errors(); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * find budget using repository | ||||
|          */ | ||||
|  | ||||
|         if (isset($data['budget_id'])) { | ||||
|             $budget = $this->_budgets->find($data['budget_id']); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * find category using repository | ||||
|          */ | ||||
|         $category = $this->_categories->firstOrCreate($data['category']); | ||||
|  | ||||
|         /* | ||||
|          * Find piggy bank using repository: | ||||
|          */ | ||||
|         $piggybank = null; | ||||
|         if (isset($data['piggybank_id'])) { | ||||
|             $piggybank = $this->_piggybanks->find($data['piggybank_id']); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * save accounts using repositories | ||||
|          * this depends on the kind of transaction and i've yet to fix this. | ||||
|          */ | ||||
|  | ||||
|         if (isset($data['account_id'])) { | ||||
|             $from = $this->_accounts->findAssetAccountById($data['account_id']); | ||||
|         } | ||||
|         if (isset($data['expense_account'])) { | ||||
|             $to = $this->_accounts->findExpenseAccountByName($data['expense_account']); | ||||
|         } | ||||
|         if (isset($data['revenue_account'])) { | ||||
|             $from = $this->_accounts->findRevenueAccountByName($data['revenue_account']); | ||||
|             $to = $this->_accounts->findAssetAccountById($data['account_id']); | ||||
|         } | ||||
|         if (isset($data['account_from_id'])) { | ||||
|             $from = $this->_accounts->findAssetAccountById($data['account_from_id']); | ||||
|         } | ||||
|         if (isset($data['account_to_id'])) { | ||||
|             $to = $this->_accounts->findAssetAccountById($data['account_to_id']); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Add a custom error when they are the same. | ||||
|          */ | ||||
|         if ($to->id == $from->id) { | ||||
|             $bag = new MessageBag; | ||||
|             $bag->add('account_from_id', 'The account from cannot be the same as the account to.'); | ||||
|             return $bag; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Check if the transactions need new data: | ||||
|          */ | ||||
|         $transactions = $journal->transactions()->orderBy('amount', 'ASC')->get(); | ||||
|         /** @var \Transaction $transaction */ | ||||
|         foreach ($transactions as $index => $transaction) { | ||||
|             switch (true) { | ||||
|                 case ($index == 0): // FROM account | ||||
|                     $transaction->account()->associate($from); | ||||
|                     $transaction->amount = floatval($data['amount']) * -1; | ||||
|                     break; | ||||
|                 case ($index == 1): // TO account. | ||||
|                     $transaction->account()->associate($to); | ||||
|                     $transaction->amount = floatval($data['amount']); | ||||
|                     break; | ||||
|             } | ||||
|             $transaction->save(); | ||||
|             // either way, try to attach the piggy bank: | ||||
|             if (!is_null($piggybank)) { | ||||
|                 if ($piggybank->account_id == $transaction->account_id) { | ||||
|                     $transaction->piggybank()->associate($piggybank); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Connect budget and category: | ||||
|          */ | ||||
|         $budgetids = !isset($budget) || (isset($budget) && is_null($budget)) ? [] : [$budget->id]; | ||||
|         $catids = is_null($category) ? [] : [$category->id]; | ||||
|         $components = array_merge($budgetids,$catids); | ||||
|         $journal->components()->sync($components); | ||||
|         $journal->save(); | ||||
|  | ||||
|         if (isset($data['return_journal']) && $data['return_journal'] == true) { | ||||
|             return $journal; | ||||
|         } | ||||
|         return $journal->errors(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns messages about the validation. | ||||
|      * | ||||
|      * @param array $data | ||||
|      * @return array | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function validate(array $data) | ||||
|     { | ||||
|         $errors = new MessageBag; | ||||
|         $warnings = new MessageBag; | ||||
|         $successes = new MessageBag; | ||||
|  | ||||
|         /* | ||||
|          * Description: | ||||
|          */ | ||||
|         if (strlen($data['description']) == 0) { | ||||
|             $errors->add('description', 'The description should not be this short.'); | ||||
|         } | ||||
|         if (strlen($data['description']) > 250) { | ||||
|             $errors->add('description', 'The description should not be this long.'); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Amount | ||||
|          */ | ||||
|         if (floatval($data['amount']) <= 0) { | ||||
|             $errors->add('amount', 'The amount cannot be zero or less than zero.'); | ||||
|         } | ||||
|         if (floatval($data['amount']) > 10000) { | ||||
|             $warnings->add('amount', 'OK, but that\'s a lot of money dude.'); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Date | ||||
|          */ | ||||
|         try { | ||||
|             $date = new Carbon($data['date']); | ||||
|         } catch (Exception $e) { | ||||
|             $errors->add('date', 'The date entered was invalid'); | ||||
|         } | ||||
|         if (strlen($data['date']) == 0) { | ||||
|             $errors->add('date', 'The date entered was invalid'); | ||||
|         } | ||||
|         if (!$errors->has('date')) { | ||||
|             $successes->add('date', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Category | ||||
|          */ | ||||
|         $category = $this->_categories->findByName($data['category']); | ||||
|         if (strlen($data['category']) == 0) { | ||||
|             $warnings->add('category', 'No category will be created.'); | ||||
|         } else { | ||||
|             if (is_null($category)) { | ||||
|                 $warnings->add('category', 'Will have to be created.'); | ||||
|             } else { | ||||
|                 $successes->add('category', 'OK!'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         switch ($data['what']) { | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot validate a ' . $data['what']); | ||||
|                 break; | ||||
|             case 'deposit': | ||||
|                 /* | ||||
|                  * Tests for deposit | ||||
|                  */ | ||||
|                 // asset account | ||||
|                 $accountId = isset($data['account_id']) ? intval($data['account_id']) : 0; | ||||
|                 $account = $this->_accounts->find($accountId); | ||||
|                 if (is_null($account)) { | ||||
|                     $errors->add('account_id', 'Cannot find this asset account.'); | ||||
|                 } else { | ||||
|                     $successes->add('account_id', 'OK!'); | ||||
|                 } | ||||
|  | ||||
|                 // revenue account: | ||||
|                 if (strlen($data['revenue_account']) == 0) { | ||||
|                     $warnings->add('revenue_account', 'Revenue account will be "cash".'); | ||||
|                 } else { | ||||
|                     $exp = $this->_accounts->findRevenueAccountByName($data['revenue_account'], false); | ||||
|                     if (is_null($exp)) { | ||||
|                         $warnings->add('revenue_account', 'Expense account will be created.'); | ||||
|                     } else { | ||||
|                         $successes->add('revenue_account', 'OK!'); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|             case 'transfer': | ||||
|                 // account from | ||||
|                 $accountId = isset($data['account_from_id']) ? intval($data['account_from_id']) : 0; | ||||
|                 $account = $this->_accounts->find($accountId); | ||||
|                 if (is_null($account)) { | ||||
|                     $errors->add('account_from_id', 'Cannot find this asset account.'); | ||||
|                 } else { | ||||
|                     $successes->add('account_from_id', 'OK!'); | ||||
|                 } | ||||
|                 unset($accountId); | ||||
|                 // account to | ||||
|                 $accountId = isset($data['account_to_id']) ? intval($data['account_to_id']) : 0; | ||||
|                 $account = $this->_accounts->find($accountId); | ||||
|                 if (is_null($account)) { | ||||
|                     $errors->add('account_to_id', 'Cannot find this asset account.'); | ||||
|                 } else { | ||||
|                     $successes->add('account_to_id', 'OK!'); | ||||
|                 } | ||||
|                 unset($accountId); | ||||
|  | ||||
|                 // piggy bank | ||||
|                 $piggybankId = isset($data['piggybank_id']) ? intval($data['piggybank_id']) : 0; | ||||
|                 $piggybank = $this->_piggybanks->find($piggybankId); | ||||
|                 if (is_null($piggybank)) { | ||||
|                     $warnings->add('piggybank_id', 'No piggy bank will be modified.'); | ||||
|                 } else { | ||||
|                     $successes->add('piggybank_id', 'OK!'); | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|             case 'withdrawal': | ||||
|                 /* | ||||
|                  * Tests for withdrawal | ||||
|                  */ | ||||
|                 // asset account | ||||
|                 $accountId = isset($data['account_id']) ? intval($data['account_id']) : 0; | ||||
|                 $account = $this->_accounts->find($accountId); | ||||
|                 if (is_null($account)) { | ||||
|                     $errors->add('account_id', 'Cannot find this asset account.'); | ||||
|                 } else { | ||||
|                     $successes->add('account_id', 'OK!'); | ||||
|                 } | ||||
|  | ||||
|                 // expense account | ||||
|                 if (strlen($data['expense_account']) == 0) { | ||||
|                     $warnings->add('expense_account', 'Expense account will be "cash".'); | ||||
|                 } else { | ||||
|                     $exp = $this->_accounts->findExpenseAccountByName($data['expense_account'], false); | ||||
|                     if (is_null($exp)) { | ||||
|                         $warnings->add('expense_account', 'Expense account will be created.'); | ||||
|                     } else { | ||||
|                         $successes->add('expense_account', 'OK!'); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // budget | ||||
|                 if (!isset($data['budget_id']) || (isset($data['budget_id']) && intval($data['budget_id']) == 0)) { | ||||
|                     $warnings->add('budget_id', 'No budget selected.'); | ||||
|                 } else { | ||||
|                     $budget = $this->_budgets->find(intval($data['budget_id'])); | ||||
|                     if (is_null($budget)) { | ||||
|                         $errors->add('budget_id', 'This budget does not exist'); | ||||
|                     } else { | ||||
|                         $successes->add('budget_id', 'OK!'); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         if (count($errors->get('description')) == 0) { | ||||
|             $successes->add('description', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         if (count($errors->get('amount')) == 0) { | ||||
|             $successes->add('amount', 'OK!'); | ||||
|         } | ||||
|  | ||||
|         return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; | ||||
|         /* | ||||
|          * Tests for deposit | ||||
|          */ | ||||
|         /* | ||||
|          * Tests for transfer | ||||
|          */ | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store a full transaction journal and associated stuff | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return MessageBag|\TransactionJournal | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.ShortVariable) | ||||
|      */ | ||||
|     public function store(array $data) | ||||
|     { | ||||
|         /* | ||||
|          * save journal using repository | ||||
|          */ | ||||
|         $journal = $this->_journals->store($data); | ||||
|  | ||||
|         /* | ||||
|          * If invalid, return the message bag: | ||||
|          */ | ||||
|         if (!$journal->validate()) { | ||||
|             return $journal->errors(); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * find budget using repository | ||||
|          */ | ||||
|         if (isset($data['budget_id'])) { | ||||
|             $budget = $this->_budgets->find($data['budget_id']); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * find category using repository | ||||
|          */ | ||||
|         $category = $this->_categories->firstOrCreate($data['category']); | ||||
|  | ||||
|         /* | ||||
|          * Find piggy bank using repository: | ||||
|          */ | ||||
|         $piggybank = null; | ||||
|         if (isset($data['piggybank_id'])) { | ||||
|             $piggybank = $this->_piggybanks->find($data['piggybank_id']); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * save accounts using repositories | ||||
|          * this depends on the kind of transaction and i've yet to fix this. | ||||
|          */ | ||||
|         if (isset($data['account_id'])) { | ||||
|             $from = $this->_accounts->findAssetAccountById($data['account_id']); | ||||
|         } | ||||
|         if (isset($data['expense_account'])) { | ||||
|             $to = $this->_accounts->findExpenseAccountByName($data['expense_account']); | ||||
|  | ||||
|         } | ||||
|         if (isset($data['revenue_account'])) { | ||||
|             $from = $this->_accounts->findRevenueAccountByName($data['revenue_account']); | ||||
|             $to = $this->_accounts->findAssetAccountById($data['account_id']); | ||||
|         } | ||||
|         if (isset($data['account_from_id'])) { | ||||
|             $from = $this->_accounts->findAssetAccountById($data['account_from_id']); | ||||
|         } | ||||
|         if (isset($data['account_to_id'])) { | ||||
|             $to = $this->_accounts->findAssetAccountById($data['account_to_id']); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Add a custom error when they are the same. | ||||
|          */ | ||||
|         if ($to->id == $from->id) { | ||||
|             $bag = new MessageBag; | ||||
|             $bag->add('account_from_id', 'The account from cannot be the same as the account to.'); | ||||
|             return $bag; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Save transactions using repository. We try to connect the (possibly existing) | ||||
|          * piggy bank to either transaction, knowing it will only work with one of them. | ||||
|          */ | ||||
|         /** @var \Transaction $one */ | ||||
|         $one = $this->_journals->saveTransaction($journal, $from, floatval($data['amount']) * -1); | ||||
|         $one->connectPiggybank($piggybank); | ||||
|         $two = $this->_journals->saveTransaction($journal, $to, floatval($data['amount'])); | ||||
|         $two->connectPiggybank($piggybank); | ||||
|         /* | ||||
|          * Count for $journal is zero? Then there were errors! | ||||
|          */ | ||||
|         if ($journal->transactions()->count() < 2) { | ||||
|             /* | ||||
|              * Join message bags and return them: | ||||
|              */ | ||||
|             $bag = $one->errors(); | ||||
|             $bag->merge($two->errors()); | ||||
|             return $bag; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Connect budget, category and piggy bank: | ||||
|          */ | ||||
|         if (isset($budget) && !is_null($budget)) { | ||||
|             $journal->budgets()->save($budget); | ||||
|         } | ||||
|         if (!is_null($category)) { | ||||
|             $journal->categories()->save($category); | ||||
|         } | ||||
|         $journal->completed = true; | ||||
|         $journal->save(); | ||||
|  | ||||
|         /* | ||||
|          * Trigger recurring transaction event. | ||||
|          */ | ||||
|         \Event::fire('journals.store',[$journal]); | ||||
|  | ||||
|         if (isset($data['return_journal']) && $data['return_journal'] == true) { | ||||
|             return ['journal' => $journal, 'messagebag' => $journal->errors()]; | ||||
|         } | ||||
|         return $journal->errors(); | ||||
|     } | ||||
|  | ||||
| }  | ||||
							
								
								
									
										48
									
								
								app/lib/Firefly/Helper/Controllers/TransactionInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								app/lib/Firefly/Helper/Controllers/TransactionInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Controllers; | ||||
| use Illuminate\Support\MessageBag; | ||||
|  | ||||
| /** | ||||
|  * Interface TransactionInterface | ||||
|  * | ||||
|  * @package Firefly\Helper\Controllers | ||||
|  */ | ||||
| interface TransactionInterface { | ||||
|  | ||||
|     /** | ||||
|      * Store a full transaction journal and associated stuff | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return MessageBag|\TransactionJournal | ||||
|      */ | ||||
|     public function store(array $data); | ||||
|  | ||||
|     /** | ||||
|      * Returns messages about the validation. | ||||
|      * | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function validate(array $data); | ||||
|  | ||||
|     /** | ||||
|      * @param \TransactionJournal $journal | ||||
|      * @param array               $data | ||||
|      * | ||||
|      * @return MessageBag|\TransactionJournal | ||||
|      */ | ||||
|     public function update(\TransactionJournal $journal, array $data); | ||||
|  | ||||
|     /** | ||||
|      * Overrule the user used when the class is created. | ||||
|      * | ||||
|      * @param \User $user | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function overruleUser(\User $user); | ||||
|  | ||||
| }  | ||||
| @@ -38,7 +38,7 @@ class EmailHelper implements EmailHelperInterface | ||||
|     { | ||||
|  | ||||
|         $password = \Str::random(12); | ||||
|         $user->password = \Hash::make($password); | ||||
|         $user->password = $password; | ||||
|         $user->reset = \Str::random(32); // new one. | ||||
|         $user->forceSave(); | ||||
|         $email = $user->email; | ||||
|   | ||||
| @@ -1,47 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Form; | ||||
|  | ||||
| /** | ||||
|  * Class FormHelper | ||||
|  * | ||||
|  * @package Firefly\Form | ||||
|  */ | ||||
| class FormHelper | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param null $value | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function budget($value = null) | ||||
|     { | ||||
|  | ||||
|         $str = '<select name="budget_id" class="form-control">'; | ||||
|  | ||||
|         $str .= '<option value="0" label="(no budget)"'; | ||||
|         if (is_null($value) || intval($value) == 0) { | ||||
|             $str .= ' selected="selected"'; | ||||
|         } | ||||
|         $str .= '</option>'; | ||||
|  | ||||
|         /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgets */ | ||||
|         $budgets = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); | ||||
|         $list = $budgets->getAsSelectList(); | ||||
|         foreach ($list as $id => $name) { | ||||
|             $str .= '<option value="' . e($id) . '" label="' . e($name) . '"'; | ||||
|             if ($id == intval($value)) { | ||||
|                 $str .= ' selected="selected"'; | ||||
|             } | ||||
|             $str .= '>' . e($name) . '</option>'; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $str .= '</select>'; | ||||
|  | ||||
|         return $str; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Form; | ||||
|  | ||||
| use Illuminate\Events\Dispatcher; | ||||
|  | ||||
| /** | ||||
|  * Class FormTrigger | ||||
|  * | ||||
|  * @package Firefly\Helper\Form | ||||
|  */ | ||||
| class FormTrigger | ||||
| { | ||||
|  | ||||
|     public function registerFormExtensions() | ||||
|     { | ||||
|         \Form::macro( | ||||
|             'budget', function () { | ||||
|                 $helper = new FormHelper; | ||||
|  | ||||
|                 return $helper->budget(); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Dispatcher $events | ||||
|      */ | ||||
|     public function subscribe(Dispatcher $events) | ||||
|     { | ||||
|         $events->listen('laravel.booted', 'Firefly\Helper\Form\FormTrigger@registerFormExtensions'); | ||||
|  | ||||
|     } | ||||
|  | ||||
| }  | ||||
| @@ -26,6 +26,27 @@ class HelperServiceProvider extends ServiceProvider | ||||
|             'Firefly\Helper\Controllers\ChartInterface', | ||||
|             'Firefly\Helper\Controllers\Chart' | ||||
|         ); | ||||
|  | ||||
|         $this->app->bind( | ||||
|             'Firefly\Helper\Controllers\JsonInterface', | ||||
|             'Firefly\Helper\Controllers\Json' | ||||
|         ); | ||||
|  | ||||
|         $this->app->bind( | ||||
|             'Firefly\Helper\Controllers\RecurringInterface', | ||||
|             'Firefly\Helper\Controllers\Recurring' | ||||
|         ); | ||||
|  | ||||
|         $this->app->bind( | ||||
|             'Firefly\Helper\Controllers\SearchInterface', | ||||
|             'Firefly\Helper\Controllers\Search' | ||||
|         ); | ||||
|  | ||||
|         $this->app->bind( | ||||
|             'Firefly\Helper\Controllers\TransactionInterface', | ||||
|             'Firefly\Helper\Controllers\Transaction' | ||||
|         ); | ||||
|  | ||||
|         $this->app->bind( | ||||
|             'Firefly\Helper\Controllers\CategoryInterface', | ||||
|             'Firefly\Helper\Controllers\Category' | ||||
|   | ||||
| @@ -1,327 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Migration; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Firefly\Exception\FireflyException; | ||||
|  | ||||
| /** | ||||
|  * Class MigrationHelper | ||||
|  * | ||||
|  * @package Firefly\Helper\Migration | ||||
|  */ | ||||
| class MigrationHelper implements MigrationHelperInterface | ||||
| { | ||||
|     protected $path; | ||||
|     protected $JSON; | ||||
|     protected $map = []; | ||||
|  | ||||
|     /** | ||||
|      * @param $path | ||||
|      * | ||||
|      * @return mixed|void | ||||
|      */ | ||||
|     public function loadFile($path) | ||||
|     { | ||||
|         $this->path = $path; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function validFile() | ||||
|     { | ||||
|         // file does not exist: | ||||
|         if (!file_exists($this->path)) { | ||||
|             \Log::error('Migration file ' . $this->path . ' does not exist!'); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // load the content: | ||||
|         $content = file_get_contents($this->path); | ||||
|         if ($content === false) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // parse the content | ||||
|         $this->JSON = json_decode($content); | ||||
|         if (is_null($this->JSON)) { | ||||
|             return false; | ||||
|         } | ||||
|         \Log::info('Migration file ' . $this->path . ' is valid!'); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function migrate() | ||||
|     { | ||||
|         \Log::info('Start of migration.'); | ||||
|         \DB::beginTransaction(); | ||||
|  | ||||
|         try { | ||||
|             // create cash account: | ||||
|             $this->_createCashAccount(); | ||||
|  | ||||
|             $this->_importAccounts(); | ||||
|             $this->_importComponents(); | ||||
|             //$this->_importPiggybanks(); | ||||
|  | ||||
|             // create transactions: | ||||
|             $this->_importTransactions(); | ||||
|  | ||||
|             // create transfers: | ||||
|             $this->_importTransfers(); | ||||
|  | ||||
|             // create limits: | ||||
|             $this->_importLimits(); | ||||
|  | ||||
|  | ||||
|         } catch (FireflyException $e) { | ||||
|             \DB::rollBack(); | ||||
|             \Log::error('Rollback because of error!'); | ||||
|             \Log::error($e->getMessage()); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         \DB::commit(); | ||||
|         \Log::info('Done!'); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     protected function _createCashAccount() | ||||
|     { | ||||
|         $cashAT = \AccountType::where('description', 'Cash account')->first(); | ||||
|         /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */ | ||||
|         $accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|         $cash = $accounts->store(['name' => 'Cash account', 'account_type' => $cashAT, 'active' => 0]); | ||||
|         \Log::info('Created cash account (#' . $cash->id . ')'); | ||||
|         $this->map['cash'] = $cash; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     protected function _importAccounts() | ||||
|     { | ||||
|  | ||||
|         /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */ | ||||
|         $accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|         \Log::info('Going to import ' . count($this->JSON->accounts) . ' accounts.'); | ||||
|         foreach ($this->JSON->accounts as $entry) { | ||||
|             // create account: | ||||
|             if ($entry->openingbalance == 0) { | ||||
|                 $account = $accounts->store(['name' => $entry->name]); | ||||
|             } else { | ||||
|                 $account = $accounts->storeWithInitialBalance( | ||||
|                     ['name' => $entry->name], | ||||
|                     new Carbon($entry->openingbalancedate), | ||||
|                     floatval($entry->openingbalance) | ||||
|                 ); | ||||
|             } | ||||
|             $this->map['accounts'][$entry->id] = $account; | ||||
|             \Log::info('Imported account "' . $entry->name . '" with balance ' . $entry->openingbalance); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     protected function _importComponents() | ||||
|     { | ||||
|         $beneficiaryAT = \AccountType::where('description', 'Beneficiary account')->first(); | ||||
|         foreach ($this->JSON->components as $entry) { | ||||
|             switch ($entry->type->type) { | ||||
|                 case 'beneficiary': | ||||
|                     /** @noinspection PhpParamsInspection */ | ||||
|                     $beneficiary = $this->_importBeneficiary($entry, $beneficiaryAT); | ||||
|                     $this->map['accounts'][$entry->id] = $beneficiary; | ||||
|                     break; | ||||
|                 case 'category': | ||||
|                     $component = $this->_importCategory($entry); | ||||
|                     $this->map['categories'][$entry->id] = $component; | ||||
|                     break; | ||||
|                 case 'budget': | ||||
|                     $component = $this->_importBudget($entry); | ||||
|                     $this->map['budgets'][$entry->id] = $component; | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param              $component | ||||
|      * @param \AccountType $beneficiaryAT | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function _importBeneficiary($component, \AccountType $beneficiaryAT) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */ | ||||
|         $accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|  | ||||
|         return $accounts->store( | ||||
|             [ | ||||
|                 'name'         => $component->name, | ||||
|                 'account_type' => $beneficiaryAT | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $component | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function _importCategory($component) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Component\ComponentRepositoryInterface $components */ | ||||
|         $components = \App::make('Firefly\Storage\Component\ComponentRepositoryInterface'); | ||||
|  | ||||
|         return $components->store(['name' => $component->name, 'class' => 'Category']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $component | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function _importBudget($component) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Component\ComponentRepositoryInterface $components */ | ||||
|         $components = \App::make('Firefly\Storage\Component\ComponentRepositoryInterface'); | ||||
|  | ||||
|         return $components->store(['name' => $component->name, 'class' => 'Budget']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     protected function _importTransactions() | ||||
|     { | ||||
|  | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ | ||||
|         $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|  | ||||
|         // loop component_transaction to find beneficiaries, categories and budgets: | ||||
|         $beneficiaries = []; | ||||
|         $categories = []; | ||||
|         $budgets = []; | ||||
|         foreach ($this->JSON->component_transaction as $entry) { | ||||
|             // beneficiaries | ||||
|             if (isset($this->map['accounts'][$entry->component_id])) { | ||||
|                 $beneficiaries[$entry->transaction_id] = $this->map['accounts'][$entry->component_id]; | ||||
|             } | ||||
|  | ||||
|             // categories | ||||
|             if (isset($this->map['categories'][$entry->component_id])) { | ||||
|                 $categories[$entry->transaction_id] = $this->map['categories'][$entry->component_id]; | ||||
|             } | ||||
|  | ||||
|             // budgets: | ||||
|             if (isset($this->map['budgets'][$entry->component_id])) { | ||||
|                 $budgets[$entry->transaction_id] = $this->map['budgets'][$entry->component_id]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         foreach ($this->JSON->transactions as $entry) { | ||||
|  | ||||
|             // to properly save the amount, do it times -1: | ||||
|             $amount = $entry->amount * -1; | ||||
|  | ||||
|             /** @var \Account $fromAccount */ | ||||
|             $fromAccount = isset($this->map['accounts'][$entry->account_id]) | ||||
|                 ? $this->map['accounts'][$entry->account_id] : false; | ||||
|  | ||||
|             /** @var \Account $toAccount */ | ||||
|             $toAccount = isset($beneficiaries[$entry->id]) ? $beneficiaries[$entry->id] : $this->map['cash']; | ||||
|  | ||||
|             $date = new Carbon($entry->date); | ||||
|             $journal = $journals->createSimpleJournal($fromAccount, $toAccount, $entry->description, $amount, $date); | ||||
|  | ||||
|             // save budgets and categories, on the journal | ||||
|             if (isset($budgets[$entry->id])) { | ||||
|                 $budget = $budgets[$entry->id]; | ||||
|                 $journal->budgets()->save($budget); | ||||
|             } | ||||
|             if (isset($categories[$entry->id])) { | ||||
|                 $category = $categories[$entry->id]; | ||||
|                 $journal->categories()->save($category); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     protected function _importTransfers() | ||||
|     { | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ | ||||
|         $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|  | ||||
|         foreach ($this->JSON->transfers as $entry) { | ||||
|  | ||||
|             // to properly save the amount, do it times 1 (?): | ||||
|             $amount = $entry->amount * -1; | ||||
|  | ||||
|             /** @var \Account $fromAccount */ | ||||
|             $fromAccount = isset($this->map['accounts'][$entry->accountfrom_id]) | ||||
|                 ? $this->map['accounts'][$entry->accountto_id] : false; | ||||
|  | ||||
|             /** @var \Account $toAccount */ | ||||
|             $toAccount = isset($this->map['accounts'][$entry->accountto_id]) | ||||
|                 ? $this->map['accounts'][$entry->accountfrom_id] : false; | ||||
|  | ||||
|             $date = new Carbon($entry->date); | ||||
|             $journals->createSimpleJournal($fromAccount, $toAccount, $entry->description, $amount, $date); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     protected function _importLimits() | ||||
|     { | ||||
|         \Log::info('Importing limits'); | ||||
|         foreach ($this->JSON->limits as $entry) { | ||||
|             \Log::debug( | ||||
|                 'Now at #' . $entry->id . ': EUR ' . $entry->amount . ' for month ' . $entry->date | ||||
|                 . ' and componentID: ' . $entry->component_id | ||||
|             ); | ||||
|             $budget = isset($this->map['budgets'][$entry->component_id]) ? $this->map['budgets'][$entry->component_id] | ||||
|                 : null; | ||||
|             if (!is_null($budget)) { | ||||
|                 \Log::debug('Found budget for this limit: #' . $budget->id . ', ' . $budget->name); | ||||
|  | ||||
|                 $limit = new \Limit; | ||||
|                 $limit->budget()->associate($budget); | ||||
|                 $limit->startdate = new Carbon($entry->date); | ||||
|                 $limit->amount = floatval($entry->amount); | ||||
|                 $limit->repeats = 0; | ||||
|                 $limit->repeat_freq = 'monthly'; | ||||
|                 try { | ||||
|                     $limit->save(); | ||||
|                 } catch (\Exception $e) { | ||||
|                 } | ||||
|             } else { | ||||
|                 \Log::warning('No budget for this limit!'); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             // create repeat thing should not be necessary. | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Helper\Migration; | ||||
|  | ||||
| /** | ||||
|  * Interface MigrationHelperInterface | ||||
|  * | ||||
|  * @package Firefly\Helper\Migration | ||||
|  */ | ||||
| interface MigrationHelperInterface | ||||
| { | ||||
|     /** | ||||
|      * @param $path | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function loadFile($path); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function validFile(); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function migrate(); | ||||
|  | ||||
| }  | ||||
| @@ -3,140 +3,130 @@ | ||||
| namespace Firefly\Helper\Toolkit; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Http\Request; | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class Toolkit | ||||
|  * | ||||
|  * @package Firefly\Helper\Toolkit | ||||
|  * @SuppressWarnings(PHPMD.CamelCaseMethodName) | ||||
|  * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) | ||||
|  */ | ||||
| class Toolkit implements ToolkitInterface | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Request $request | ||||
|      * Lots of code in Firefly III still depends on session['start'], session['end'] and | ||||
|      * session['range'] to be available, even though this feature has been removed from Firefly | ||||
|      * in favor of a new reporting feature. This reporting feature can show the user past and future | ||||
|      * date ranges instead of the dashboard (the dashboard always shows "right now"). | ||||
|      * | ||||
|      * @return \Illuminate\Http\RedirectResponse|mixed|null | ||||
|      * The only actual choice the user is left with is the range, which can be changed using the Preferences pane. | ||||
|      * | ||||
|      * The start/end dates are set here, regardless of what the user might want to see. | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getDateRange(Request $request) | ||||
|     public function getDateRange() | ||||
|     { | ||||
|         /* | ||||
|          * Get all data from the session: | ||||
|          */ | ||||
|         $range = $this->_getRange(); | ||||
|         $start = $this->_getStartDate(); | ||||
|         $end = $this->_getEndDate(); | ||||
|         #\Log::debug('Range is: ' . $range); | ||||
|         $start = \Session::has('start') ? \Session::get('start') : new Carbon; | ||||
|  | ||||
|         // update start only: | ||||
|         #\Log::debug('Session start is: ' . $start->format('Y-m-d')); | ||||
|         $end = \Session::has('end') ? \Session::get('end') : new Carbon; | ||||
|         #\Log::debug('Session end is  : ' . $end->format('Y-m-d')); | ||||
|  | ||||
|         /* | ||||
|          * Force start date to at the start of the $range. | ||||
|          * Ie. the start of the week, month, year. | ||||
|          */ | ||||
|         $start = $this->_updateStartDate($range, $start); | ||||
|         #\Log::debug('After update, session start is: ' . $start->format('Y-m-d')); | ||||
|  | ||||
|         // update end only: | ||||
|         $end = $this->_updateEndDate($range, $start, $end); | ||||
|         /* | ||||
|          * Force end date to at the END of the $range. Always based on $start. | ||||
|          * Ie. the END of the week, month, year. | ||||
|          */ | ||||
|         $end = $this->_updateEndDate($range, $start); | ||||
|         #\Log::debug('After update, session end is  : ' . $end->format('Y-m-d')); | ||||
|  | ||||
|         if (\Input::get('action') == 'prev') { | ||||
|             $start = $this->_moveStartPrevious($range, $start); | ||||
|             $end = $this->_moveEndPrevious($range, $end); | ||||
|         } | ||||
|         if (\Input::get('action') == 'next') { | ||||
|             $start = $this->_moveStartNext($range, $start); | ||||
|             $end = $this->_moveEndNext($range, $end); | ||||
|         } | ||||
|         /* | ||||
|          * get the name of the month, depending on the range. Purely for astetics | ||||
|          */ | ||||
|         $period = $this->_periodName($range, $start); | ||||
|  | ||||
|         // save in session: | ||||
|         /* | ||||
|          * Get the date for the previous and next period. | ||||
|          * Ie. next week, next month, etc. | ||||
|          */ | ||||
|         $prev = $this->_previous($range, clone $start); | ||||
|         $next = $this->_next($range, clone $start); | ||||
|  | ||||
|         /* | ||||
|          * Save everything in the session: | ||||
|          */ | ||||
|         \Session::put('start', $start); | ||||
|         \Session::put('end', $end); | ||||
|         \Session::put('range', $range); | ||||
|         if (!is_null(\Input::get('action'))) { | ||||
|             return \Redirect::to($request->url()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         \Session::put('period', $period); | ||||
|         \Session::put('prev', $this->_periodName($range, $prev)); | ||||
|         \Session::put('next', $this->_periodName($range, $next)); | ||||
|         return null; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      * | ||||
|      */ | ||||
|     public function getDateRangeDates() | ||||
|     public function checkImportJobs() | ||||
|     { | ||||
|         return [\Session::get('start'), \Session::get('end')]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getReminders() | ||||
|     { | ||||
|         // get reminders, for menu, mumble mumble: | ||||
|         $today = new Carbon; | ||||
|         $reminders = \Auth::user()->reminders()->where('class', 'PiggybankReminder')->validOn($today)->get(); | ||||
|  | ||||
|         /** @var \Reminder $reminder */ | ||||
|         foreach ($reminders as $index => $reminder) { | ||||
|             if (\Session::has('dismissal-' . $reminder->id)) { | ||||
|                 $time = \Session::get('dismissal-' . $reminder->id); | ||||
|                 if ($time >= $today) { | ||||
|                     unset($reminders[$index]); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         \Session::put('reminderCount', count($reminders)); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function _getrange() | ||||
|     { | ||||
|         if (!is_null(\Input::get('range'))) { | ||||
|             $range = \Input::get('range'); | ||||
|         /* | ||||
|          * Get all jobs. | ||||
|          */ | ||||
|         /** @var \Importmap $importJob */ | ||||
|         $importJob = \Importmap::where('user_id', \Auth::user()->id) | ||||
|                                ->where('totaljobs', '>', \DB::Raw('`jobsdone`')) | ||||
|                                ->orderBy('created_at', 'DESC') | ||||
|                                ->first(); | ||||
|         if (!is_null($importJob)) { | ||||
|             $diff  = intval($importJob->totaljobs) - intval($importJob->jobsdone); | ||||
|             $date  = new Carbon; | ||||
|             $today = new Carbon; | ||||
|             $date->addSeconds($diff); | ||||
|             \Session::put('job_pct', $importJob->pct()); | ||||
|             \Session::put('job_text', $date->diffForHumans()); | ||||
|         } else { | ||||
|             if (!is_null(\Session::get('range'))) { | ||||
|                 $range = \Session::get('range'); | ||||
|             } else { | ||||
|                 /** @noinspection PhpUndefinedClassInspection */ | ||||
|                 $preferences = \App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); | ||||
|                 $viewRange = $preferences->get('viewRange', '1M'); | ||||
|  | ||||
|                 // default range: | ||||
|                 $range = $viewRange->data; | ||||
|             } | ||||
|             \Session::forget('job_pct'); | ||||
|             \Session::forget('job_text'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function _getRange() | ||||
|     { | ||||
|         if (!is_null(\Session::get('range'))) { | ||||
|             $range = \Session::get('range'); | ||||
|         } else { | ||||
|             /** @noinspection PhpUndefinedClassInspection */ | ||||
|             $preferences = \App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); | ||||
|             $viewRange   = $preferences->get('viewRange', '1M'); | ||||
|  | ||||
|             // default range: | ||||
|             $range = $viewRange->data; | ||||
|             \Session::put('range', $range); | ||||
|         } | ||||
|         return $range; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Carbon|mixed | ||||
|      */ | ||||
|     protected function _getStartDate() | ||||
|     { | ||||
|         $start = \Session::has('start') ? \Session::get('start') : new Carbon; | ||||
|         if (\Input::get('start') && \Input::get('end')) { | ||||
|             $start = new Carbon(\Input::get('start')); | ||||
|         } | ||||
|  | ||||
|         return $start; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Carbon|mixed | ||||
|      */ | ||||
|     protected function _getEndDate() | ||||
|     { | ||||
|         $end = \Session::has('end') ? \Session::get('end') : new Carbon; | ||||
|         if (\Input::get('start') && \Input::get('end')) { | ||||
|             $end = new Carbon(\Input::get('end')); | ||||
|         } | ||||
|  | ||||
|         return $end; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param        $range | ||||
|      * @param Carbon $start | ||||
| @@ -145,7 +135,6 @@ class Toolkit implements ToolkitInterface | ||||
|      */ | ||||
|     protected function _updateStartDate($range, Carbon $start) | ||||
|     { | ||||
|         $today = new Carbon; | ||||
|         switch ($range) { | ||||
|             case '1D': | ||||
|                 $start->startOfDay(); | ||||
| @@ -160,12 +149,15 @@ class Toolkit implements ToolkitInterface | ||||
|                 $start->firstOfQuarter(); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 if (intval($today->format('m')) >= 7) { | ||||
|                 if (intval($start->format('m')) >= 7) { | ||||
|                     $start->startOfYear()->addMonths(6); | ||||
|                 } else { | ||||
|                     $start->startOfYear(); | ||||
|                 } | ||||
|                 break; | ||||
|             case '1Y': | ||||
|                 $start->startOfYear(); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return $start; | ||||
| @@ -179,150 +171,337 @@ class Toolkit implements ToolkitInterface | ||||
|      * | ||||
|      * @return Carbon | ||||
|      */ | ||||
|     protected function _updateEndDate($range, Carbon $start, Carbon $end) | ||||
|     protected function _updateEndDate($range, Carbon $start) | ||||
|     { | ||||
|         $today = new Carbon; | ||||
|         $end = clone $start; | ||||
|         switch ($range) { | ||||
|             default: | ||||
|                 throw new FireflyException('_updateEndDate cannot handle $range ' . $range); | ||||
|                 break; | ||||
|             case '1D': | ||||
|                 $end = clone $start; | ||||
|                 $end->endOfDay(); | ||||
|                 break; | ||||
|             case '1W': | ||||
|                 $end = clone $start; | ||||
|                 $end->endOfWeek(); | ||||
|                 break; | ||||
|             case '1M': | ||||
|                 $end = clone $start; | ||||
|                 $end->endOfMonth(); | ||||
|                 break; | ||||
|             case '3M': | ||||
|                 $end = clone $start; | ||||
|                 $end->lastOfQuarter(); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 $end = clone $start; | ||||
|                 if (intval($today->format('m')) >= 7) { | ||||
|                 if (intval($start->format('m')) >= 7) { | ||||
|                     $end->endOfYear(); | ||||
|                 } else { | ||||
|                     $end->startOfYear()->addMonths(6); | ||||
|                 } | ||||
|                 break; | ||||
|             case '1Y': | ||||
|                 $end->endOfYear(); | ||||
|                 break; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return $end; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param        $range | ||||
|      * @param Carbon $start | ||||
|      * | ||||
|      * @return Carbon | ||||
|      */ | ||||
|     protected function _moveStartPrevious($range, Carbon $start) | ||||
|     protected function _periodName($range, Carbon $date) | ||||
|     { | ||||
|         switch ($range) { | ||||
|             default: | ||||
|                 throw new FireflyException('No _periodName() for range "' . $range . '"'); | ||||
|                 break; | ||||
|             case '1D': | ||||
|                 return $date->format('jS F Y'); | ||||
|                 break; | ||||
|             case '1W': | ||||
|                 return 'week ' . $date->format('W, Y'); | ||||
|                 break; | ||||
|             case '1M': | ||||
|                 return $date->format('F Y'); | ||||
|                 break; | ||||
|             case '3M': | ||||
|                 $month = intval($date->format('m')); | ||||
|                 return 'Q' . ceil(($month / 12) * 4) . ' ' . $date->format('Y'); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 $month    = intval($date->format('m')); | ||||
|                 $half     = ceil(($month / 12) * 2); | ||||
|                 $halfName = $half == 1 ? 'first' : 'second'; | ||||
|                 return $halfName . ' half of ' . $date->format('d-m-Y'); | ||||
|                 break; | ||||
|             case '1Y': | ||||
|                 return $date->format('Y'); | ||||
|                 break; | ||||
|  | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function _previous($range, Carbon $date) | ||||
|     { | ||||
|         switch ($range) { | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot do _previous() on ' . $range); | ||||
|                 break; | ||||
|             case '1D': | ||||
|                 $date->startOfDay()->subDay(); | ||||
|                 break; | ||||
|             case '1W': | ||||
|                 $date->startOfWeek()->subWeek(); | ||||
|                 break; | ||||
|             case '1M': | ||||
|                 $date->startOfMonth()->subMonth(); | ||||
|                 break; | ||||
|             case '3M': | ||||
|                 $date->firstOfQuarter()->subMonths(3)->firstOfQuarter(); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 $month = intval($date->format('m')); | ||||
|                 if ($month <= 6) { | ||||
|                     $date->startOfYear()->subMonths(6); | ||||
|                 } else { | ||||
|                     $date->startOfYear(); | ||||
|                 } | ||||
|                 break; | ||||
|             case '1Y': | ||||
|                 $date->startOfYear()->subYear(); | ||||
|                 break; | ||||
|  | ||||
|         } | ||||
|         return $date; | ||||
|     } | ||||
|  | ||||
|     protected function _next($range, Carbon $date) | ||||
|     { | ||||
|         switch ($range) { | ||||
|             case '1D': | ||||
|                 $start->subDay(); | ||||
|                 $date->endOfDay()->addDay(); | ||||
|                 break; | ||||
|             case '1W': | ||||
|                 $start->subWeek(); | ||||
|                 $date->endOfWeek()->addDay()->startOfWeek(); | ||||
|                 break; | ||||
|             case '1M': | ||||
|                 $start->subMonth(); | ||||
|                 $date->endOfMonth()->addDay()->startOfMonth(); | ||||
|                 break; | ||||
|             case '3M': | ||||
|                 $start->subMonths(3)->firstOfQuarter(); | ||||
|                 $date->lastOfQuarter()->addDay(); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 $start->subMonths(6); | ||||
|                 if (intval($date->format('m')) >= 7) { | ||||
|                     $date->startOfYear()->addYear(); | ||||
|                 } else { | ||||
|                     $date->startOfYear()->addMonths(6); | ||||
|                 } | ||||
|                 break; | ||||
|             case '1Y': | ||||
|                 $date->startOfYear()->addYear(); | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot do _next() on ' . $range); | ||||
|                 break; | ||||
|         } | ||||
|         return $start; | ||||
|         return $date; | ||||
|     } | ||||
|  | ||||
|     public function next() | ||||
|     { | ||||
|         /* | ||||
|          * Get the start date and the range from the session | ||||
|          */ | ||||
|         $range = $this->_getRange(); | ||||
|         $start = \Session::get('start'); | ||||
|  | ||||
|         /* | ||||
|          * Add some period to $start. | ||||
|          */ | ||||
|         $next = $this->_next($range, clone $start); | ||||
|  | ||||
|         /* | ||||
|          * Save in session: | ||||
|          */ | ||||
|         \Session::put('start', $next); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function prev() | ||||
|     { | ||||
|         /* | ||||
|          * Get the start date and the range from the session | ||||
|          */ | ||||
|         $range = $this->_getRange(); | ||||
|         $start = \Session::get('start'); | ||||
|  | ||||
|         /* | ||||
|          * Substract some period to $start. | ||||
|          */ | ||||
|         $prev = $this->_previous($range, clone $start); | ||||
|  | ||||
|         /* | ||||
|          * Save in session: | ||||
|          */ | ||||
|         \Session::put('start', $prev); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param        $range | ||||
|      * @param Carbon $end | ||||
|      * Takes any collection and tries to make a sensible select list compatible array of it. | ||||
|      * | ||||
|      * @return Carbon | ||||
|      * @param Collection $set | ||||
|      * @param null $titleField | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function _moveEndPrevious($range, Carbon $end) | ||||
|     public function makeSelectList(Collection $set, $titleField = null) | ||||
|     { | ||||
|         switch ($range) { | ||||
|             case '1D': | ||||
|                 $end->subDay(); | ||||
|                 break; | ||||
|             case '1W': | ||||
|                 $end->subWeek(); | ||||
|                 break; | ||||
|             case '1M': | ||||
|                 $end->startOfMonth()->subMonth()->endOfMonth(); | ||||
|                 break; | ||||
|             case '3M': | ||||
|                 $end->subMonths(3)->lastOfQuarter(); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 $end->subMonths(6); | ||||
|                 break; | ||||
|         } | ||||
|         return $end; | ||||
|         $selectList = []; | ||||
|         /** @var Model $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $id    = intval($entry->id); | ||||
|             $title = null; | ||||
|             if (is_null($titleField)) { | ||||
|                 // try 'title' field. | ||||
|                 if (isset($entry->title)) { | ||||
|                     $title = $entry->title; | ||||
|                 } | ||||
|                 // try 'name' field | ||||
|                 if (is_null($title)) { | ||||
|                     $title = $entry->name; | ||||
|                 } | ||||
|  | ||||
|                 // try 'description' field | ||||
|                 if (is_null($title)) { | ||||
|                     $title = $entry->description; | ||||
|                 } | ||||
|             } else { | ||||
|                 $title = $entry->$titleField; | ||||
|             } | ||||
|             $selectList[$id] = $title; | ||||
|         } | ||||
|         return $selectList; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param        $range | ||||
|      * @param Carbon $start | ||||
|      * | ||||
|      * @return Carbon | ||||
|      * @param string $start | ||||
|      * @param string $end | ||||
|      * @param int $steps | ||||
|      */ | ||||
|     protected function _moveStartNext($range, Carbon $start) | ||||
|     public function colorRange($start, $end, $steps = 5) | ||||
|     { | ||||
|         switch ($range) { | ||||
|             case '1D': | ||||
|                 $start->addDay(); | ||||
|                 break; | ||||
|             case '1W': | ||||
|                 $start->addWeek(); | ||||
|                 break; | ||||
|             case '1M': | ||||
|                 $start->addMonth(); | ||||
|                 break; | ||||
|             case '3M': | ||||
|                 $start->addMonths(3)->firstOfQuarter(); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 $start->addMonths(6); | ||||
|                 break; | ||||
|         if (strlen($start) != 6) { | ||||
|             throw new FireflyException('Start, ' . e($start) . ' should be a six character HTML colour.'); | ||||
|         } | ||||
|         return $start; | ||||
|         if (strlen($end) != 6) { | ||||
|             throw new FireflyException('End, ' . e($end) . ' should be a six character HTML colour.'); | ||||
|         } | ||||
|         if ($steps < 1) { | ||||
|             throw new FireflyException('Steps must be > 1'); | ||||
|         } | ||||
|  | ||||
|         $start = '#' . $start; | ||||
|         $end   = '#' . $end; | ||||
|         /* | ||||
|          * Split html colours. | ||||
|          */ | ||||
|         list($rs, $gs, $bs) = sscanf($start, "#%02x%02x%02x"); | ||||
|         list($re, $ge, $be) = sscanf($end, "#%02x%02x%02x"); | ||||
|  | ||||
|         $stepr = ($re - $rs) / $steps; | ||||
|         $stepg = ($ge - $gs) / $steps; | ||||
|         $stepb = ($be - $bs) / $steps; | ||||
|  | ||||
|         $return = []; | ||||
|         for ($i = 0; $i <= $steps; $i++) { | ||||
|             $cr = $rs + ($stepr * $i); | ||||
|             $cg = $gs + ($stepg * $i); | ||||
|             $cb = $bs + ($stepb * $i); | ||||
|  | ||||
|             $return[] = $this->rgb2html($cr, $cg, $cb); | ||||
|         } | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     protected function rgb2html($r, $g = -1, $b = -1) | ||||
|     { | ||||
|         $r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r)); | ||||
|         $g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g)); | ||||
|         $b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b)); | ||||
|  | ||||
|         $color = (strlen($r) < 2 ? '0' : '') . $r; | ||||
|         $color .= (strlen($g) < 2 ? '0' : '') . $g; | ||||
|         $color .= (strlen($b) < 2 ? '0' : '') . $b; | ||||
|         return '#' . $color; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param        $range | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return Carbon | ||||
|      * @param Carbon $currentEnd | ||||
|      * @param $repeatFreq | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     protected function _moveEndNext($range, Carbon $end) | ||||
|     public function endOfPeriod(Carbon $currentEnd, $repeatFreq) | ||||
|     { | ||||
|         switch ($range) { | ||||
|             case '1D': | ||||
|                 $end->addDay(); | ||||
|         switch ($repeatFreq) { | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq); | ||||
|                 break; | ||||
|             case '1W': | ||||
|                 $end->addWeek(); | ||||
|             case 'daily': | ||||
|                 $currentEnd->addDay(); | ||||
|                 break; | ||||
|             case '1M': | ||||
|                 $end->addMonth(); | ||||
|             case 'weekly': | ||||
|                 $currentEnd->addWeek()->subDay(); | ||||
|                 break; | ||||
|             case '3M': | ||||
|                 $end->addMonths(6)->lastOfQuarter(); | ||||
|             case 'monthly': | ||||
|                 $currentEnd->addMonth()->subDay(); | ||||
|                 break; | ||||
|             case '6M': | ||||
|                 $end->addMonths(6); | ||||
|             case 'quarterly': | ||||
|                 $currentEnd->addMonths(3)->subDay(); | ||||
|                 break; | ||||
|             case 'half-year': | ||||
|                 $currentEnd->addMonths(6)->subDay(); | ||||
|                 break; | ||||
|             case 'yearly': | ||||
|                 $currentEnd->addYear()->subDay(); | ||||
|                 break; | ||||
|         } | ||||
|         return $end; | ||||
|     } | ||||
|  | ||||
| }  | ||||
|     /** | ||||
|      * @param Carbon $date | ||||
|      * @param $repeatFreq | ||||
|      * @param $skip | ||||
|      * @return Carbon | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function addPeriod(Carbon $date, $repeatFreq, $skip) | ||||
|     { | ||||
|         $add = ($skip + 1); | ||||
|         switch ($repeatFreq) { | ||||
|             default: | ||||
|                 throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq); | ||||
|                 break; | ||||
|             case 'daily': | ||||
|                 $date->addDays($add); | ||||
|                 break; | ||||
|             case 'weekly': | ||||
|                 $date->addWeeks($add); | ||||
|                 break; | ||||
|             case 'monthly': | ||||
|                 $date->addMonths($add); | ||||
|                 break; | ||||
|             case 'quarterly': | ||||
|                 $months = $add * 3; | ||||
|                 $date->addMonths($months); | ||||
|                 break; | ||||
|             case 'half-year': | ||||
|                 $months = $add * 6; | ||||
|                 $date->addMonths($months); | ||||
|                 break; | ||||
|             case 'yearly': | ||||
|                 $date->addYears($add); | ||||
|                 break; | ||||
|         } | ||||
|         return $date; | ||||
|     } | ||||
| } | ||||
| @@ -2,7 +2,8 @@ | ||||
|  | ||||
| namespace Firefly\Helper\Toolkit; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface ToolkitInterface | ||||
| @@ -12,20 +13,40 @@ use Illuminate\Http\Request; | ||||
| interface ToolkitInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Request $request | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getDateRange(); | ||||
|  | ||||
|     /** | ||||
|      * Takes any collection and tries to make a sensible select list compatible array of it. | ||||
|      * | ||||
|      * @param Collection $set | ||||
|      * @param null $titleField | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getDateRange(Request $request); | ||||
|     public function makeSelectList(Collection $set, $titleField = null); | ||||
|  | ||||
|     public function next(); | ||||
|  | ||||
|     public function prev(); | ||||
|  | ||||
|     public function checkImportJobs(); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      * @param string $start | ||||
|      * @param string $end | ||||
|      * @param int $steps | ||||
|      */ | ||||
|     public function getDateRangeDates(); | ||||
|     public function colorRange($start, $end, $steps = 5); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      * @param Carbon $date | ||||
|      * @param $repeatFreq | ||||
|      * @param $skip | ||||
|      * @return Carbon | ||||
|      */ | ||||
|     public function getReminders(); | ||||
|     public function addPeriod(Carbon $date, $repeatFreq, $skip); | ||||
|  | ||||
| }  | ||||
| } | ||||
							
								
								
									
										596
									
								
								app/lib/Firefly/Queue/Import.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										596
									
								
								app/lib/Firefly/Queue/Import.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,596 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Queue; | ||||
|  | ||||
| use Illuminate\Queue\Jobs\Job; | ||||
|  | ||||
| /** | ||||
|  * Class Import | ||||
|  * | ||||
|  * @package Firefly\Queue | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  */ | ||||
| class Import | ||||
| { | ||||
|     /** @var \Firefly\Storage\Account\AccountRepositoryInterface */ | ||||
|     protected $_accounts; | ||||
|  | ||||
|     /** @var \Firefly\Storage\Import\ImportRepositoryInterface */ | ||||
|     protected $_repository; | ||||
|  | ||||
|     /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface */ | ||||
|     protected $_piggybanks; | ||||
|  | ||||
|     /** | ||||
|      * This constructs the import handler and initiates all the relevant interfaces / classes. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->_accounts   = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); | ||||
|         $this->_repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|         $this->_piggybanks = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The final step in the import routine is to get all transactions which have one of their accounts | ||||
|      * still set to "import", which means it is a cash transaction. This routine will set them all to cash instead. | ||||
|      * | ||||
|      * If there was no account present for these accounts in the import routine (no beneficiary set), Firefly | ||||
|      * II would fall back to the import account. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      */ | ||||
|     public function cleanImportAccount(Job $job, array $payload) | ||||
|     { | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $this->_repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         // two import account types. | ||||
|         $importAccountType = $this->_accounts->findAccountType('Import account'); | ||||
|         $cashAccountType   = $this->_accounts->findAccountType('Cash account'); | ||||
|  | ||||
|         // find or create import account: | ||||
|         $importAccount = $this->_accounts->firstOrCreate( | ||||
|             [ | ||||
|                 'name'            => 'Import account', | ||||
|                 'account_type_id' => $importAccountType->id, | ||||
|                 'active'          => 1, | ||||
|                 'user_id'         => $user->id, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         // find or create cash account: | ||||
|         $cashAccount = $this->_accounts->firstOrCreate( | ||||
|             [ | ||||
|                 'name'            => 'Cash account', | ||||
|                 'account_type_id' => $cashAccountType->id, | ||||
|                 'active'          => 1, | ||||
|                 'user_id'         => $user->id, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         // update all users transactions: | ||||
|         $count = \DB::table('transactions') | ||||
|             ->where('account_id', $importAccount->id)->count(); | ||||
|  | ||||
|         \DB::table('transactions') | ||||
|             ->where('account_id', $importAccount->id) | ||||
|             ->update(['account_id' => $cashAccount->id]); | ||||
|  | ||||
|         \Log::debug('Updated ' . $count . ' transactions from Import Account to cash.'); | ||||
|         $job->delete(); // no count fix | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      */ | ||||
|     protected function overruleUser(\User $user) | ||||
|     { | ||||
|         $this->_accounts->overruleUser($user); | ||||
|         $this->_repository->overruleUser($user); | ||||
|         $this->_piggybanks->overruleUser($user); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This job queues new jobs that will connect components to their proper transactions and updates the | ||||
|      * expense account: categories, budgets an beneficiaries used to be components. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      */ | ||||
|     public function importComponentTransaction(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $this->_repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Took too long to fix this: | ||||
|          */ | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('Could not map transaction to component after 10 tries. KILL'); | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * Prep some vars from the payload | ||||
|          */ | ||||
|         $transactionId = intval($payload['data']['transaction_id']); | ||||
|         $componentId   = intval($payload['data']['component_id']); | ||||
|  | ||||
|         /* | ||||
|          * We don't know what kind of component we have. So we search for it. We have a specific function | ||||
|          * for this: | ||||
|          */ | ||||
|         $oldComponentMap = $this->_repository->findImportComponentMap($importMap, $componentId); | ||||
|  | ||||
|         /* | ||||
|          * If the map is null, the component (whatever it is) is not imported yet, and we release the job. | ||||
|          */ | ||||
|         if (is_null($oldComponentMap)) { | ||||
|             \Log::notice('No map for this component, release transaction/component import.'); | ||||
|  | ||||
|             /* | ||||
|              * When in sync, its pointless to release jobs. Simply remove them. | ||||
|              */ | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Switch on the class found in the map, and push a new job to update the transaction journal: | ||||
|          */ | ||||
|         switch ($oldComponentMap->class) { | ||||
|             default: | ||||
|                 \Log::error('Cannot handle "' . $oldComponentMap->class . '" in component<>transaction routine!'); | ||||
|                 $job->delete(); | ||||
|                 break; | ||||
|             case 'Budget': | ||||
|                 \Log::debug('Push job to connect budget to transaction #' . $transactionId); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Budget\BudgetRepositoryInterface@importUpdateTransaction', $payload | ||||
|                 ); | ||||
|                 $importMap->totaljobs++; | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|  | ||||
|                 $job->delete(); // count fixed | ||||
|                 break; | ||||
|             case 'Category': | ||||
|                 \Log::debug('Push job to connect category to transaction #' . $transactionId); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Category\CategoryRepositoryInterface@importUpdateTransaction', $payload | ||||
|                 ); | ||||
|  | ||||
|                 $importMap->totaljobs++; | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|  | ||||
|                 $job->delete(); // count fixed | ||||
|                 break; | ||||
|             case 'Account': | ||||
|                 \Log::debug('Push job to connect account to transaction #' . $transactionId); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Account\AccountRepositoryInterface@importUpdateTransaction', $payload | ||||
|                 ); | ||||
|  | ||||
|                 $importMap->totaljobs++; | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|  | ||||
|                 $job->delete(); // count fixed | ||||
|                 break; | ||||
|         } | ||||
|         return; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This job queues new jobs that will connect components to their proper transfers and updates the | ||||
|      * expense account: categories, budgets an beneficiaries used to be components. Even though not all | ||||
|      * of the transfers used to have these components, we check for them all. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      */ | ||||
|     public function importComponentTransfer(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $this->_repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Took too long to fix this: | ||||
|          */ | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('Could not map transaction to component after 10 tries. KILL'); | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Prep some vars from the payload | ||||
|          */ | ||||
|         $transferId  = intval($payload['data']['transfer_id']); | ||||
|         $componentId = intval($payload['data']['component_id']); | ||||
|  | ||||
|         /* | ||||
|          * We don't know what kind of component we have. So we search for it. We have a specific function | ||||
|          * for this: | ||||
|          */ | ||||
|         $oldComponentMap = $this->_repository->findImportComponentMap($importMap, $componentId); | ||||
|  | ||||
|         /* | ||||
|          * If the map is null, the component (whatever it is) is not imported yet, and we release the job. | ||||
|          */ | ||||
|         if (is_null($oldComponentMap)) { | ||||
|             \Log::notice('No map for this component, release transfer/component import.'); | ||||
|             /* | ||||
|              * When in sync, its pointless to release jobs. Simply remove them. | ||||
|             */ | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Switch on the class found in the map, and push a new job to update the transaction journal: | ||||
|          */ | ||||
|         switch ($oldComponentMap->class) { | ||||
|             default: | ||||
|                 \Log::error('Cannot handle "' . $oldComponentMap->class . '" in component<>transfer routine!'); | ||||
|                 $job->delete(); | ||||
|                 break; | ||||
|             case 'Category': | ||||
|                 \Log::debug('Push job to connect category to transfer #' . $transferId); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Category\CategoryRepositoryInterface@importUpdateTransfer', $payload | ||||
|                 ); | ||||
|  | ||||
|                 $importMap->totaljobs++; | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|  | ||||
|                 $job->delete(); // count fixed | ||||
|                 break; | ||||
|             case 'Budget': | ||||
|                 \Log::debug('Push job to connect budget to transfer #' . $transferId); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Budget\BudgetRepositoryInterface@importUpdateTransfer', $payload | ||||
|                 ); | ||||
|                 $importMap->totaljobs++; | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|  | ||||
|                 $job->delete(); // count fixed | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This job will see if the particular setting is a 'piggyAccount' setting, | ||||
|      * one we need to fix all imported piggy banks. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      */ | ||||
|     public function importSetting(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $this->_repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('No account found for piggyAccount setting after 10 tries. KILL!'); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|         $name = $payload['data']['name']; | ||||
|         switch ($name) { | ||||
|             default: | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed. | ||||
|                 return; | ||||
|                 break; | ||||
|             case 'piggyAccount': | ||||
|  | ||||
|                 /* | ||||
|                  * If user has this account, update all piggy banks: | ||||
|                  */ | ||||
|                 $accountID = intval($payload['data']['value']); | ||||
|  | ||||
|                 /* | ||||
|                  * Is account imported already? | ||||
|                  */ | ||||
|                 $importEntry = $this->_repository->findImportEntry($importMap, 'Account', $accountID); | ||||
|  | ||||
|                 /* | ||||
|                  * We imported this account already. | ||||
|                  */ | ||||
|                 if ($importEntry) { | ||||
|                     $all     = $this->_piggybanks->get(); | ||||
|                     $account = $this->_accounts->find($importEntry->new); | ||||
|                     /* | ||||
|                      * Update all piggy banks. | ||||
|                      */ | ||||
|                     if (!is_null($account)) { | ||||
|                         \Log::debug('Updating all piggybanks, found the right setting.'); | ||||
|                         foreach ($all as $piggy) { | ||||
|                             $piggy->account()->associate($account); | ||||
|                             unset($piggy->leftInAccount); | ||||
|                             $piggy->save(); | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     \Log::notice('Account not yet imported, hold or 5 minutes.'); | ||||
|                     /* | ||||
|                      * When in sync, its pointless to release jobs. Simply remove them. | ||||
|                     */ | ||||
|                     if (\Config::get('queue.default') == 'sync') { | ||||
|                         $importMap->jobsdone++; | ||||
|                         $importMap->save(); | ||||
|                         $job->delete(); // count fixed | ||||
|                     } else { | ||||
|                         $job->release(300); // proper release. | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         // update map: | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         $job->delete(); // count fixed. | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This job will loop and queue jobs for the import file; almost every set of records will be imported. | ||||
|      * | ||||
|      * @param Job $job | ||||
|      * @param     $payload | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|      */ | ||||
|     public function start(Job $job, array $payload) | ||||
|     { | ||||
|         \Log::debug('Start with job "start"'); | ||||
|         $user     = \User::find($payload['user']); | ||||
|         $filename = $payload['file']; | ||||
|         if (file_exists($filename) && !is_null($user)) { | ||||
|             /* | ||||
|              * Make an import map. Need it to refer back to import. | ||||
|              */ | ||||
|             $importMap = new \Importmap; | ||||
|             $importMap->user()->associate($user); | ||||
|             $importMap->file      = $filename; | ||||
|             $importMap->totaljobs = 0; | ||||
|             $importMap->jobsdone  = 0; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $totalJobs = 0; | ||||
|  | ||||
|             /* | ||||
|              * Loop over all data in the JSON file, then create jobs. | ||||
|              */ | ||||
|             $raw  = file_get_contents($filename); | ||||
|             $JSON = json_decode($raw); | ||||
|  | ||||
|             // first import all asset accounts: | ||||
|             foreach ($JSON->accounts as $entry) { | ||||
|                 \Log::debug('Create job to import asset account'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Account\AccountRepositoryInterface@importAccount', [ | ||||
|                         'data'         => $entry, | ||||
|                         'class'        => 'Account', | ||||
|                         'account_type' => 'Asset account', | ||||
|                         'mapID'        => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             // then import all beneficiaries: | ||||
|             foreach ($JSON->components as $entry) { | ||||
|                 if ($entry->type->type == 'beneficiary') { | ||||
|                     \Log::debug('Create job to import expense account'); | ||||
|                     \Queue::push( // count fixed | ||||
|                         'Firefly\Storage\Account\AccountRepositoryInterface@importAccount', [ | ||||
|                             'data'         => $entry, | ||||
|                             'class'        => 'Account', | ||||
|                             'account_type' => 'Expense account', | ||||
|                             'mapID'        => $importMap->id | ||||
|                         ] | ||||
|                     ); | ||||
|                     $totalJobs++; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // then import all categories. | ||||
|             foreach ($JSON->components as $entry) { | ||||
|                 if ($entry->type->type == 'category') { | ||||
|                     \Log::debug('Create job to import category'); | ||||
|                     \Queue::push( // count fixed | ||||
|                         'Firefly\Storage\Category\CategoryRepositoryInterface@importCategory', [ | ||||
|                             'data'  => $entry, | ||||
|                             'class' => 'Category', | ||||
|                             'mapID' => $importMap->id | ||||
|                         ] | ||||
|                     ); | ||||
|                     $totalJobs++; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // then import all budgets: | ||||
|             foreach ($JSON->components as $entry) { | ||||
|                 if ($entry->type->type == 'budget') { | ||||
|                     \Log::debug('Create job to import budget'); | ||||
|                     \Queue::push( // count fixed | ||||
|                         'Firefly\Storage\Budget\BudgetRepositoryInterface@importBudget', [ | ||||
|                             'data'  => $entry, | ||||
|                             'class' => 'Budget', | ||||
|                             'mapID' => $importMap->id | ||||
|                         ] | ||||
|                     ); | ||||
|                     $totalJobs++; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // then import all limits. | ||||
|             foreach ($JSON->limits as $entry) { | ||||
|                 \Log::debug('Create job to import limit'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Limit\LimitRepositoryInterface@importLimit', [ | ||||
|                         'data'  => $entry, | ||||
|                         'class' => 'Limit', | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             // all piggy banks | ||||
|             foreach ($JSON->piggybanks as $entry) { | ||||
|                 \Log::debug('Create job to import piggy bank'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\Piggybank\PiggybankRepositoryInterface@importPiggybank', [ | ||||
|                         'data'  => $entry, | ||||
|                         'class' => 'Piggybank', | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             // all predictables. | ||||
|             foreach ($JSON->predictables as $entry) { | ||||
|                 \Log::debug('Create job to import predictable'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface@importPredictable', [ | ||||
|                         'data'  => $entry, | ||||
|                         'class' => 'Predictable', | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             // all settings (to fix the piggy banks) | ||||
|             foreach ($JSON->settings as $entry) { | ||||
|                 \Log::debug('Create job to import setting'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Queue\Import@importSetting', [ | ||||
|                         'data'  => $entry, | ||||
|                         'class' => 'Setting', | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             // all transactions | ||||
|             foreach ($JSON->transactions as $entry) { | ||||
|  | ||||
|                 \Log::debug('Create job to import transaction'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface@importTransaction', [ | ||||
|                         'data'  => $entry, | ||||
|                         'class' => 'Transaction', | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|  | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             // all transfers | ||||
|             foreach ($JSON->transfers as $entry) { | ||||
|                 \Log::debug('Create job to import transfer'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface@importTransfer', [ | ||||
|                         'data'  => $entry, | ||||
|                         'class' => 'Transfer', | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             // then, fix all component <> transaction links | ||||
|             foreach ($JSON->component_transaction as $entry) { | ||||
|                 \Log::debug('Create job to import components_transaction'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Queue\Import@importComponentTransaction', | ||||
|                     [ | ||||
|                         'data'  => $entry, | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             // then, fix all component <> transfer links | ||||
|             foreach ($JSON->component_transfer as $entry) { | ||||
|                 \Log::debug('Create job to import components_transfer'); | ||||
|                 \Queue::push( // count fixed | ||||
|                     'Firefly\Queue\Import@importComponentTransfer', | ||||
|                     [ | ||||
|                         'data'  => $entry, | ||||
|                         'mapID' => $importMap->id | ||||
|                     ] | ||||
|                 ); | ||||
|                 $totalJobs++; | ||||
|             } | ||||
|  | ||||
|             $importMap->totaljobs = $totalJobs; | ||||
|             $importMap->save(); | ||||
|             /* | ||||
|              * We save the import map which now holds the number of jobs we've got planned. | ||||
|              */ | ||||
|  | ||||
|             \Queue::push('Firefly\Queue\Import@cleanImportAccount', ['mapID' => $importMap->id]); | ||||
|  | ||||
|             $job->delete(); // count fixed | ||||
|  | ||||
|             \Log::debug('Done with job "start"'); | ||||
|         } | ||||
|     } | ||||
| }  | ||||
| @@ -3,6 +3,8 @@ | ||||
|  | ||||
| namespace Firefly\Storage\Account; | ||||
|  | ||||
| use Illuminate\Queue\Jobs\Job; | ||||
|  | ||||
| /** | ||||
|  * Interface AccountRepositoryInterface | ||||
|  * | ||||
| @@ -11,25 +13,35 @@ namespace Firefly\Storage\Account; | ||||
| interface AccountRepositoryInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importAccount(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function count(); | ||||
|  | ||||
|     /** | ||||
|      * @param              $name | ||||
|      * @param \AccountType $type | ||||
|      * Gets a list of accounts that have the mentioned type. Will automatically convert | ||||
|      * strings in this array to actual (model) account types. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @param array $types | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function createOrFind($name, \AccountType $type); | ||||
|     public function getOfTypes(array $types); | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param array $data | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function createOrFindBeneficiary($name); | ||||
|     public function firstOrCreate(array $data); | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
| @@ -46,32 +58,50 @@ interface AccountRepositoryInterface | ||||
|     public function find($accountId); | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param $type | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function findByName($name); | ||||
|     public function findAccountType($type); | ||||
|  | ||||
|     /** | ||||
|      * Takes a transaction/account component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function get(); | ||||
|     public function importUpdateTransaction(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * @param $id | ||||
|      * | ||||
|      * @return |Account|null | ||||
|      */ | ||||
|     public function findAssetAccountById($id); | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param $create | ||||
|      * | ||||
|      * @return |Account|null | ||||
|      */ | ||||
|     public function findExpenseAccountByName($name, $create = true); | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param $create | ||||
|      * | ||||
|      * @return |Account|null | ||||
|      */ | ||||
|     public function findRevenueAccountByName($name, $create = true); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getActiveDefault(); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getActiveDefaultAsSelectList(); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getBeneficiaries(); | ||||
|  | ||||
|     /** | ||||
|      * @param $ids | ||||
|      * | ||||
| @@ -82,12 +112,21 @@ interface AccountRepositoryInterface | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getCashAccount(); | ||||
|     public function getDefault(); | ||||
|  | ||||
|     /** | ||||
|      * @param \AccountType $type | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getDefault(); | ||||
|     public function getByAccountType(\AccountType $type); | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function overruleUser(\User $user); | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|   | ||||
| @@ -4,6 +4,8 @@ | ||||
| namespace Firefly\Storage\Account; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Database\QueryException; | ||||
| use Illuminate\Queue\Jobs\Job; | ||||
|  | ||||
| /** | ||||
|  * Class EloquentAccountRepository | ||||
| @@ -12,11 +14,15 @@ use Carbon\Carbon; | ||||
|  */ | ||||
| class EloquentAccountRepository implements AccountRepositoryInterface | ||||
| { | ||||
|  | ||||
|     protected $_user = null; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->_user = \Auth::user(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -24,49 +30,10 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|      */ | ||||
|     public function count() | ||||
|     { | ||||
|         return \Auth::user()->accounts()->count(); | ||||
|         return $this->_user->accounts()->count(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param              $name | ||||
|      * @param \AccountType $type | ||||
|      * | ||||
|      * @return \Account|mixed | ||||
|      */ | ||||
|     public function createOrFind($name, \AccountType $type = null) | ||||
|     { | ||||
|  | ||||
|         $account = $this->findByName($name, $type); | ||||
|         if (!$account) { | ||||
|             $data = [ | ||||
|                 'name'         => $name, | ||||
|                 'account_type' => $type | ||||
|             ]; | ||||
|  | ||||
|             return $this->store($data); | ||||
|         } | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return \Account|mixed|null | ||||
|      */ | ||||
|     public function createOrFindBeneficiary($name) | ||||
|     { | ||||
|         if (is_null($name) || strlen($name) == 0) { | ||||
|             return null; | ||||
|         } | ||||
|         $type = \AccountType::where('description', 'Beneficiary account')->first(); | ||||
|  | ||||
|         /** @noinspection PhpParamsInspection */ | ||||
|  | ||||
|         return $this->createOrFind($name, $type); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * | ||||
| @@ -74,38 +41,29 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|      */ | ||||
|     public function destroy(\Account $account) | ||||
|     { | ||||
|         // find the oldest transaction which also is a "Opening balance" | ||||
|         $first = \Transaction:: | ||||
|             leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|             ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') | ||||
|             ->where('transaction_journals.user_id', \Auth::user()->id) | ||||
|             ->where('transaction_types.type', 'Opening balance') | ||||
|             ->where('account_id', '!=', $account->id) | ||||
|             ->orderBy('transactions.id', 'DESC')->first(['transactions.*']); | ||||
|         // find all transaction journals related to this account: | ||||
|         $journals = \TransactionJournal::withRelevantData()->accountIs($account)->get(['transaction_journals.*']); | ||||
|         $accountIDs = []; | ||||
|  | ||||
|         $initialbalanceAccount = null; | ||||
|         if (!is_null($first)) { | ||||
|             $initialbalanceAccount = $first->account()->first(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // loop the account, find all transaction journals, and delete them: | ||||
|         $transactions = $account->transactions()->with('transactionjournal')->get(); | ||||
|         $journals = []; | ||||
|         /** @var \Transaction $transaction */ | ||||
|         foreach ($transactions as $transaction) { | ||||
|             $journals[$transaction->transaction_journal_id] = $transaction->transactionJournal; | ||||
|         } | ||||
|         /** @var \TransactionJournal $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             // remember the account id's of the transactions involved: | ||||
|             foreach ($journal->transactions as $t) { | ||||
|                 $accountIDs[] = $t->account_id; | ||||
|             } | ||||
|             $journal->delete(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         if (!is_null($initialbalanceAccount)) { | ||||
|             $initialbalanceAccount->delete(); | ||||
|         $accountIDs = array_unique($accountIDs); | ||||
|         if (count($accountIDs) > 0) { | ||||
|             // find the "initial balance" type accounts in this list. Should be just 1. | ||||
|             $query = $this->_user->accounts()->accountTypeIn(['Initial balance account']) | ||||
|                                  ->whereIn('accounts.id', $accountIDs); | ||||
|             if ($query->count() == 1) { | ||||
|                 $iba = $query->first(['accounts.*']); | ||||
|                 $iba->delete(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         $account->delete(); | ||||
|  | ||||
|         /** | ||||
| @@ -118,80 +76,169 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $accountId | ||||
|      * @param $id | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @return |Account|null | ||||
|      */ | ||||
|     public function find($accountId) | ||||
|     public function findAssetAccountById($id) | ||||
|     { | ||||
|         return \Auth::user()->accounts()->where('id', $accountId)->first(); | ||||
|         return $this->_user->accounts()->find($id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param              $name | ||||
|      * @param \AccountType $type | ||||
|      * This method finds the expense account mentioned by name. This method is a sneaky little hobbits, | ||||
|      * because when you feed it "Import account" it will always return an import account of that type. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @param $name | ||||
|      * @param $create | ||||
|      * | ||||
|      * @return null|\Account | ||||
|      */ | ||||
|     public function findByName($name, \AccountType $type = null) | ||||
|     public function findExpenseAccountByName($name, $create = true) | ||||
|     { | ||||
|         $type = is_null($type) ? \AccountType::where('description', 'Default account')->first() : $type; | ||||
|         $cashType = $this->findAccountType('Cash account'); | ||||
|         $importType = $this->findAccountType('Import account'); | ||||
|         // catch Import account: | ||||
|         if ($name == 'Import account') { | ||||
|  | ||||
|         return \Auth::user()->accounts()->where('account_type_id', $type->id)->where('name', 'like', '%' . $name . '%') | ||||
|             ->first(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function get() | ||||
|     { | ||||
|         return \Auth::user()->accounts()->with('accounttype')->orderBy('name', 'ASC')->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getActiveDefault() | ||||
|     { | ||||
|         return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') | ||||
|             ->where('account_types.description', 'Default account')->where('accounts.active', 1) | ||||
|  | ||||
|             ->get(['accounts.*']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array|mixed | ||||
|      */ | ||||
|     public function  getActiveDefaultAsSelectList() | ||||
|     { | ||||
|         $list = \Auth::user()->accounts()->leftJoin( | ||||
|             'account_types', 'account_types.id', '=', 'accounts.account_type_id' | ||||
|         ) | ||||
|             ->where('account_types.description', 'Default account')->where('accounts.active', 1) | ||||
|  | ||||
|             ->orderBy('accounts.name', 'ASC')->get(['accounts.*']); | ||||
|         $return = []; | ||||
|         foreach ($list as $entry) { | ||||
|             $return[intval($entry->id)] = $entry->name; | ||||
|             $import = $this->firstOrCreate( | ||||
|                 [ | ||||
|                     'name' => 'Import account', | ||||
|                     'user_id' => $this->_user->id, | ||||
|                     'account_type_id' => $importType->id, | ||||
|                     'active' => 1 | ||||
|                 ] | ||||
|             ); | ||||
|             return $import; | ||||
|         } | ||||
|  | ||||
|         return $return; | ||||
|         // find account: | ||||
|  | ||||
|         $account = $this->_user->accounts()->where('name', $name)->accountTypeIn( | ||||
|             ['Expense account', 'Beneficiary account'] | ||||
|         )->first(['accounts.*']); | ||||
|  | ||||
|         // create if not found: | ||||
|         if (strlen($name) > 0 && is_null($account) && $create === true) { | ||||
|             $type = $this->findAccountType('Expense account'); | ||||
|             $set = [ | ||||
|                 'name' => $name, | ||||
|                 'user_id' => $this->_user->id, | ||||
|                 'active' => 1, | ||||
|                 'account_type_id' => $type->id | ||||
|             ]; | ||||
|             $account = $this->firstOrCreate($set); | ||||
|         } else if (strlen($name) > 0 && is_null($account) && $create === false) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // find cash account as fall back: | ||||
|         if (is_null($account)) { | ||||
|  | ||||
|             $account = $this->_user->accounts()->where('account_type_id', $cashType->id)->first(); | ||||
|         } | ||||
|  | ||||
|         // create cash account as ultimate fall back: | ||||
|         if (is_null($account)) { | ||||
|             $set = [ | ||||
|                 'name' => 'Cash account', | ||||
|                 'user_id' => $this->_user->id, | ||||
|                 'active' => 1, | ||||
|                 'account_type_id' => $cashType->id | ||||
|             ]; | ||||
|             $account = $this->firstOrCreate($set); | ||||
|         } | ||||
|  | ||||
|         if ($account->active == 0 && $account->account_type_id != $cashType->id) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      * @param $type | ||||
|      * | ||||
|      * @return \AccountType|null | ||||
|      */ | ||||
|     public function getBeneficiaries() | ||||
|     public function findAccountType($type) | ||||
|     { | ||||
|         $list = \Auth::user()->accounts()->leftJoin( | ||||
|             'account_types', 'account_types.id', '=', 'accounts.account_type_id' | ||||
|         ) | ||||
|             ->where('account_types.description', 'Beneficiary account')->where('accounts.active', 1) | ||||
|         return \AccountType::where('type', $type)->first(); | ||||
|     } | ||||
|  | ||||
|             ->orderBy('accounts.name', 'ASC')->get(['accounts.*']); | ||||
|     public function firstOrCreate(array $data) | ||||
|     { | ||||
|         return \Account::firstOrCreate($data); | ||||
|     } | ||||
|  | ||||
|         return $list; | ||||
|     /** | ||||
|      * @param $name | ||||
|      * @param $create | ||||
|      * | ||||
|      * @return |Account|null | ||||
|      */ | ||||
|     public function findRevenueAccountByName($name, $create = true) | ||||
|     { | ||||
|         // catch Import account: | ||||
|         if ($name == 'Import account') { | ||||
|             $importType = $this->findAccountType('Import account'); | ||||
|             $import = $this->firstOrCreate( | ||||
|                 [ | ||||
|                     'name' => 'Import account', | ||||
|                     'user_id' => $this->_user->id, | ||||
|                     'account_type_id' => $importType->id, | ||||
|                     'active' => 1 | ||||
|                 ] | ||||
|             ); | ||||
|             return $import; | ||||
|         } | ||||
|  | ||||
|         // find account: | ||||
|         $type = $this->findAccountType('Revenue account'); | ||||
|         $account = $this->_user->accounts()->where('name', $name)->where('account_type_id', $type->id)->first(); | ||||
|  | ||||
|         // create if not found: | ||||
|         if (strlen($name) > 0 && is_null($account) && $create === true) { | ||||
|             $set = [ | ||||
|                 'name' => $name, | ||||
|                 'user_id' => $this->_user->id, | ||||
|                 'active' => 1, | ||||
|                 'account_type_id' => $type->id | ||||
|             ]; | ||||
|             $account = $this->firstOrCreate($set); | ||||
|         } else if (strlen($name) > 0 && is_null($account) && $create === false) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // find cash account as fall back: | ||||
|         if (is_null($account)) { | ||||
|             $cashType = $this->findAccountType('Cash account'); | ||||
|             $account = $this->_user->accounts()->where('account_type_id', $cashType->id)->first(); | ||||
|         } | ||||
|  | ||||
|         // create cash account as ultimate fall back: | ||||
|         if (is_null($account)) { | ||||
|             $set = [ | ||||
|                 'name' => 'Cash account', | ||||
|                 'user_id' => $this->_user->id, | ||||
|                 'active' => 1, | ||||
|                 'account_type_id' => $cashType->id | ||||
|             ]; | ||||
|             $account = $this->firstOrCreate($set); | ||||
|         } | ||||
|  | ||||
|         if ($account->active == 0) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     public function getByAccountType(\AccountType $type) | ||||
|     { | ||||
|         return $this->_user->accounts()->with('accounttype')->orderBy('name', 'ASC') | ||||
|                            ->where('account_type_id', $type->id)->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -202,22 +249,71 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|     public function getByIds(array $ids) | ||||
|     { | ||||
|         if (count($ids) > 0) { | ||||
|             return \Auth::user()->accounts()->with('accounttype')->whereIn('id', $ids)->orderBy('name', 'ASC')->get(); | ||||
|             return $this->_user->accounts()->with('accounttype')->whereIn('id', $ids)->orderBy('name', 'ASC')->get(); | ||||
|         } else { | ||||
|             return $this->getActiveDefault(); | ||||
|         } | ||||
|     } | ||||
| // | ||||
| //    /** | ||||
| //     * @param $name | ||||
| //     * | ||||
| //     * @return \Account|mixed|null | ||||
| //     */ | ||||
| //    public function createOrFindBeneficiary($name) | ||||
| //    { | ||||
| //        if (is_null($name) || strlen($name) == 0) { | ||||
| //            return null; | ||||
| //        } | ||||
| //        $type = \AccountType::where('type', 'Expense account')->first(); | ||||
| //        return $this->createOrFind($name, $type); | ||||
| //    } | ||||
| // | ||||
| //    /** | ||||
| //     * @param              $name | ||||
| //     * @param \AccountType $type | ||||
| //     * | ||||
| //     * @return \Account|mixed | ||||
| //     */ | ||||
| //    public function createOrFind($name, \AccountType $type = null) | ||||
| //    { | ||||
| //        $account = $this->findByName($name, $type); | ||||
| //        if (!$account) { | ||||
| //            $data = [ | ||||
| //                'name'         => $name, | ||||
| //                'account_type' => $type | ||||
| //            ]; | ||||
| // | ||||
| //            return $this->store($data); | ||||
| //        } | ||||
| // | ||||
| //        return $account; | ||||
| //    } | ||||
| // | ||||
| //    /** | ||||
| //     * @param              $name | ||||
| //     * @param \AccountType $type | ||||
| //     * | ||||
| //     * @return mixed | ||||
| //     */ | ||||
| //    public function findByName($name, \AccountType $type = null) | ||||
| //    { | ||||
| //        $type = is_null($type) ? \AccountType::where('type', 'Asset account')->first() : $type; | ||||
| // | ||||
| //        return $this->_user->accounts()->where('account_type_id', $type->id) | ||||
| //            ->where('name', 'like', '%' . $name . '%') | ||||
| //            ->first(); | ||||
| //    } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getCashAccount() | ||||
|     public function getActiveDefault() | ||||
|     { | ||||
|         $type = \AccountType::where('description', 'Cash account')->first(); | ||||
|         $cash = \Auth::user()->accounts()->where('account_type_id', $type->id)->first(); | ||||
|  | ||||
|         return $cash; | ||||
|  | ||||
|         return $this->_user->accounts()->accountTypeIn(['Default account', 'Asset account'])->where( | ||||
|             'accounts.active', 1 | ||||
|         ) | ||||
|                            ->get(['accounts.*']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -225,12 +321,120 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|      */ | ||||
|     public function getDefault() | ||||
|     { | ||||
|         return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') | ||||
|             ->where('account_types.description', 'Default account') | ||||
|  | ||||
|             ->orderBy('accounts.name', 'ASC')->get(['accounts.*']); | ||||
|         return $this->_user->accounts()->accountTypeIn(['Default account', 'Asset account']) | ||||
|                            ->orderBy('accounts.name', 'ASC')->get(['accounts.*']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets a list of accounts that have the mentioned type. Will automatically convert | ||||
|      * strings in this array to actual (model) account types. | ||||
|      * | ||||
|      * @param array $types | ||||
|      * | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function getOfTypes(array $types) | ||||
|     { | ||||
|         $accounts = $this->_user->accounts()->accountTypeIn($types)->get(['accounts.*']); | ||||
|         return $accounts; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Job $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importAccount(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * maybe Account is already imported: | ||||
|          */ | ||||
|         $importEntry = $repository->findImportEntry($importMap, 'Account', intval($payload['data']['id'])); | ||||
|  | ||||
|         /* | ||||
|          * if so, delete job and return: | ||||
|          */ | ||||
|         if (!is_null($importEntry)) { | ||||
|             \Log::debug('Already imported ' . $payload['data']['name'] . ' of type ' . $payload['account_type']); | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * find the payload's account type: | ||||
|          */ | ||||
|         $payload['account_type'] = isset($payload['account_type']) ? $payload['account_type'] : 'Expense account'; | ||||
|         $type = $this->findAccountType($payload['account_type']); | ||||
|  | ||||
|         /* | ||||
|          * Create data array for store() procedure. | ||||
|          */ | ||||
|         $data = [ | ||||
|             'account_type' => $type, | ||||
|             'name' => $payload['data']['name'], | ||||
|         ]; | ||||
|         if (isset($payload['data']['openingbalance'])) { | ||||
|             $data['openingbalance'] = floatval($payload['data']['openingbalance']); | ||||
|             $data['openingbalancedate'] = $payload['data']['openingbalancedate']; | ||||
|         } | ||||
|         if (isset($payload['data']['inactive'])) { | ||||
|             $data['active'] = intval($payload['data']['inactive']) == 0 ? 1 : 0; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Try to store: | ||||
|          */ | ||||
|         $account = $this->store($data); | ||||
|  | ||||
|         /* | ||||
|          * Check for failure. | ||||
|          */ | ||||
|         if (count($account->errors()) > 0) { | ||||
|             \Log::error('Account creation error: ' . $account->errors()->first()); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|         \Log::debug('Imported ' . $payload['account_type'] . ': ' . $payload['data']['name']); | ||||
|  | ||||
|         /* | ||||
|          * Save meta data | ||||
|          */ | ||||
|         $repository->store($importMap, 'Account', intval($payload['data']['id']), $account->id); | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|         $job->delete(); // count fixed. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| //    /** | ||||
| //     * Used for import | ||||
| //     * | ||||
| //     * @param              $name | ||||
| //     * | ||||
| //     * @return mixed | ||||
| //     */ | ||||
| //    public function findByNameAny($name) | ||||
| //    { | ||||
| //        return $this->_user->accounts() | ||||
| //            ->where('name', 'like', '%' . $name . '%') | ||||
| //            ->first(); | ||||
| //    } | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
| @@ -239,13 +443,31 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|      */ | ||||
|     public function store($data) | ||||
|     { | ||||
|         $defaultAccountType = \AccountType::where('description', 'Default account')->first(); | ||||
|         $accountType = isset($data['account_type']) ? $data['account_type'] : $defaultAccountType; | ||||
|         /** | ||||
|          * If the AccountType has been passed through, use it: | ||||
|          */ | ||||
|         if (isset($data['account_type']) && is_object($data['account_type']) | ||||
|             && get_class($data['account_type']) == 'AccountType' | ||||
|         ) { | ||||
|             $accountType = $data['account_type']; | ||||
|         } else if (isset($data['account_type']) && is_string($data['account_type'])) { | ||||
|             // if it isnt but set as string, find it: | ||||
|             $accountType = \AccountType::where('type', $data['account_type'])->first(); | ||||
|  | ||||
|         // create Account: | ||||
|         } else { | ||||
|             $accountType = \AccountType::where('type', 'Asset account')->first(); | ||||
|         } | ||||
|         $active = isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval( | ||||
|             $data['active'] | ||||
|         ) : 1; | ||||
|  | ||||
|         /** | ||||
|          * Create new account: | ||||
|          */ | ||||
|         $account = new \Account; | ||||
|         $account->accountType()->associate($accountType); | ||||
|         $account->user()->associate(\Auth::user()); | ||||
|         $account->user()->associate($this->_user); | ||||
|  | ||||
|         $account->name = $data['name']; | ||||
|         $account->active | ||||
|             = isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval( | ||||
| @@ -253,13 +475,19 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|         ) : 1; | ||||
|  | ||||
|         // try to save it: | ||||
|         if ($account->save()) { | ||||
|             // create initial balance, if necessary: | ||||
|             if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) { | ||||
|                 $amount = floatval($data['openingbalance']); | ||||
|                 $date = new Carbon($data['openingbalancedate']); | ||||
|                 $this->_createInitialBalance($account, $amount, $date); | ||||
|         try { | ||||
|             if ($account->save()) { | ||||
|                 // create initial balance, if necessary: | ||||
|                 if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) { | ||||
|                     $amount = floatval($data['openingbalance']); | ||||
|                     $date = new Carbon($data['openingbalancedate']); | ||||
|                     if ($amount != 0) { | ||||
|                         $this->_createInitialBalance($account, $amount, $date); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } catch (QueryException $e) { | ||||
|             // do nothing | ||||
|         } | ||||
|  | ||||
|  | ||||
| @@ -267,6 +495,206 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * @param int $amount | ||||
|      * @param Carbon $date | ||||
|      * | ||||
|      * @return bool | ||||
|      * @SuppressWarnings(PHPMD.CamelCaseMethodName) | ||||
|      */ | ||||
|     protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date) | ||||
|     { | ||||
|         /* | ||||
|          * The repositories we need: | ||||
|          */ | ||||
|         /** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */ | ||||
|         $transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface'); | ||||
|         $transactions->overruleUser($this->_user); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|          * get account type: | ||||
|          */ | ||||
|         $initialBalanceAT = $this->findAccountType('Initial balance account'); | ||||
|  | ||||
|         /* | ||||
|          * create new account | ||||
|          */ | ||||
|         $initial = new \Account; | ||||
|         $initial->accountType()->associate($initialBalanceAT); | ||||
|         $initial->user()->associate($this->_user); | ||||
|         $initial->name = $account->name . ' initial balance'; | ||||
|         $initial->active = 0; | ||||
|         if ($initial->validate()) { | ||||
|             $initial->save(); | ||||
|             /* | ||||
|              * create new transaction journal (and transactions): | ||||
|              */ | ||||
|  | ||||
|             $set = [ | ||||
|                 'account_from_id' => $initial->id, | ||||
|                 'account_to_id' => $account->id, | ||||
|                 'description' => 'Initial Balance for ' . $account->name, | ||||
|                 'what' => 'Opening balance', | ||||
|                 'amount' => $amount, | ||||
|                 'category' => '', | ||||
|                 'date' => $date->format('Y-m-d') | ||||
|             ]; | ||||
|             $transactions->store($set); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Takes a transaction/account component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransaction(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('Never found budget/account combination "' . $payload['data']['transaction_id'] . '"'); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed. | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ | ||||
|         $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|         $journals->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Prep some vars from the payload | ||||
|          */ | ||||
|         $transactionId = intval($payload['data']['transaction_id']); | ||||
|         $componentId = intval($payload['data']['component_id']); | ||||
|  | ||||
|         /* | ||||
|          * Find the import map for both: | ||||
|          */ | ||||
|         $accountMap = $repository->findImportEntry($importMap, 'Account', $componentId); | ||||
|         $transactionMap = $repository->findImportEntry($importMap, 'Transaction', $transactionId); | ||||
|  | ||||
|         /* | ||||
|          * Either may be null: | ||||
|          */ | ||||
|         if (is_null($accountMap) || is_null($transactionMap)) { | ||||
|             \Log::notice('No map found in account/transaction mapper. Release.'); | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Find the account and the transaction: | ||||
|          */ | ||||
|         $account = $this->find($accountMap->new); | ||||
|         /** @var \TransactionJournal $journal */ | ||||
|         $journal = $journals->find($transactionMap->new); | ||||
|  | ||||
|         /* | ||||
|          * If either is null, release: | ||||
|          */ | ||||
|         if (is_null($account) || is_null($journal)) { | ||||
|             \Log::notice('Map is incorrect in account/transaction mapper. Release.'); | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Update one of the journal's transactions to have the right account: | ||||
|          */ | ||||
|         $importType = $this->findAccountType('Import account'); | ||||
|         /** @var \Transaction $transaction */ | ||||
|         $updated = false; | ||||
|         \Log::debug( | ||||
|             'Connect "' . $account->name . '" (#' . $account->id . ') to "' . $journal->description . '" (#' | ||||
|             . $journal->id . ')' | ||||
|         ); | ||||
|         foreach ($journal->transactions as $index => $transaction) { | ||||
|             /* | ||||
|              * If it's of the right type, update it! | ||||
|              */ | ||||
|             \Log::debug( | ||||
|                 'Transaction ' . $index . ' (#' . $transaction->id . '): [' . $transaction->account->account_type_id | ||||
|                 . ' vs. ' . $importType->id . ']' | ||||
|             ); | ||||
|             if ($transaction->account->account_type_id == $importType->id) { | ||||
|                 $transaction->account()->associate($account); | ||||
|                 $transaction->save(); | ||||
|                 $updated = true; | ||||
|                 \Log::debug( | ||||
|                     'Connected expense account "' . $account->name . '" to journal "' . $journal->description . '"' | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         if ($updated === false) { | ||||
|             \Log::error( | ||||
|                 'Did not connect transactions of journal #' . $journal->id . ' to expense account ' . $account->id | ||||
|             ); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         $journal->save(); | ||||
|  | ||||
|  | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         $job->delete(); // count fixed | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * | ||||
|      * @return mixed|void | ||||
|      */ | ||||
|     public function overruleUser(\User $user) | ||||
|     { | ||||
|         $this->_user = $user; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $accountId | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function find($accountId) | ||||
|     { | ||||
|         return $this->_user->accounts()->where('id', $accountId)->first(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * @param          $data | ||||
| @@ -281,12 +709,12 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|             $account->save(); | ||||
|         } | ||||
|         // update initial balance if necessary: | ||||
|         if (floatval($data['openingbalance']) != 0) { | ||||
|         if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) { | ||||
|  | ||||
|             /** @var \Firefly\Helper\Controllers\AccountInterface $interface */ | ||||
|             $interface = \App::make('Firefly\Helper\Controllers\AccountInterface'); | ||||
|  | ||||
|             if ($account->accounttype->description == 'Default account') { | ||||
|             if ($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account') { | ||||
|  | ||||
|  | ||||
|                 $journal = $interface->openingBalanceTransaction($account); | ||||
| @@ -304,41 +732,5 @@ class EloquentAccountRepository implements AccountRepositoryInterface | ||||
|         return $account; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Account $account | ||||
|      * @param int      $amount | ||||
|      * @param Carbon   $date | ||||
|      * | ||||
|      * @return bool | ||||
|      * @SuppressWarnings(PHPMD.CamelCaseMethodName) | ||||
|      */ | ||||
|     protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date) | ||||
|     { | ||||
|         // get account type: | ||||
|         $initialBalanceAT = \AccountType::where('description', 'Initial balance account')->first(); | ||||
|  | ||||
|         // create new account: | ||||
|         $initial = new \Account; | ||||
|         $initial->accountType()->associate($initialBalanceAT); | ||||
|         $initial->user()->associate(\Auth::user()); | ||||
|         $initial->name = $account->name . ' initial balance'; | ||||
|         $initial->active = 0; | ||||
|         if ($initial->validate()) { | ||||
|             $initial->save(); | ||||
|             // create new transaction journal (and transactions): | ||||
|             /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $transactionJournal */ | ||||
|             $transactionJournal = \App::make( | ||||
|                 'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface' | ||||
|             ); | ||||
|  | ||||
|             $transactionJournal->createSimpleJournal( | ||||
|                 $initial, $account, 'Initial Balance for ' . $account->name, $amount, $date | ||||
|             ); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Storage\Budget; | ||||
| use Illuminate\Queue\Jobs\Job; | ||||
|  | ||||
| /** | ||||
|  * Interface BudgetRepositoryInterface | ||||
| @@ -9,6 +10,34 @@ namespace Firefly\Storage\Budget; | ||||
|  */ | ||||
| interface BudgetRepositoryInterface | ||||
| { | ||||
|     /** | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importBudget(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * Takes a transaction/budget component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransaction(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * Takes a transfer/budget component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransfer(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * | ||||
| @@ -23,15 +52,23 @@ interface BudgetRepositoryInterface | ||||
|      */ | ||||
|     public function find($budgetId); | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param $budgetName | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function get(); | ||||
|     public function findByName($budgetName); | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function overruleUser(\User $user); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getAsSelectList(); | ||||
|     public function get(); | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|   | ||||
| @@ -3,19 +3,361 @@ | ||||
| namespace Firefly\Storage\Budget; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Database\Eloquent\Collection; | ||||
| use Illuminate\Queue\Jobs\Job; | ||||
|  | ||||
| /** | ||||
|  * Class EloquentBudgetRepository | ||||
|  * | ||||
|  * @package Firefly\Storage\Budget | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CamelCasePropertyName) | ||||
|  * | ||||
|  */ | ||||
| class EloquentBudgetRepository implements BudgetRepositoryInterface | ||||
| { | ||||
|  | ||||
|     protected $_user = null; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->_user = \Auth::user(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importBudget(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * maybe Budget is already imported: | ||||
|          */ | ||||
|         $importEntry = $repository->findImportEntry($importMap, 'Budget', intval($payload['data']['id'])); | ||||
|  | ||||
|         /* | ||||
|          * if so, delete job and return: | ||||
|          */ | ||||
|         if (!is_null($importEntry)) { | ||||
|             \Log::debug('Already imported budget ' . $payload['data']['name']); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * maybe Budget is already imported. | ||||
|          */ | ||||
|         $budget = $this->findByName($payload['data']['name']); | ||||
|  | ||||
|         if (is_null($budget)) { | ||||
|             /* | ||||
|              * Not imported yet. | ||||
|              */ | ||||
|             $budget = $this->store($payload['data']); | ||||
|             $repository->store($importMap, 'Budget', $payload['data']['id'], $budget->id); | ||||
|             \Log::debug('Imported budget "' . $payload['data']['name'] . '".'); | ||||
|         } else { | ||||
|             /* | ||||
|              * already imported. | ||||
|              */ | ||||
|             $repository->store($importMap, 'Budget', $payload['data']['id'], $budget->id); | ||||
|             \Log::debug('Already had budget "' . $payload['data']['name'] . '".'); | ||||
|         } | ||||
|  | ||||
|         // update map: | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         // delete job. | ||||
|         $job->delete(); // count fixed | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * | ||||
|      * @return mixed|void | ||||
|      */ | ||||
|     public function overruleUser(\User $user) | ||||
|     { | ||||
|         $this->_user = $user; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $budgetName | ||||
|      * | ||||
|      * @return \Budget|null | ||||
|      */ | ||||
|     public function findByName($budgetName) | ||||
|     { | ||||
|  | ||||
|         return $this->_user->budgets()->whereName($budgetName)->first(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return \Budget | ||||
|      */ | ||||
|     public function store($data) | ||||
|     { | ||||
|         $budget       = new \Budget; | ||||
|         $budget->name = $data['name']; | ||||
|         $budget->user()->associate($this->_user); | ||||
|         $budget->save(); | ||||
|  | ||||
|         // if limit, create limit (repetition itself will be picked up elsewhere). | ||||
|         if (isset($data['amount']) && floatval($data['amount']) > 0) { | ||||
|             $startDate = new Carbon; | ||||
|             $limitData = [ | ||||
|                 'budget_id' => $budget->id, | ||||
|                 'startdate' => $startDate->format('Y-m-d'), | ||||
|                 'period'    => $data['repeat_freq'], | ||||
|                 'amount'    => floatval($data['amount']), | ||||
|                 'repeats'   => 0 | ||||
|             ]; | ||||
|             /** @var \Firefly\Storage\Limit\LimitRepositoryInterface $limitRepository */ | ||||
|             $limitRepository = \App::make('Firefly\Storage\Limit\LimitRepositoryInterface'); | ||||
|             $limitRepository->overruleUser($this->_user); | ||||
|             $limit = $limitRepository->store($limitData); | ||||
|             \Event::fire('limits.store', [$limit]); | ||||
|         } | ||||
|  | ||||
|         if ($budget->validate()) { | ||||
|             $budget->save(); | ||||
|         } | ||||
|  | ||||
|         return $budget; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Takes a transfer/budget component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransfer(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('Never found budget/transfer combination "' . $payload['data']['transfer_id'] . '"'); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ | ||||
|         $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|         $journals->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Prep some vars from the payload | ||||
|          */ | ||||
|         $transferId = intval($payload['data']['transfer_id']); | ||||
|         $componentId   = intval($payload['data']['component_id']); | ||||
|  | ||||
|         /* | ||||
|          * Find the import map for both: | ||||
|          */ | ||||
|         $budgetMap      = $repository->findImportEntry($importMap, 'Budget', $componentId); | ||||
|         $transferMap = $repository->findImportEntry($importMap, 'Transfer', $transferId); | ||||
|  | ||||
|         /* | ||||
|          * Either may be null: | ||||
|          */ | ||||
|         if (is_null($budgetMap) || is_null($transferMap)) { | ||||
|             \Log::notice('No map found in budget/transfer mapper. Release.'); | ||||
|             if(\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Find the budget and the transaction: | ||||
|          */ | ||||
|         $budget = $this->find($budgetMap->new); | ||||
|         /** @var \TransactionJournal $journal */ | ||||
|         $journal = $journals->find($transferMap->new); | ||||
|  | ||||
|         /* | ||||
|          * If either is null, release: | ||||
|          */ | ||||
|         if (is_null($budget) || is_null($journal)) { | ||||
|             \Log::notice('Map is incorrect in budget/transfer mapper. Release.'); | ||||
|             if(\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Update journal to have budget: | ||||
|          */ | ||||
|         $journal->budgets()->save($budget); | ||||
|         $journal->save(); | ||||
|         \Log::debug('Connected budget "' . $budget->name . '" to journal "' . $journal->description . '"'); | ||||
|  | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         $job->delete(); // count fixed | ||||
|  | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Takes a transaction/budget component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransaction(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('Never found budget/transaction combination "' . $payload['data']['transaction_id'] . '"'); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ | ||||
|         $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|         $journals->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Prep some vars from the payload | ||||
|          */ | ||||
|         $transactionId = intval($payload['data']['transaction_id']); | ||||
|         $componentId   = intval($payload['data']['component_id']); | ||||
|  | ||||
|         /* | ||||
|          * Find the import map for both: | ||||
|          */ | ||||
|         $budgetMap      = $repository->findImportEntry($importMap, 'Budget', $componentId); | ||||
|         $transactionMap = $repository->findImportEntry($importMap, 'Transaction', $transactionId); | ||||
|  | ||||
|         /* | ||||
|          * Either may be null: | ||||
|          */ | ||||
|         if (is_null($budgetMap) || is_null($transactionMap)) { | ||||
|             \Log::notice('No map found in budget/transaction mapper. Release.'); | ||||
|             if(\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Find the budget and the transaction: | ||||
|          */ | ||||
|         $budget = $this->find($budgetMap->new); | ||||
|         /** @var \TransactionJournal $journal */ | ||||
|         $journal = $journals->find($transactionMap->new); | ||||
|  | ||||
|         /* | ||||
|          * If either is null, release: | ||||
|          */ | ||||
|         if (is_null($budget) || is_null($journal)) { | ||||
|             \Log::notice('Map is incorrect in budget/transaction mapper. Release.'); | ||||
|             if(\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Update journal to have budget: | ||||
|          */ | ||||
|         $journal->budgets()->save($budget); | ||||
|         $journal->save(); | ||||
|         \Log::debug('Connected budget "' . $budget->name . '" to journal "' . $journal->description . '"'); | ||||
|  | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         $job->delete(); // count fixed | ||||
|  | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $budgetId | ||||
|      * | ||||
|      * @return \Budget|null | ||||
|      */ | ||||
|     public function find($budgetId) | ||||
|     { | ||||
|  | ||||
|         return $this->_user->budgets()->find($budgetId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * | ||||
|      * @return bool|mixed | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function destroy(\Budget $budget) | ||||
|     { | ||||
| @@ -25,112 +367,20 @@ class EloquentBudgetRepository implements BudgetRepositoryInterface | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $budgetId | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function find($budgetId) | ||||
|     { | ||||
|  | ||||
|         return \Auth::user()->budgets()->find($budgetId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      * @return Collection | ||||
|      */ | ||||
|     public function get() | ||||
|     { | ||||
|         $set = \Auth::user()->budgets()->with( | ||||
|         $set = $this->_user->budgets()->with( | ||||
|             ['limits'                        => function ($q) { | ||||
|                     $q->orderBy('limits.startdate', 'DESC'); | ||||
|                 }, 'limits.limitrepetitions' => function ($q) { | ||||
|                     $q->orderBy('limit_repetitions.startdate', 'ASC'); | ||||
|                 }] | ||||
|         )->orderBy('name', 'ASC')->get(); | ||||
|         foreach ($set as $budget) { | ||||
|             foreach ($budget->limits as $limit) { | ||||
|                 foreach ($limit->limitrepetitions as $rep) { | ||||
|                     $rep->left = $rep->left(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array|mixed | ||||
|      */ | ||||
|     public function getAsSelectList() | ||||
|     { | ||||
|         $list = \Auth::user()->budgets()->with( | ||||
|             ['limits', 'limits.limitrepetitions'] | ||||
|         )->orderBy('name', 'ASC')->get(); | ||||
|         $return = []; | ||||
|         foreach ($list as $entry) { | ||||
|             $return[intval($entry->id)] = $entry->name; | ||||
|         } | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return \Budget|mixed | ||||
|      */ | ||||
|     public function store($data) | ||||
|     { | ||||
|         $budget = new \Budget; | ||||
|         $budget->name = $data['name']; | ||||
|         $budget->user()->associate(\Auth::user()); | ||||
|         $budget->save(); | ||||
|  | ||||
|         // if limit, create limit (repetition itself will be picked up elsewhere). | ||||
|         if (floatval($data['amount']) > 0) { | ||||
|             $limit = new \Limit; | ||||
|             $limit->budget()->associate($budget); | ||||
|             $startDate = new Carbon; | ||||
|             switch ($data['repeat_freq']) { | ||||
|                 case 'daily': | ||||
|                     $startDate->startOfDay(); | ||||
|                     break; | ||||
|                 case 'weekly': | ||||
|                     $startDate->startOfWeek(); | ||||
|                     break; | ||||
|                 case 'monthly': | ||||
|                     $startDate->startOfMonth(); | ||||
|                     break; | ||||
|                 case 'quarterly': | ||||
|                     $startDate->firstOfQuarter(); | ||||
|                     break; | ||||
|                 case 'half-year': | ||||
|                     $startDate->startOfYear(); | ||||
|                     if (intval($startDate->format('m')) >= 7) { | ||||
|                         $startDate->addMonths(6); | ||||
|                     } | ||||
|                     break; | ||||
|                 case 'yearly': | ||||
|                     $startDate->startOfYear(); | ||||
|                     break; | ||||
|             } | ||||
|             $limit->startdate = $startDate; | ||||
|             $limit->amount = $data['amount']; | ||||
|             $limit->repeats = isset($data['repeats']) ? $data['repeats'] : 0; | ||||
|             $limit->repeat_freq = $data['repeat_freq']; | ||||
|             if ($limit->validate()) { | ||||
|                 $limit->save(); | ||||
|                 \Event::fire('limits.store', [$limit]); | ||||
|             } | ||||
|         } | ||||
|         if ($budget->validate()) { | ||||
|             $budget->save(); | ||||
|         } | ||||
|  | ||||
|         return $budget; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \Budget $budget | ||||
|      * @param         $data | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Storage\Category; | ||||
| use Illuminate\Queue\Jobs\Job; | ||||
|  | ||||
| /** | ||||
|  * Interface CategoryRepositoryInterface | ||||
| @@ -9,6 +10,33 @@ namespace Firefly\Storage\Category; | ||||
|  */ | ||||
| interface CategoryRepositoryInterface | ||||
| { | ||||
|     /** | ||||
|      * Takes a transaction/category component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransaction(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * Takes a transfer/category component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransfer(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importCategory(Job $job, array $payload); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
| @@ -27,7 +55,13 @@ interface CategoryRepositoryInterface | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function createOrFind($name); | ||||
|     public function firstOrCreate($name); | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function overruleUser(\User $user); | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|   | ||||
| @@ -2,6 +2,8 @@ | ||||
|  | ||||
| namespace Firefly\Storage\Category; | ||||
|  | ||||
| use Illuminate\Queue\Jobs\Job; | ||||
|  | ||||
| /** | ||||
|  * Class EloquentCategoryRepository | ||||
|  * | ||||
| @@ -9,23 +11,340 @@ namespace Firefly\Storage\Category; | ||||
|  */ | ||||
| class EloquentCategoryRepository implements CategoryRepositoryInterface | ||||
| { | ||||
|     protected $_user = null; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->_user = \Auth::user(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Takes a transfer/category component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransfer(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|  | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('Never found category/transfer combination "' . $payload['data']['transfer_id'] . '"'); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed. | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ | ||||
|         $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|         $journals->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Prep some vars from the payload | ||||
|          */ | ||||
|         $transferId  = intval($payload['data']['transfer_id']); | ||||
|         $componentId = intval($payload['data']['component_id']); | ||||
|  | ||||
|         /* | ||||
|          * Find the import map for both: | ||||
|          */ | ||||
|         $categoryMap = $repository->findImportEntry($importMap, 'Category', $componentId); | ||||
|         $transferMap = $repository->findImportEntry($importMap, 'Transfer', $transferId); | ||||
|  | ||||
|         /* | ||||
|          * Either may be null: | ||||
|          */ | ||||
|         if (is_null($categoryMap) || is_null($transferMap)) { | ||||
|             \Log::notice('No map found in category/transfer mapper. Release.'); | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Find the budget and the transaction: | ||||
|          */ | ||||
|         $category = $this->find($categoryMap->new); | ||||
|         /** @var \TransactionJournal $journal */ | ||||
|         $journal = $journals->find($transferMap->new); | ||||
|  | ||||
|         /* | ||||
|          * If either is null, release: | ||||
|          */ | ||||
|         if (is_null($category) || is_null($journal)) { | ||||
|             \Log::notice('Map is incorrect in category/transfer mapper. Release.'); | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Update journal to have budget: | ||||
|          */ | ||||
|         $journal->categories()->save($category); | ||||
|         $journal->save(); | ||||
|         \Log::debug('Connected category "' . $category->name . '" to journal "' . $journal->description . '"'); | ||||
|  | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         $job->delete(); // count fixed | ||||
|  | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * | ||||
|      * @return mixed|void | ||||
|      */ | ||||
|     public function overruleUser(\User $user) | ||||
|     { | ||||
|         $this->_user = $user; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $categoryId | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function find($categoryId) | ||||
|     { | ||||
|         return $this->_user->categories()->find($categoryId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Takes a transaction/category component and updates the transaction journal to match. | ||||
|      * | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importUpdateTransaction(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|  | ||||
|         if ($job->attempts() > 10) { | ||||
|             \Log::error('Never found category/transaction combination "' . $payload['data']['transaction_id'] . '"'); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed. | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ | ||||
|         $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); | ||||
|         $journals->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Prep some vars from the payload | ||||
|          */ | ||||
|         $transactionId = intval($payload['data']['transaction_id']); | ||||
|         $componentId   = intval($payload['data']['component_id']); | ||||
|  | ||||
|         /* | ||||
|          * Find the import map for both: | ||||
|          */ | ||||
|         $categoryMap    = $repository->findImportEntry($importMap, 'Category', $componentId); | ||||
|         $transactionMap = $repository->findImportEntry($importMap, 'Transaction', $transactionId); | ||||
|  | ||||
|         /* | ||||
|          * Either may be null: | ||||
|          */ | ||||
|         if (is_null($categoryMap) || is_null($transactionMap)) { | ||||
|             \Log::notice('No map found in category/transaction mapper. Release.'); | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Find the budget and the transaction: | ||||
|          */ | ||||
|         $category = $this->find($categoryMap->new); | ||||
|         /** @var \TransactionJournal $journal */ | ||||
|         $journal = $journals->find($transactionMap->new); | ||||
|  | ||||
|         /* | ||||
|          * If either is null, release: | ||||
|          */ | ||||
|         if (is_null($category) || is_null($journal)) { | ||||
|             \Log::notice('Map is incorrect in category/transaction mapper. Release.'); | ||||
|             if (\Config::get('queue.default') == 'sync') { | ||||
|                 $importMap->jobsdone++; | ||||
|                 $importMap->save(); | ||||
|                 $job->delete(); // count fixed | ||||
|             } else { | ||||
|                 $job->release(300); // proper release. | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Update journal to have budget: | ||||
|          */ | ||||
|         $journal->categories()->save($category); | ||||
|         $journal->save(); | ||||
|         \Log::debug('Connected category "' . $category->name . '" to journal "' . $journal->description . '"'); | ||||
|  | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         $job->delete(); // count fixed | ||||
|  | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Job   $job | ||||
|      * @param array $payload | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function importCategory(Job $job, array $payload) | ||||
|     { | ||||
|         /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ | ||||
|         $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); | ||||
|  | ||||
|         /** @var \Importmap $importMap */ | ||||
|         $importMap = $repository->findImportmap($payload['mapID']); | ||||
|         $user      = $importMap->user; | ||||
|         $this->overruleUser($user); | ||||
|  | ||||
|         /* | ||||
|          * Maybe the category has already been imported | ||||
|          */ | ||||
|         $importEntry = $repository->findImportEntry($importMap, 'Category', intval($payload['data']['id'])); | ||||
|  | ||||
|         /* | ||||
|          * if so, delete job and return: | ||||
|          */ | ||||
|         if (!is_null($importEntry)) { | ||||
|             \Log::debug('Already imported category ' . $payload['data']['name']); | ||||
|  | ||||
|             $importMap->jobsdone++; | ||||
|             $importMap->save(); | ||||
|  | ||||
|             $job->delete(); // count fixed | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * try to find category first | ||||
|          */ | ||||
|         $current = $this->findByName($payload['data']['name']); | ||||
|  | ||||
|         /* | ||||
|          * If not found, create it: | ||||
|          */ | ||||
|         if (is_null($current)) { | ||||
|             $category = $this->store($payload['data']); | ||||
|             $repository->store($importMap, 'Category', $payload['data']['id'], $category->id); | ||||
|             \Log::debug('Imported category "' . $payload['data']['name'] . '".'); | ||||
|         } else { | ||||
|             $repository->store($importMap, 'Category', $payload['data']['id'], $current->id); | ||||
|             \Log::debug('Already had category "' . $payload['data']['name'] . '".'); | ||||
|         } | ||||
|  | ||||
|         // update map: | ||||
|         $importMap->jobsdone++; | ||||
|         $importMap->save(); | ||||
|  | ||||
|         $job->delete(); // count fixed | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function findByName($name) | ||||
|     { | ||||
|         if ($name == '' || strlen($name) == 0) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return $this->_user->categories()->where('name', $name)->first(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return \Category|mixed | ||||
|      */ | ||||
|     public function store($data) | ||||
|     { | ||||
|         $category       = new \Category; | ||||
|         $category->name = $data['name']; | ||||
|  | ||||
|         $category->user()->associate($this->_user); | ||||
|         $category->save(); | ||||
|  | ||||
|         return $category; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return \Category|mixed | ||||
|      */ | ||||
|     public function createOrFind($name) | ||||
|     public function firstOrCreate($name) | ||||
|     { | ||||
|         if (strlen($name) == 0) { | ||||
|             return null; | ||||
|         } | ||||
|         $category = $this->findByName($name); | ||||
|         if (!$category) { | ||||
|             return $this->store(['name' => $name]); | ||||
|         } | ||||
|  | ||||
|         return $category; | ||||
|  | ||||
|         $data = [ | ||||
|             'name'    => $name, | ||||
|             'user_id' => $this->_user->id, | ||||
|         ]; | ||||
|         return \Category::firstOrCreate($data); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -41,53 +360,12 @@ class EloquentCategoryRepository implements CategoryRepositoryInterface | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $categoryId | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function find($categoryId) | ||||
|     { | ||||
|         return \Auth::user()->categories()->find($categoryId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function findByName($name) | ||||
|     { | ||||
|         if ($name == '' || strlen($name) == 0) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return \Auth::user()->categories()->where('name', 'LIKE', '%' . $name . '%')->first(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function get() | ||||
|     { | ||||
|         return \Auth::user()->categories()->orderBy('name', 'ASC')->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return \Category|mixed | ||||
|      */ | ||||
|     public function store($data) | ||||
|     { | ||||
|         $category = new \Category; | ||||
|         $category->name = $data['name']; | ||||
|  | ||||
|         $category->user()->associate(\Auth::user()); | ||||
|         $category->save(); | ||||
|  | ||||
|         return $category; | ||||
|         return $this->_user->categories()->orderBy('name', 'ASC')->get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -1,31 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
|  | ||||
| namespace Firefly\Storage\Component; | ||||
|  | ||||
| /** | ||||
|  * Interface ComponentRepositoryInterface | ||||
|  * | ||||
|  * @package Firefly\Storage\Component | ||||
|  */ | ||||
| interface ComponentRepositoryInterface | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function count(); | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function get(); | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function store($data); | ||||
|  | ||||
| }  | ||||
| @@ -1,77 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
|  | ||||
| namespace Firefly\Storage\Component; | ||||
|  | ||||
| use Firefly\Exception\FireflyException; | ||||
| use Illuminate\Database\QueryException; | ||||
|  | ||||
| /** | ||||
|  * Class EloquentComponentRepository | ||||
|  * | ||||
|  * @package Firefly\Storage\Component | ||||
|  */ | ||||
| class EloquentComponentRepository implements ComponentRepositoryInterface | ||||
| { | ||||
|     public $validator; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function count() | ||||
|     { | ||||
|         return \Auth::user()->components()->count(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return mixed|void | ||||
|      * @throws \Firefly\Exception\FireflyException | ||||
|      */ | ||||
|     public function get() | ||||
|     { | ||||
|         throw new FireflyException('No implementation.'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return \Budget|\Category|mixed | ||||
|      * @throws \Firefly\Exception\FireflyException | ||||
|      */ | ||||
|     public function store($data) | ||||
|     { | ||||
|         if (!isset($data['class'])) { | ||||
|             throw new FireflyException('No class type present.'); | ||||
|         } | ||||
|         switch ($data['class']) { | ||||
|             default: | ||||
|             case 'Budget': | ||||
|                 $component = new \Budget; | ||||
|                 break; | ||||
|             case 'Category': | ||||
|                 $component = new \Category; | ||||
|                 break; | ||||
|  | ||||
|         } | ||||
|         $component->name = $data['name']; | ||||
|         $component->user()->associate(\Auth::user()); | ||||
|         try { | ||||
|             $component->save(); | ||||
|         } catch (QueryException $e) { | ||||
|             \Log::error('DB ERROR: ' . $e->getMessage()); | ||||
|             throw new FireflyException('Could not save component ' . $data['name'] . ' of type' | ||||
|                 . $data['class']); | ||||
|         } | ||||
|  | ||||
|         return $component; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										58
									
								
								app/lib/Firefly/Storage/Import/EloquentImportRepository.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/lib/Firefly/Storage/Import/EloquentImportRepository.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Firefly\Storage\Import; | ||||
|  | ||||
|  | ||||
| class EloquentImportRepository implements ImportRepositoryInterface | ||||
| { | ||||
|     protected $_user = null; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->_user = \Auth::user(); | ||||
|     } | ||||
|  | ||||
|     public function findImportComponentMap(\Importmap $map, $oldComponentId) | ||||
|     { | ||||
|         $entry = \Importentry::where('importmap_id', $map->id) | ||||
|                              ->whereIn('class', ['Budget', 'Category', 'Account', 'Component']) | ||||
|                              ->where('old', intval($oldComponentId))->first(); | ||||
|  | ||||
|         return $entry; | ||||
|     } | ||||
|  | ||||
|     public function findImportEntry(\Importmap $map, $class, $oldID) | ||||
|     { | ||||
|  | ||||
|         return \Importentry::where('importmap_id', $map->id)->where('class', $class)->where('old', $oldID)->first(); | ||||
|     } | ||||
|  | ||||
|     public function findImportMap($id) | ||||
|     { | ||||
|         return \Importmap::find($id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \User $user | ||||
|      * @return mixed|void | ||||
|      */ | ||||
|     public function overruleUser(\User $user) | ||||
|     { | ||||
|         $this->_user = $user; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function store(\Importmap $map, $class, $oldID, $newID) | ||||
|     { | ||||
|         $entry = new \Importentry; | ||||
|         $entry->importmap()->associate($map); | ||||
|         $entry->class = $class; | ||||
|         $entry->old   = intval($oldID); | ||||
|         $entry->new   = intval($newID); | ||||
|         $entry->save(); | ||||
|     } | ||||
|  | ||||
| }  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user