Compare commits

..

190 Commits
4.0.2 ... 4.1.5

Author SHA1 Message Date
James Cole
a66990459e Merge branch 'release/4.1.5' 2016-11-01 18:45:25 +01:00
James Cole
fecbdc7fbf New version.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-01 18:44:26 +01:00
James Cole
0369ace5f7 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii: (28 commits)
  Approved. Step name: Proofread
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  ...
2016-11-01 18:41:15 +01:00
James Cole
1657048181 Fixed bug #370 2016-11-01 18:40:35 +01:00
James Cole
b9bdaa7a56 Merge pull request #369 from JC5/l10n_develop
New Crowdin translations
2016-11-01 11:43:00 +01:00
James Cole
f28d07e17b Approved. Step name: Proofread 2016-11-01 11:40:36 +01:00
James Cole
8b8bf1debc New translations 2016-10-31 18:41:44 +01:00
James Cole
aff1c1e3ef New translations 2016-10-31 18:41:40 +01:00
James Cole
169bb2c9bb New translations 2016-10-31 18:41:29 +01:00
James Cole
fb1eafef43 New translations 2016-10-31 18:41:26 +01:00
James Cole
bfe26ceb39 New translations 2016-10-31 18:41:25 +01:00
James Cole
050f305e80 New translations 2016-10-31 18:41:24 +01:00
James Cole
63a6a4f823 New translations 2016-10-31 18:41:23 +01:00
James Cole
a3b167cab5 New translations 2016-10-31 18:41:23 +01:00
James Cole
48327948e2 New translations 2016-10-31 18:41:22 +01:00
James Cole
93856d4577 New translations 2016-10-31 18:41:21 +01:00
James Cole
7ff068aa95 New translations 2016-10-31 18:41:20 +01:00
James Cole
b2f00c869e New translations 2016-10-31 18:41:18 +01:00
James Cole
b717cab8f6 New translations 2016-10-31 18:41:17 +01:00
James Cole
adaff52707 New translations 2016-10-31 18:41:16 +01:00
James Cole
54050edcc6 New translations 2016-10-31 18:41:16 +01:00
James Cole
9acbb69a6a New translations 2016-10-31 18:41:15 +01:00
James Cole
a5e6de047a New translations 2016-10-31 18:41:14 +01:00
James Cole
3d8d35207b New translations 2016-10-31 18:41:13 +01:00
James Cole
0a95f59813 New translations 2016-10-31 18:41:10 +01:00
James Cole
43a3d28dbd New translations 2016-10-31 18:41:09 +01:00
James Cole
685cb7a505 Translated 2016-10-31 18:41:06 +01:00
James Cole
dd82466d07 Translated 2016-10-31 18:41:05 +01:00
James Cole
2cbe4a013e Translated 2016-10-31 18:41:04 +01:00
James Cole
fb85341844 Translated 2016-10-31 18:41:03 +01:00
James Cole
116b3ecdad Approved. Step name: Proofread 2016-10-31 18:40:58 +01:00
James Cole
af85fbf0a3 New translations 2016-10-31 18:40:50 +01:00
James Cole
1d250593c0 New translations 2016-10-31 18:40:45 +01:00
James Cole
ed33a054ad This update will make the help method fall back to the English content, if it is available. 2016-10-31 18:31:52 +01:00
James Cole
4e3e015912 Fix multi year account report [skip ci] 2016-10-30 20:13:49 +01:00
James Cole
7821c52842 Ajax some report parts. 2016-10-30 18:29:26 +01:00
James Cole
0a6f299ae6 Merge branch 'release/4.1.4' 2016-10-30 08:56:35 +01:00
James Cole
73f87e30c2 Changelog.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 08:52:09 +01:00
James Cole
838ece2c89 Merge pull request #368 from JC5/l10n_develop
New Crowdin translations
2016-10-30 07:32:23 +01:00
James Cole
d8b88ea2c0 New translations 2016-10-30 07:20:17 +01:00
James Cole
5908951b75 New translations 2016-10-30 07:20:17 +01:00
James Cole
0b41f4c4d2 New translations 2016-10-30 07:20:16 +01:00
James Cole
35439d4fbc New translations 2016-10-30 07:20:16 +01:00
James Cole
fdce40310f New translations 2016-10-30 07:20:15 +01:00
James Cole
6b4785ae32 New translations 2016-10-30 07:20:15 +01:00
James Cole
f74e8e9cb7 New translations 2016-10-30 07:20:13 +01:00
James Cole
5a4eb7e09e New translations 2016-10-30 07:20:12 +01:00
James Cole
3bc4df03cc New translations 2016-10-30 07:20:11 +01:00
James Cole
5d585132fb New translations 2016-10-30 07:20:11 +01:00
James Cole
eff4905883 New translations 2016-10-30 07:20:10 +01:00
James Cole
8923ac4fe3 New translations 2016-10-30 07:20:09 +01:00
James Cole
073535e5ed New translations 2016-10-30 07:20:09 +01:00
James Cole
d304b90ca6 New translations 2016-10-30 07:20:08 +01:00
James Cole
816c26e14e New translations 2016-10-30 07:20:07 +01:00
James Cole
b1244ffa01 New translations 2016-10-30 07:20:07 +01:00
James Cole
fc1342bff9 New translations 2016-10-30 07:20:06 +01:00
James Cole
d7b95194b5 New translations 2016-10-30 07:20:04 +01:00
James Cole
b58bdeccd2 New translations 2016-10-30 07:20:04 +01:00
James Cole
f260b9bdee New translations 2016-10-30 07:20:02 +01:00
James Cole
fb1bdc9ec5 New translations 2016-10-30 07:20:01 +01:00
James Cole
697eff48fc New translations 2016-10-30 07:20:00 +01:00
James Cole
c05019339a Approved. Step name: Proofread 2016-10-30 07:20:00 +01:00
James Cole
8438efaf41 Approved. Step name: Proofread 2016-10-30 07:19:59 +01:00
James Cole
81c019cc99 Translated 2016-10-30 07:19:59 +01:00
James Cole
c773fdc435 Approved. Step name: Proofread 2016-10-30 07:19:58 +01:00
James Cole
c1406f51f1 Approved. Step name: Proofread 2016-10-30 07:19:58 +01:00
James Cole
92affd3440 Approved. Step name: Proofread 2016-10-30 07:19:57 +01:00
James Cole
d3da0652ef Approved. Step name: Proofread 2016-10-30 07:19:57 +01:00
James Cole
e3fbbd6cf1 Translated 2016-10-30 07:19:56 +01:00
James Cole
fcff13470c Approved. Step name: Proofread 2016-10-30 07:19:56 +01:00
James Cole
e3061ee7e7 Approved. Step name: Proofread 2016-10-30 07:19:55 +01:00
James Cole
0ee305fc4a Approved. Step name: Proofread 2016-10-30 07:19:55 +01:00
James Cole
58b93fd0c4 Approved. Step name: Proofread 2016-10-30 07:19:53 +01:00
James Cole
b30217fa2d Approved. Step name: Proofread 2016-10-30 07:19:52 +01:00
James Cole
ae48eec3a2 Translated 2016-10-30 07:19:52 +01:00
James Cole
948233ba27 Translated 2016-10-30 07:19:52 +01:00
James Cole
c2db9b183a New translations 2016-10-30 07:19:51 +01:00
James Cole
6d2b88fa0b New translations 2016-10-30 07:19:51 +01:00
James Cole
1d5da825c5 New translations 2016-10-30 07:19:50 +01:00
James Cole
330c9b53d6 New translations 2016-10-30 07:19:50 +01:00
James Cole
751fe7d4fb New translations 2016-10-30 07:19:49 +01:00
James Cole
9df1fc6e5d New translations 2016-10-30 07:19:48 +01:00
James Cole
8d660f1701 New translations 2016-10-30 07:19:46 +01:00
James Cole
4d61d3c4aa New translations 2016-10-30 07:19:46 +01:00
James Cole
0457088c99 New translations 2016-10-30 07:19:45 +01:00
James Cole
8e575da74e New translations 2016-10-30 07:19:45 +01:00
James Cole
48ed28888e Translated 2016-10-30 07:19:43 +01:00
James Cole
4084b1124e Translated 2016-10-30 07:19:42 +01:00
James Cole
60ba607027 Translated 2016-10-30 07:19:42 +01:00
James Cole
3df2c11b4a Translated 2016-10-30 07:19:41 +01:00
James Cole
c93221923a Translated 2016-10-30 07:19:41 +01:00
James Cole
375317e932 New translations 2016-10-30 07:19:40 +01:00
James Cole
7ce527957a New translations 2016-10-30 07:19:40 +01:00
James Cole
6946521199 New translations 2016-10-30 07:19:39 +01:00
James Cole
18ee20e680 Update crowdin file [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 06:37:00 +01:00
James Cole
c53da15219 Update composer.lock in anticipation of new release.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 06:20:18 +01:00
James Cole
d4995e342f Fixed #330 2016-10-30 06:14:07 +01:00
James Cole
c9f14da294 Translations. 2016-10-29 17:30:55 +02:00
James Cole
e9c2446cba Debug log for #366 2016-10-29 16:16:10 +02:00
James Cole
35f179625c New queries for #366 2016-10-29 16:11:54 +02:00
James Cole
39749aa113 First code set for #330 2016-10-29 15:14:33 +02:00
James Cole
ba65e982fd Forgot to include model [skip ci] 2016-10-29 11:42:04 +02:00
James Cole
b50e5d7e59 Can also change destination in new rule. 2016-10-29 09:22:51 +02:00
James Cole
a3148dc172 Code for #321 2016-10-29 09:03:14 +02:00
James Cole
73f1491d2d Updates for translations. 2016-10-29 07:44:46 +02:00
James Cole
28eb54dc96 Initial split for report options. 2016-10-28 19:01:52 +02:00
James Cole
21fb426524 Make sure date is localised. 2016-10-28 18:16:30 +02:00
James Cole
d5710ca809 Update Crowdin configuration file 2016-10-28 14:06:41 +02:00
James Cole
0ba6cdda17 Merge pull request #364 from schoentoon/develop
Added dockerfile
2016-10-27 19:40:27 +02:00
James Cole
afdcfa8525 Tweak reports. 2016-10-26 19:45:10 +02:00
James Cole
5db4f8512b Tweak reports. 2016-10-26 19:37:19 +02:00
James Cole
dc0c1b73bc Better view for expenses. 2016-10-26 19:32:19 +02:00
James Cole
f999257095 New verify database routine. 2016-10-26 19:32:07 +02:00
James Cole
7182909e28 Keep the box [skip ci] 2016-10-26 16:54:52 +02:00
James Cole
fe3f015171 Add more stuff to ajax controllers, making report controller simpler. 2016-10-26 16:46:43 +02:00
Toon Schoenmakers
5bb668be63 Added dockerfile 2016-10-26 15:07:36 +02:00
James Cole
01de147900 Display message about common error. 2016-10-26 06:41:50 +02:00
James Cole
a7e5fcc806 Move some stuff over to AJAX thing. 2016-10-25 18:53:54 +02:00
James Cole
e2d187d74b Various small bug fixes. 2016-10-24 18:01:15 +02:00
James Cole
48b0620629 New help thing. 2016-10-23 17:33:53 +02:00
James Cole
19e9f382e4 Add some rounding to make forms more neat. 2016-10-23 16:56:18 +02:00
James Cole
446eaf6588 Some code cleanup [skip ci] 2016-10-23 14:58:39 +02:00
James Cole
78deb1420d Some fixes for bills. 2016-10-23 14:56:05 +02:00
James Cole
e092515dff Better export. 2016-10-23 12:55:07 +02:00
James Cole
81f6fef978 Add new line to files [skip ci] 2016-10-23 12:42:44 +02:00
James Cole
6a2f8fa9ee No use models directly. 2016-10-23 12:41:54 +02:00
James Cole
a79a8c8874 Various small upgrades. 2016-10-23 12:37:12 +02:00
James Cole
c39659b064 Remove a lot of references to user id. 2016-10-23 12:19:32 +02:00
James Cole
9a30fbd05a Move stuff to request classes for #339 2016-10-23 12:10:22 +02:00
James Cole
83f48418f6 Small updates [skip ci] 2016-10-23 09:57:04 +02:00
James Cole
bcd7b41c91 Simplified export. 2016-10-23 09:44:14 +02:00
James Cole
cefb7d12bc Merge branch 'release/4.1.3' 2016-10-22 22:45:27 +02:00
James Cole
3c0c15103e This fixes #361 2016-10-22 22:44:57 +02:00
James Cole
a8a8afc2be More for #339 2016-10-22 22:03:00 +02:00
James Cole
49e32abd3f Move some code for #339 2016-10-22 21:40:31 +02:00
James Cole
7977eefaca Merge branch 'release/4.1.2' 2016-10-22 20:52:54 +02:00
James Cole
f1fa6c3108 Fixed a bug in the store transaction routine. 2016-10-22 20:50:20 +02:00
James Cole
2fa0d55f39 Merge branch 'release/4.1.1' 2016-10-22 12:03:34 +02:00
James Cole
5bff509346 New translations.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 12:03:11 +02:00
James Cole
a147e9b74a Fix edit screen.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 11:53:34 +02:00
James Cole
0d87f7c4ca Better implementation of markdown.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 11:50:33 +02:00
James Cole
8c675615df Support markdown in notes.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 11:47:47 +02:00
James Cole
7edd1bff40 Merge branch 'release/4.1.0' 2016-10-22 10:21:15 +02:00
James Cole
3bfcb1f3ab New change log. [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 10:20:25 +02:00
James Cole
7b6c63e6a8 New version number [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 10:15:50 +02:00
James Cole
5500e5b0aa Remove debug classes 2016-10-22 10:13:56 +02:00
James Cole
e4d249e73c Piggy bank supports notes (#350) 2016-10-22 10:13:49 +02:00
James Cole
091f6e918b Fix some reported issues. 2016-10-22 09:44:47 +02:00
James Cole
5d9b68c3e7 Various code cleanup [skip ci] 2016-10-22 09:39:31 +02:00
James Cole
12a6a61100 Various code cleanup [skip ci] 2016-10-22 09:33:03 +02:00
James Cole
7ce3b8d4ef Updated events, fixes #345 2016-10-22 09:31:27 +02:00
James Cole
3d9b855849 Force larval 5.3.18 2016-10-22 07:52:17 +02:00
James Cole
2346d2ec05 Fine tuning split edit screens. 2016-10-22 07:28:31 +02:00
James Cole
a4c081c8a5 Fix unset variable. [skip ci] 2016-10-21 22:01:42 +02:00
James Cole
316980efbd Fix unset variable. [skip ci] 2016-10-21 22:00:45 +02:00
James Cole
a05bc0eed0 Fix route [skip ci] 2016-10-21 21:57:10 +02:00
James Cole
4d1c271da6 Renamed a route [skip ci] 2016-10-21 21:54:57 +02:00
James Cole
0dd7ecbfbe Remove code no longer used. 2016-10-21 21:43:12 +02:00
James Cole
0dc188b083 Removed old code 2016-10-21 21:41:50 +02:00
James Cole
6a553f77f3 Large update to fix split journals. 2016-10-21 21:41:31 +02:00
James Cole
a74cef439b For simplicity, split controller. 2016-10-21 19:20:03 +02:00
James Cole
9a3cd27700 Many updates to get split transactions and normal transactions working side by side. 2016-10-21 19:06:22 +02:00
James Cole
801c7c0ab6 Remove unused function. 2016-10-21 13:22:45 +02:00
James Cole
a95a4e783a Fix and simplify bill repos 2016-10-21 13:20:51 +02:00
James Cole
af1ee9db93 This fixes bills unpaid. 2016-10-21 07:29:25 +02:00
James Cole
fcdb6fd2a7 Do loop instead of while loop [skip ci] 2016-10-21 06:41:33 +02:00
James Cole
97c0fb389d More logs [skip ci] 2016-10-21 06:38:00 +02:00
James Cole
a9c3992331 Back to old method [skip ci] 2016-10-21 06:33:56 +02:00
James Cole
a38e057fa7 Rewrote some methods to fix #341 2016-10-21 06:26:12 +02:00
James Cole
f83aaf77f1 Improve bill things for issue #341 2016-10-20 21:40:45 +02:00
James Cole
d92768ecbf This code fixes #349 2016-10-20 19:10:43 +02:00
James Cole
b9308cd74a Test flash messages. 2016-10-20 16:51:05 +02:00
James Cole
78b577bc9d Better ip info [skip ci] 2016-10-18 06:50:35 +02:00
James Cole
7d247897ed Should correctly show user info. [skip ci] 2016-10-18 06:47:48 +02:00
James Cole
5dcbdec491 Update composer file and update routine. [skip ci] 2016-10-15 18:52:21 +02:00
James Cole
9bf980431e Remove unused methods. 2016-10-15 14:07:51 +02:00
James Cole
da60bfbcff Better text [skip ci] 2016-10-15 14:05:56 +02:00
James Cole
92553cbc7e Add icon, missing translation [skip ci] 2016-10-15 12:41:45 +02:00
James Cole
8e48e53f17 Restucturing some code. 2016-10-15 12:39:34 +02:00
James Cole
2f9a4bb79a Better text 2016-10-15 12:39:11 +02:00
James Cole
ac968dd6cd Extended the user admin. 2016-10-15 07:11:53 +02:00
James Cole
6e4f2c0c8a Small script to upgrade transactions. 2016-10-15 06:19:21 +02:00
James Cole
d662c18ed7 Fix sorting in chart. [skip ci] 2016-10-14 20:07:15 +02:00
James Cole
e4ea234707 New revenue accounts chart. 2016-10-14 20:01:17 +02:00
James Cole
0b526c0168 New revenue chart 2016-10-14 19:59:10 +02:00
James Cole
2acde5c72a Option to show deposit accounts on the front page. 2016-10-14 19:52:30 +02:00
James Cole
ec8cf2c459 New preferences screen. 2016-10-14 19:48:19 +02:00
James Cole
3598780d54 This should at least catch #357 2016-10-14 19:18:00 +02:00
James Cole
35dd8ac6e6 Revert "This should at least catch #357"
This reverts commit 5ff7c7ffab.
2016-10-14 19:16:39 +02:00
James Cole
5ff7c7ffab This should at least catch #357 2016-10-14 19:16:28 +02:00
289 changed files with 16302 additions and 10424 deletions

View File

@@ -2,7 +2,67 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.1.5] - 2016-11-01
### Changed
- Report parts are loaded using AJAX, making a lot of code more simple.
- Help content will fall back to English.
- Help content is translated through Crowdin.
### Fixed
- Issue #370
## [4.1.4] - 2016-10-30
### Added
- New Dockerfile thanks to @schoentoon
- Added changing the destination account as rule action.
- Added changing the source account as rule action.
- Can convert transactions into different types.
### Changed
- Changed the export routine to be more future-proof.
- Improved help routine.
- Integrated CrowdIn translations.
- Simplified reports
- Change error message to refer to solution.
### Fixed
- #367 thanks to @HungryFeline
- #366 thanks to @3mz3t
- #362 and #341 thanks to @bnw
- #355 thanks to @roberthorlings
## [4.1.3] - 2016-10-22
### Fixed
- Some event handlers called the wrong method.
## [4.1.2] - 2016-10-22
### Fixed
- A bug is fixed in the journal event handler that prevented Firefly III from actually storing journals.
## [4.1.1] - 2016-10-22
### Added
- Option to show deposit accounts on the front page.
- Script to upgrade split transactions
- Can now save notes on piggy banks.
- Extend user admin options.
- Run import jobs from the command line
### Changed
- New preferences screen layout.
### Deprecated
- ``firefly:import`` is now ``firefly:start-import``
### Removed
- Lots of old code
### Fixed
- #357, where non utf-8 files would break Firefly.
- Tab delimiter is not properly loaded from import configuration (@roberthorlings)
- System response to yearly bills
## [4.0.2] - 2016-10-14
### Added

42
Dockerfile Normal file
View File

@@ -0,0 +1,42 @@
FROM php:7-apache
RUN apt-get update -y && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev \
zlib1g-dev \
libjpeg62-turbo-dev \
libpng12-dev \
libicu-dev \
libmcrypt-dev \
libedit-dev \
libtidy-dev \
libxml2-dev \
libsqlite3-dev \
libbz2-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2
# Enable apache mod rewrite..
RUN a2enmod rewrite
# Setup the Composer installer
RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer && \
curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig && \
php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" && \
chmod +x /tmp/composer-setup.php && \
php /tmp/composer-setup.php && \
mv composer.phar /usr/local/bin/composer && \
rm -f /tmp/composer-setup.{php,sig}
ADD . /var/www/firefly-iii
RUN chown -R www-data:www-data /var/www/
ADD docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
USER www-data
WORKDIR /var/www/firefly-iii
RUN composer install --no-scripts --no-dev
USER root

View File

@@ -0,0 +1,152 @@
<?php
/**
* CreateImport.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Console\Commands;
use Artisan;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command;
use Log;
/**
* Class CreateImport
*
* @package FireflyIII\Console\Commands
*/
class CreateImport extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Use this command to create a new import. Your user ID can be found on the /profile page.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:create-import {file} {configuration} {--user=1} {--type=csv} {--start}';
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// find the file
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = $this->argument('file');
$configuration = $this->argument('configuration');
$user = $userRepository->find(intval($this->option('user')));
$cwd = getcwd();
$type = strtolower($this->option('type'));
if (!$this->validArguments()) {
return;
}
// try to parse configuration data:
$configurationData = json_decode(file_get_contents($configuration));
if (is_null($configurationData)) {
$this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return;
}
$this->info(sprintf('Going to create a job to import file: %s', $file));
$this->info(sprintf('Using configuration file: %s', $configuration));
$this->info(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
$this->info(sprintf('Type of import: %s', $type));
/** @var ImportJobRepositoryInterface $jobRepository */
$jobRepository = app(ImportJobRepositoryInterface::class, [$user]);
$job = $jobRepository->create($type);
$this->line(sprintf('Created job "%s"...', $job->key));
// put the file in the proper place:
Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]);
$this->line('Stored import data...');
// store the configuration in the job:
$job->configuration = $configurationData;
$job->status = 'settings_complete';
$job->save();
$this->line('Stored configuration...');
// if user wants to run it, do!
if ($this->option('start') === true) {
$this->line('The import will start in a moment. This process is not visible...');
Log::debug('Go for import!');
Artisan::call('firefly:start-import', ['key' => $job->key]);
$this->line('Done!');
}
return;
}
/**
* @return bool
*/
private function validArguments(): bool
{
// find the file
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = $this->argument('file');
$configuration = $this->argument('configuration');
$user = $userRepository->find(intval($this->option('user')));
$cwd = getcwd();
$validTypes = array_keys(config('firefly.import_formats'));
$type = strtolower($this->option('type'));
if (is_null($user->id)) {
$this->error(sprintf('There is no user with ID %d.', $this->option('user')));
return false;
}
if (!in_array($type, $validTypes)) {
$this->error(sprintf('Cannot import file of type "%s"', $type));
return false;
}
if (!file_exists($file)) {
$this->error(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd));
return false;
}
if (!file_exists($configuration)) {
$this->error(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return false;
}
return true;
}
}

View File

@@ -32,14 +32,14 @@ class Import extends Command
*
* @var string
*/
protected $description = 'Import stuff into Firefly III.';
protected $description = 'This will start a new import.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:import {key}';
protected $signature = 'firefly:start-import {key}';
/**
* Create a new command instance.
@@ -57,9 +57,12 @@ class Import extends Command
*/
public function handle()
{
Log::debug('Start start-import command');
$jobKey = $this->argument('key');
$job = ImportJob::whereKey($jobKey)->first();
if (!$this->isValid($job)) {
Log::error('Job is not valid for some reason. Exit.');
return;
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* UpgradeDatabase.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Console\Commands;
use DB;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Database\QueryException;
use Log;
/**
* Class UpgradeDatabase
*
* @package FireflyIII\Console\Commands
*/
class UpgradeDatabase extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Will run various commands to update database records.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:upgrade-database';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$this->setTransactionIdentifier();
}
/**
* This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird.
*/
private function setTransactionIdentifier()
{
$subQuery = TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->groupBy(['transaction_journals.id'])
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
$result = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived'))
->mergeBindings($subQuery->getQuery())
->where('t_count', '>', 2)
->select(['id', 't_count']);
$journalIds = array_unique($result->pluck('id')->toArray());
foreach ($journalIds as $journalId) {
// grab all positive transactiosn from this journal that are not deleted.
// for each one, grab the negative opposing one which has 0 as an identifier and give it the same identifier.
$identifier = 0;
$processed = [];
$transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
// find opposing:
$amount = bcmul(strval($transaction->amount), '-1');
try {
/** @var Transaction $opposing */
$opposing = Transaction
::where('transaction_journal_id', $journalId)
->where('amount', $amount)->where('identifier', '=', 0)
->whereNotIn('id', $processed)
->first();
} catch (QueryException $e) {
Log::error($e->getMessage());
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
$this->error('This field is required for Firefly III version ' . config('firefly.version') . ' to run.');
$this->error('Please run "php artisan migrate" to add this field to the table.');
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
break 2;
}
if (!is_null($opposing)) {
// give both a new identifier:
$transaction->identifier = $identifier;
$transaction->save();
$opposing->identifier = $identifier;
$opposing->save();
$processed[] = $transaction->id;
$processed[] = $opposing->id;
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
}
$identifier++;
}
}
}
}

View File

@@ -15,6 +15,7 @@ namespace FireflyIII\Console\Commands;
use Crypt;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
@@ -84,6 +85,9 @@ class VerifyDatabase extends Command
// transfers with budgets.
$this->reportTransfersBudgets();
// report on journals with the wrong types of accounts.
$this->reportIncorrectJournals();
}
/**
@@ -202,6 +206,45 @@ class VerifyDatabase extends Command
}
}
private function reportIncorrectJournals()
{
$configuration = [
// a withdrawal can not have revenue account:
TransactionType::WITHDRAWAL => [AccountType::REVENUE],
// deposit cannot have an expense account:
TransactionType::DEPOSIT => [AccountType::EXPENSE],
// transfer cannot have either:
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
];
foreach ($configuration as $transactionType => $accountTypes) {
$set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
->leftJoin('users', 'users.id', '=', 'transaction_journals.user_id')
->where('transaction_types.type', $transactionType)
->whereIn('account_types.type', $accountTypes)
->whereNull('transaction_journals.deleted_at')
->get(['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type', 'transaction_types.type']);
foreach ($set as $entry) {
$this->error(
sprintf(
'Transaction journal #%d (user #%d, %s) is of type "%s" but ' .
'is linked to a "%s". The transaction journal should be recreated.',
$entry->id,
$entry->user_id,
$entry->email,
$entry->type,
$entry->a_type
)
);
}
}
}
/**
* Any deleted transaction journals that have transactions that are NOT deleted:
*/

View File

@@ -13,9 +13,11 @@ declare(strict_types = 1);
namespace FireflyIII\Console;
use FireflyIII\Console\Commands\CreateImport;
use FireflyIII\Console\Commands\EncryptFile;
use FireflyIII\Console\Commands\Import;
use FireflyIII\Console\Commands\ScanAttachments;
use FireflyIII\Console\Commands\UpgradeDatabase;
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
use FireflyIII\Console\Commands\VerifyDatabase;
use Illuminate\Console\Scheduling\Schedule;
@@ -57,8 +59,10 @@ class Kernel extends ConsoleKernel
UpgradeFireflyInstructions::class,
VerifyDatabase::class,
Import::class,
CreateImport::class,
EncryptFile::class,
ScanAttachments::class,
UpgradeDatabase::class,
];

View File

@@ -1,221 +0,0 @@
<?php
/**
* Journal.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Crud\Split;
use FireflyIII\Events\TransactionStored;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Class Journal
*
* @package FireflyIII\Crud\Split
*/
class Journal implements JournalInterface
{
/** @var User */
private $user;
/**
* AttachmentRepository constructor.
*
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* @param $journal
*
* @return bool
*/
public function markAsComplete(TransactionJournal $journal)
{
$journal->completed = 1;
$journal->save();
return true;
}
/**
* @param TransactionJournal $journal
* @param array $transaction
* @param int $identifier
*
* @return Collection
*/
public function storeTransaction(TransactionJournal $journal, array $transaction, int $identifier): Collection
{
// store accounts (depends on type)
list($sourceAccount, $destinationAccount) = $this->storeAccounts($journal->transactionType->type, $transaction);
// store transaction one way:
/** @var Transaction $one */
$one = Transaction::create(
['account_id' => $sourceAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $transaction['amount'] * -1,
'description' => $transaction['description'], 'identifier' => $identifier]
);
$two = Transaction::create(
['account_id' => $destinationAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $transaction['amount'],
'description' => $transaction['description'], 'identifier' => $identifier]
);
if (strlen($transaction['category']) > 0) {
$category = Category::firstOrCreateEncrypted(['name' => $transaction['category'], 'user_id' => $journal->user_id]);
$one->categories()->save($category);
$two->categories()->save($category);
}
if (intval($transaction['budget_id']) > 0) {
$budget = Budget::find($transaction['budget_id']);
$one->budgets()->save($budget);
$two->budgets()->save($budget);
}
if ($transaction['piggy_bank_id'] > 0) {
$transaction['date'] = $journal->date->format('Y-m-d');
event(new TransactionStored($transaction));
}
return new Collection([$one, $two]);
}
/**
* @param TransactionJournal $journal
* @param array $data
*
* @return TransactionJournal
*/
public function updateJournal(TransactionJournal $journal, array $data): TransactionJournal
{
$journal->description = $data['journal_description'];
$journal->transaction_currency_id = $data['journal_currency_id'];
$journal->date = $data['date'];
$journal->interest_date = $data['interest_date'];
$journal->book_date = $data['book_date'];
$journal->process_date = $data['process_date'];
$journal->save();
// delete original transactions, and recreate them.
$journal->transactions()->delete();
$identifier = 0;
foreach ($data['transactions'] as $transaction) {
$this->storeTransaction($journal, $transaction, $identifier);
$identifier++;
}
$journal->completed = true;
$journal->save();
return $journal;
}
/**
* @param string $type
* @param array $transaction
*
* @return array
* @throws FireflyException
*/
private function storeAccounts(string $type, array $transaction): array
{
$sourceAccount = null;
$destinationAccount = null;
switch ($type) {
case TransactionType::WITHDRAWAL:
list($sourceAccount, $destinationAccount) = $this->storeWithdrawalAccounts($transaction);
break;
case TransactionType::DEPOSIT:
list($sourceAccount, $destinationAccount) = $this->storeDepositAccounts($transaction);
break;
case TransactionType::TRANSFER:
$sourceAccount = Account::where('user_id', $this->user->id)->where('id', $transaction['source_account_id'])->first();
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $transaction['destination_account_id'])->first();
break;
default:
throw new FireflyException('Cannot handle ' . e($type));
}
return [$sourceAccount, $destinationAccount];
}
/**
* @param array $data
*
* @return array
*/
private function storeDepositAccounts(array $data): array
{
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']);
if (isset($data['source_account_name']) && strlen($data['source_account_name']) > 0) {
$sourceType = AccountType::where('type', 'Revenue account')->first();
$sourceAccount = Account::firstOrCreateEncrypted(
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
);
return [$sourceAccount, $destinationAccount];
}
$sourceType = AccountType::where('type', 'Cash account')->first();
$sourceAccount = Account::firstOrCreateEncrypted(
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
);
return [$sourceAccount, $destinationAccount];
}
/**
* @param array $data
*
* @return array
*/
private function storeWithdrawalAccounts(array $data): array
{
$sourceAccount = Account::where('user_id', $this->user->id)->where('id', $data['source_account_id'])->first(['accounts.*']);
if (strlen($data['destination_account_name']) > 0) {
$destinationType = AccountType::where('type', 'Expense account')->first();
$destinationAccount = Account::firstOrCreateEncrypted(
[
'user_id' => $this->user->id,
'account_type_id' => $destinationType->id,
'name' => $data['destination_account_name'],
'active' => 1,
]
);
return [$sourceAccount, $destinationAccount];
}
$destinationType = AccountType::where('type', 'Cash account')->first();
$destinationAccount = Account::firstOrCreateEncrypted(
['user_id' => $this->user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
);
return [$sourceAccount, $destinationAccount];
}
}

View File

@@ -1,50 +0,0 @@
<?php
/**
* JournalInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Crud\Split;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* Interface JournalInterface
*
* @package FireflyIII\Crud\Split
*/
interface JournalInterface
{
/**
* @param $journal
*
* @return bool
*/
public function markAsComplete(TransactionJournal $journal);
/**
* @param TransactionJournal $journal
* @param array $transaction
* @param int $identifier
*
* @return Collection
*/
public function storeTransaction(TransactionJournal $journal, array $transaction, int $identifier): Collection;
/**
* @param TransactionJournal $journal
* @param array $data
*
* @return TransactionJournal
*/
public function updateJournal(TransactionJournal $journal, array $data): TransactionJournal;
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* UserIsConfirmed.php
* ConfirmedUser.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -17,11 +17,11 @@ use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class UserIsConfirmed
* Class ConfirmedUser
*
* @package FireflyIII\Events
*/
class UserIsConfirmed extends Event
class ConfirmedUser extends Event
{
use SerializesModels;
@@ -29,7 +29,7 @@ class UserIsConfirmed extends Event
public $user;
/**
* Create a new event instance.
* Create a new event instance. This event is triggered when a user confirms their new account.
*
* @param User $user
* @param string $ipAddress

View File

@@ -1,6 +1,6 @@
<?php
/**
* ResendConfirmation.php
* RegisteredUser.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -17,11 +17,11 @@ use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class ResendConfirmation
* Class RegisteredUser
*
* @package FireflyIII\Events
*/
class ResendConfirmation extends Event
class RegisteredUser extends Event
{
use SerializesModels;
@@ -29,7 +29,7 @@ class ResendConfirmation extends Event
public $user;
/**
* Create a new event instance.
* Create a new event instance. This event is triggered when a new user registers.
*
* @param User $user
* @param string $ipAddress

View File

@@ -1,6 +1,6 @@
<?php
/**
* UserIsDeleted.php
* ResentConfirmation.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -17,11 +17,11 @@ use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class UserIsDeleted
* Class ResentConfirmation
*
* @package FireflyIII\Events
*/
class UserIsDeleted extends Event
class ResentConfirmation extends Event
{
use SerializesModels;
@@ -29,7 +29,7 @@ class UserIsDeleted extends Event
public $user;
/**
* Create a new event instance.
* Create a new event instance. This event is triggered when a users wants a new confirmation.
*
* @param User $user
* @param string $ipAddress

View File

@@ -1,6 +1,6 @@
<?php
/**
* BudgetLimitStored.php
* StoredBudgetLimit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -18,11 +18,11 @@ use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels;
/**
* Class BudgetLimitStored
* Class StoredBudgetLimit
*
* @package FireflyIII\Events
*/
class BudgetLimitStored extends Event
class StoredBudgetLimit extends Event
{
use SerializesModels;

View File

@@ -1,6 +1,6 @@
<?php
/**
* TransactionJournalStored.php
* StoredTransactionJournal.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -17,11 +17,11 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
/**
* Class TransactionJournalStored
* Class StoredTransactionJournal
*
* @package FireflyIII\Events
*/
class TransactionJournalStored extends Event
class StoredTransactionJournal extends Event
{
use SerializesModels;

View File

@@ -1,41 +0,0 @@
<?php
/**
* TransactionStored.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Events;
use Illuminate\Queue\SerializesModels;
/**
* Class TransactionJournalStored
*
* @package FireflyIII\Events
*/
class TransactionStored extends Event
{
use SerializesModels;
public $transaction = [];
/**
* Create a new event instance.
*
* @param array $transaction
*/
public function __construct(array $transaction)
{
//
$this->transaction = $transaction;
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* BudgetLimitUpdated.php
* UpdatedBudgetLimit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -18,11 +18,11 @@ use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels;
/**
* Class BudgetLimitUpdated
* Class UpdatedBudgetLimit
*
* @package FireflyIII\Events
*/
class BudgetLimitUpdated extends Event
class UpdatedBudgetLimit extends Event
{
use SerializesModels;

View File

@@ -1,6 +1,6 @@
<?php
/**
* TransactionJournalUpdated.php
* UpdatedTransactionJournal.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -17,11 +17,11 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
/**
* Class TransactionJournalUpdated
* Class UpdatedTransactionJournal
*
* @package FireflyIII\Events
*/
class TransactionJournalUpdated extends Event
class UpdatedTransactionJournal extends Event
{
use SerializesModels;

View File

@@ -1,42 +0,0 @@
<?php
/**
* UserRegistration.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Events;
use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class UserRegistration
*
* @package FireflyIII\Events
*/
class UserRegistration extends Event
{
use SerializesModels;
public $ipAddress;
public $user;
/**
* Create a new event instance.
*
* @param User $user
* @param string $ipAddress
*/
public function __construct(User $user, string $ipAddress)
{
$this->user = $user;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -13,11 +13,10 @@ declare(strict_types = 1);
namespace FireflyIII\Export\Collector;
use Amount;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Collection;
@@ -31,12 +30,14 @@ use Storage;
*/
class AttachmentCollector extends BasicCollector implements CollectorInterface
{
/** @var string */
private $explanationString = '';
/** @var Carbon */
private $end;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
/** @var AttachmentRepositoryInterface */
private $repository;
/** @var Carbon */
private $start;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $uploadDisk;
@@ -69,34 +70,17 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
$this->exportAttachment($attachment);
}
// put the explanation string in a file and attach it as well.
$file = $this->job->key . '-Source of all your attachments explained.txt';
$this->exportDisk->put($file, $this->explanationString);
$this->getFiles()->push($file);
return true;
}
/**
* @param Attachment $attachment
* @param Carbon $start
* @param Carbon $end
*/
private function explain(Attachment $attachment)
public function setDates(Carbon $start, Carbon $end)
{
/** @var TransactionJournal $journal */
$journal = $attachment->attachable;
$args = [
'attachment_name' => e($attachment->filename),
'attachment_id' => $attachment->id,
'type' => strtolower($journal->transactionType->type),
'description' => e($journal->description),
'journal_id' => $journal->id,
'date' => $journal->date->formatLocalized(strval(trans('config.month_and_day'))),
'amount' => Amount::formatJournal($journal, false),
];
$string = trans('firefly.attachment_explanation', $args) . "\n";
Log::debug('Appended explanation string', ['string' => $string]);
$this->explanationString .= $string;
$this->start = $start;
$this->end = $end;
}
/**
@@ -112,10 +96,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
$decrypted = Crypt::decrypt($this->uploadDisk->get($file));
$exportFile = $this->exportFileName($attachment);
$this->exportDisk->put($exportFile, $decrypted);
$this->getFiles()->push($exportFile);
$this->getEntries()->push($exportFile);
// explain:
$this->explain($attachment);
} catch (DecryptException $e) {
Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage());
}
@@ -143,7 +125,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
*/
private function getAttachments(): Collection
{
$attachments = $this->repository->get();
$attachments = $this->repository->getBetween($this->start, $this->end);
return $attachments;
}

View File

@@ -27,7 +27,7 @@ class BasicCollector
/** @var ExportJob */
protected $job;
/** @var Collection */
private $files;
private $entries;
/**
* BasicCollector constructor.
@@ -36,24 +36,24 @@ class BasicCollector
*/
public function __construct(ExportJob $job)
{
$this->files = new Collection;
$this->job = $job;
$this->entries = new Collection;
$this->job = $job;
}
/**
* @return Collection
*/
public function getFiles(): Collection
public function getEntries(): Collection
{
return $this->files;
return $this->entries;
}
/**
* @param Collection $files
* @param Collection $entries
*/
public function setFiles(Collection $files)
public function setEntries(Collection $entries)
{
$this->files = $files;
$this->entries = $entries;
}

View File

@@ -25,7 +25,7 @@ interface CollectorInterface
/**
* @return Collection
*/
public function getFiles(): Collection;
public function getEntries(): Collection;
/**
* @return bool
@@ -33,9 +33,9 @@ interface CollectorInterface
public function run(): bool;
/**
* @param Collection $files
* @param Collection $entries
*
*/
public function setFiles(Collection $files);
public function setEntries(Collection $entries);
}

View File

@@ -0,0 +1,348 @@
<?php
/**
* JournalCollector.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Collector;
use Carbon\Carbon;
use Crypt;
use DB;
use FireflyIII\Models\Transaction;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class JournalCollector
*
* @package FireflyIII\Export\Collector
*/
class JournalCollector extends BasicCollector implements CollectorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/** @var Collection */
private $workSet;
/**
* @return bool
*/
public function run(): bool
{
/*
* Instead of collecting journals we collect transactions for the given accounts.
* We left join the OPPOSING transaction AND some journal data.
* After that we complement this info with budgets, categories, etc.
*
* This is way more efficient and will also work on split journals.
*/
$this->getWorkSet();
/*
* Extract:
* possible budget ids for journals
* possible category ids journals
* possible budget ids for transactions
* possible category ids for transactions
*
* possible IBAN and account numbers?
*
*/
$journals = $this->extractJournalIds();
$transactions = $this->extractTransactionIds();
// extend work set with category data from journals:
$this->categoryDataForJournals($journals);
// extend work set with category cate from transactions (overrules journals):
$this->categoryDataForTransactions($transactions);
// same for budgets:
$this->budgetDataForJournals($journals);
$this->budgetDataForTransactions($transactions);
$this->setEntries($this->workSet);
return true;
}
/**
* @param Collection $accounts
*/
public function setAccounts(Collection $accounts)
{
$this->accounts = $accounts;
}
/**
* @param Carbon $start
* @param Carbon $end
*/
public function setDates(Carbon $start, Carbon $end)
{
$this->start = $start;
$this->end = $end;
}
/**
* @param array $journals
*
* @return bool
*/
private function budgetDataForJournals(array $journals): bool
{
$set = DB::table('budget_transaction_journal')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->whereIn('budget_transaction_journal.transaction_journal_id', $journals)
->get(
[
'budget_transaction_journal.budget_id',
'budget_transaction_journal.transaction_journal_id',
'budgets.name',
'budgets.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_journal_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
if (isset($array[$obj->transaction_journal_id])) {
$obj->budget_id = $array[$obj->transaction_journal_id]['id'];
$obj->budget_name = $array[$obj->transaction_journal_id]['name'];
}
}
);
return true;
}
/**
* @param array $transactions
*
* @return bool
*/
private function budgetDataForTransactions(array $transactions): bool
{
$set = DB::table('budget_transaction')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction.budget_id')
->whereIn('budget_transaction.transaction_id', $transactions)
->get(
[
'budget_transaction.budget_id',
'budget_transaction.transaction_id',
'budgets.name',
'budgets.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
// first transaction
if (isset($array[$obj->id])) {
$obj->budget_id = $array[$obj->id]['id'];
$obj->budget_name = $array[$obj->id]['name'];
}
}
);
return true;
}
/**
* @param array $journals
*
* @return bool
*/
private function categoryDataForJournals(array $journals): bool
{
$set = DB::table('category_transaction_journal')
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->whereIn('category_transaction_journal.transaction_journal_id', $journals)
->get(
[
'category_transaction_journal.category_id',
'category_transaction_journal.transaction_journal_id',
'categories.name',
'categories.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_journal_id] = ['id' => $obj->category_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
if (isset($array[$obj->transaction_journal_id])) {
$obj->category_id = $array[$obj->transaction_journal_id]['id'];
$obj->category_name = $array[$obj->transaction_journal_id]['name'];
}
}
);
return true;
}
/**
* @param array $transactions
*
* @return bool
*/
private function categoryDataForTransactions(array $transactions): bool
{
$set = DB::table('category_transaction')
->leftJoin('categories', 'categories.id', '=', 'category_transaction.category_id')
->whereIn('category_transaction.transaction_id', $transactions)
->get(
[
'category_transaction.category_id',
'category_transaction.transaction_id',
'categories.name',
'categories.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_id] = ['id' => $obj->category_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
// first transaction
if (isset($array[$obj->id])) {
$obj->category_id = $array[$obj->id]['id'];
$obj->category_name = $array[$obj->id]['name'];
}
}
);
return true;
}
/**
* @return array
*/
private function extractJournalIds(): array
{
return $this->workSet->pluck('transaction_journal_id')->toArray();
}
/**
* @return array
*/
private function extractTransactionIds()
{
$set = $this->workSet->pluck('id')->toArray();
$opposing = $this->workSet->pluck('opposing_id')->toArray();
$complete = $set + $opposing;
return array_unique($complete);
}
/**
*
*/
private function getWorkSet()
{
$accountIds = $this->accounts->pluck('id')->toArray();
$this->workSet = Transaction
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'transactions AS opposing', function (JoinClause $join) {
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'))
->where('transactions.identifier', '=', 'opposing.identifier');
}
)
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id')
->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id')
->whereIn('transactions.account_id', $accountIds)
->where('transaction_journals.user_id', $this->job->user_id)
->where('transaction_journals.date', '>=', $this->start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->end->format('Y-m-d'))
->where('transaction_journals.completed', 1)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->whereNull('opposing.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transactions.identifier', 'ASC')
->get(
[
'transactions.id',
'transactions.amount',
'transactions.description',
'transactions.account_id',
'accounts.name as account_name',
'accounts.encrypted as account_name_encrypted',
'transactions.identifier',
'opposing.id as opposing_id',
'opposing.amount AS opposing_amount',
'opposing.description as opposing_description',
'opposing.account_id as opposing_account_id',
'opposing_accounts.name as opposing_account_name',
'opposing_accounts.encrypted as opposing_account_encrypted',
'opposing.identifier as opposing_identifier',
'transaction_journals.id as transaction_journal_id',
'transaction_journals.date',
'transaction_journals.description as journal_description',
'transaction_journals.encrypted as journal_encrypted',
'transaction_journals.transaction_type_id',
'transaction_types.type as transaction_type',
'transaction_journals.transaction_currency_id',
'transaction_currencies.code AS transaction_currency_code',
]
);
}
}

View File

@@ -26,16 +26,14 @@ use Storage;
*/
class UploadCollector extends BasicCollector implements CollectorInterface
{
/** @var string */
private $expected;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
private $importKeys = [];
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $uploadDisk;
/** @var string */
private $vintageFormat;
/**
*
* AttachmentCollector constructor.
*
* @param ExportJob $job
@@ -51,50 +49,74 @@ class UploadCollector extends BasicCollector implements CollectorInterface
$this->exportDisk = Storage::disk('export');
// file names associated with the old import routine.
$this->expected = 'csv-upload-' . auth()->user()->id . '-';
$this->vintageFormat = sprintf('csv-upload-%d-', auth()->user()->id);
// for the new import routine:
$this->getImportKeys();
}
/**
* Is called from the outside to actually start the export.
*
* @return bool
*/
public function run(): bool
{
// grab upload directory.
$files = $this->uploadDisk->files();
// collect old upload files (names beginning with "csv-upload".
$this->collectVintageUploads();
foreach ($files as $entry) {
$this->processUpload($entry);
// then collect current upload files:
$this->collectModernUploads();
return true;
}
/**
* This method collects all the uploads that are uploaded using the new importer. So after the summer of 2016.
*
* @return bool
*/
private function collectModernUploads(): bool
{
$set = $this->job->user->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
$keys = [];
if ($set->count() > 0) {
$keys = $set->pluck('key')->toArray();
}
foreach ($keys as $key) {
$this->processModernUpload($key);
}
return true;
}
/**
* This method collects all the uploads that are uploaded using the "old" importer. So from before the summer of 2016.
*
* @return bool
*/
private function getImportKeys()
private function collectVintageUploads():bool
{
$set = auth()->user()->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
if ($set->count() > 0) {
$keys = $set->pluck('key')->toArray();
$this->importKeys = $keys;
// grab upload directory.
$files = $this->uploadDisk->files();
foreach ($files as $entry) {
$this->processVintageUpload($entry);
}
Log::debug('Valid import keys are ', $this->importKeys);
return true;
}
/**
* This method tells you when the vintage upload file was actually uploaded.
*
* @param string $entry
*
* @return string
*/
private function getOriginalUploadDate(string $entry): string
private function getVintageUploadDate(string $entry): string
{
// this is an original upload.
$parts = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry));
$parts = explode('-', str_replace(['.csv.encrypted', $this->vintageFormat], '', $entry));
$originalUpload = intval($parts[1]);
$date = date('Y-m-d \a\t H-i-s', $originalUpload);
@@ -102,33 +124,17 @@ class UploadCollector extends BasicCollector implements CollectorInterface
}
/**
* Tells you if a file name is a vintage upload.
*
* @param string $entry
*
* @return bool
*/
private function isImportFile(string $entry): bool
private function isVintageImport(string $entry): bool
{
$name = str_replace('.upload', '', $entry);
if (in_array($name, $this->importKeys)) {
Log::debug(sprintf('Import file "%s" is in array', $name), $this->importKeys);
return true;
}
Log::debug(sprintf('Import file "%s" is NOT in array', $name), $this->importKeys);
return false;
}
/**
* @param string $entry
*
* @return bool
*/
private function isOldImport(string $entry): bool
{
$len = strlen($this->expected);
$len = strlen($this->vintageFormat);
// file is part of the old import routine:
if (substr($entry, 0, $len) === $this->expected) {
if (substr($entry, 0, $len) === $this->vintageFormat) {
return true;
}
@@ -137,49 +143,62 @@ class UploadCollector extends BasicCollector implements CollectorInterface
}
/**
* @param $entry
* @param string $key
*
* @return bool
*/
private function processUpload(string $entry)
{
// file is old import:
if ($this->isOldImport($entry)) {
$this->saveOldImportFile($entry);
}
// file is current import.
if ($this->isImportFile($entry)) {
$this->saveImportFile($entry);
}
}
/**
* @param string $entry
*/
private function saveImportFile(string $entry)
private function processModernUpload(string $key): bool
{
// find job associated with import file:
$name = str_replace('.upload', '', $entry);
$job = auth()->user()->importJobs()->where('key', $name)->first();
$content = '';
try {
$content = Crypt::decrypt($this->uploadDisk->get($entry));
} catch (DecryptException $e) {
Log::error('Could not decrypt old import file ' . $entry . '. Skipped because ' . $e->getMessage());
$job = $this->job->user->importJobs()->where('key', $key)->first();
if (is_null($job)) {
return false;
}
if (!is_null($job) && strlen($content) > 0) {
// find the file for this import:
$content = '';
try {
$content = Crypt::decrypt($this->uploadDisk->get(sprintf('%s.upload', $key)));
} catch (DecryptException $e) {
Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage()));
}
if (strlen($content) > 0) {
// add to export disk.
$date = $job->created_at->format('Y-m-d');
$file = sprintf('%s-Old %s import dated %s.%s', $this->job->key, strtoupper($job->file_type), $date, $job->file_type);
$this->exportDisk->put($file, $content);
$this->getFiles()->push($file);
$this->getEntries()->push($file);
}
return true;
}
/**
* If the file is a vintage upload, process it.
*
* @param string $entry
*
* @return bool
*/
private function processVintageUpload(string $entry): bool
{
if ($this->isVintageImport($entry)) {
$this->saveVintageImportFile($entry);
return true;
}
return false;
}
/**
* This will store the content of the old vintage upload somewhere.
*
* @param string $entry
*/
private function saveOldImportFile(string $entry)
private function saveVintageImportFile(string $entry)
{
$content = '';
try {
@@ -190,10 +209,10 @@ class UploadCollector extends BasicCollector implements CollectorInterface
if (strlen($content) > 0) {
// add to export disk.
$date = $this->getOriginalUploadDate($entry);
$date = $this->getVintageUploadDate($entry);
$file = $this->job->key . '-Old import dated ' . $date . '.csv';
$this->exportDisk->put($file, $content);
$this->getFiles()->push($file);
$this->getEntries()->push($file);
}
}

View File

@@ -1,68 +0,0 @@
<?php
/**
* ConfigurationFile.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Models\ExportJob;
use Storage;
/**
* Class ConfigurationFile
*
* @package FireflyIII\Export
*/
class ConfigurationFile
{
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
/** @var ExportJob */
private $job;
/**
* ConfigurationFile constructor.
*
* @param ExportJob $job
*/
public function __construct(ExportJob $job)
{
$this->job = $job;
$this->exportDisk = Storage::disk('export');
}
/**
* @return string
*/
public function make(): string
{
$fields = array_keys(Entry::getFieldsAndTypes());
$types = Entry::getFieldsAndTypes();
$configuration = [
'date-format' => 'Y-m-d', // unfortunately, this is hard-coded.
'has-headers' => true,
'map' => [], // we could build a map if necessary for easy re-import.
'roles' => [],
'mapped' => [],
'specifix' => [],
];
foreach ($fields as $field) {
$configuration['roles'][] = $types[$field];
}
$file = $this->job->key . '-configuration.json';
$this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT));
return $file;
}
}

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Crypt;
/**
* To extend the exported object, in case of new features in Firefly III for example,
@@ -35,98 +34,77 @@ use Illuminate\Support\Collection;
*/
final class Entry
{
/** @var string */
public $amount;
/** @var EntryBill */
public $bill;
/** @var EntryBudget */
public $budget;
/** @var EntryCategory */
public $category;
/** @var string */
// @formatter:off
public $journal_id;
public $date;
/** @var string */
public $description;
/** @var EntryAccount */
public $destinationAccount;
/** @var Collection */
public $destinationAccounts;
/** @var EntryAccount */
public $sourceAccount;
/** @var Collection */
public $sourceAccounts;
public $currency_code;
public $amount;
public $transaction_type;
public $source_account_id;
public $source_account_name;
public $destination_account_id;
public $destination_account_name;
public $budget_id;
public $budget_name;
public $category_id;
public $category_name;
// @formatter:on
/**
* Entry constructor.
*/
private function __construct()
{
$this->sourceAccounts = new Collection;
$this->destinationAccounts = new Collection;
}
/**
* @param TransactionJournal $journal
* @param $object
*
* @return Entry
*/
public static function fromJournal(TransactionJournal $journal)
public static function fromObject($object): Entry
{
$entry = new self;
$entry = new self;
$entry->description = $journal->description;
$entry->date = $journal->date->format('Y-m-d');
$entry->amount = TransactionJournal::amount($journal);
// journal information:
$entry->journal_id = $object->transaction_journal_id;
$entry->description = $object->journal_encrypted === 1 ? Crypt::decrypt($object->journal_description) : $object->journal_description;
$entry->amount = round($object->amount, 2); // always positive
$entry->date = $object->date;
$entry->transaction_type = $object->transaction_type;
$entry->currency_code = $object->transaction_currency_code;
$entry->budget = new EntryBudget($journal->budgets->first());
$entry->category = new EntryCategory($journal->categories->first());
$entry->bill = new EntryBill($journal->bill);
// source information:
$entry->source_account_id = $object->account_id;
$entry->source_account_name = $object->account_name_encrypted === 1 ? Crypt::decrypt($object->account_name) : $object->account_name;
$sources = TransactionJournal::sourceAccountList($journal);
$destinations = TransactionJournal::destinationAccountList($journal);
$entry->sourceAccount = new EntryAccount($sources->first());
$entry->destinationAccount = new EntryAccount($destinations->first());
foreach ($sources as $source) {
$entry->sourceAccounts->push(new EntryAccount($source));
}
// destination information
$entry->destination_account_id = $object->opposing_account_id;
$entry->destination_account_name = $object->opposing_account_encrypted === 1 ? Crypt::decrypt($object->opposing_account_name)
: $object->opposing_account_name;
foreach ($destinations as $destination) {
$entry->destinationAccounts->push(new EntryAccount($destination));
// category and budget
$entry->category_id = $object->category_id ?? '';
$entry->category_name = $object->category_name ?? '';
$entry->budget_id = $object->budget_id ?? '';
$entry->budget_name = $object->budget_name ?? '';
// update description when transaction description is different:
if (!is_null($object->description) && $object->description != $entry->description) {
$entry->description = $entry->description . ' (' . $object->description . ')';
}
return $entry;
}
/**
* @return array
*/
public static function getFieldsAndTypes(): array
{
// key = field name (see top of class)
// value = field type (see csv.php under 'roles')
return [
'description' => 'description',
'amount' => 'amount',
'date' => 'date-transaction',
'source_account_id' => 'account-id',
'source_account_name' => 'account-name',
'source_account_iban' => 'account-iban',
'source_account_type' => '_ignore',
'source_account_number' => 'account-number',
'destination_account_id' => 'opposing-id',
'destination_account_name' => 'opposing-name',
'destination_account_iban' => 'opposing-iban',
'destination_account_type' => '_ignore',
'destination_account_number' => 'account-number',
'budget_id' => 'budget-id',
'budget_name' => 'budget-name',
'category_id' => 'category-id',
'category_name' => 'category-name',
'bill_id' => 'bill-id',
'bill_name' => 'bill-name',
];
}
}

View File

@@ -1,49 +0,0 @@
<?php
/**
* EntryAccount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Account;
/**
* Class EntryAccount
*
* @package FireflyIII\Export\Entry
*/
class EntryAccount
{
/** @var int */
public $accountId;
/** @var string */
public $iban;
/** @var string */
public $name;
/** @var string */
public $number;
/** @var string */
public $type;
/**
* EntryAccount constructor.
*
* @param Account $account
*/
public function __construct(Account $account)
{
$this->accountId = $account->id;
$this->name = $account->name;
$this->iban = $account->iban;
$this->type = $account->accountType->type;
$this->number = $account->getMeta('accountNumber');
}
}

View File

@@ -1,43 +0,0 @@
<?php
/**
* EntryBill.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Bill;
/**
* Class EntryBill
*
* @package FireflyIII\Export\Entry
*/
class EntryBill
{
/** @var int */
public $billId = '';
/** @var string */
public $name = '';
/**
* EntryBill constructor.
*
* @param Bill $bill
*/
public function __construct(Bill $bill = null)
{
if (!is_null($bill)) {
$this->billId = $bill->id;
$this->name = $bill->name;
}
}
}

View File

@@ -1,43 +0,0 @@
<?php
/**
* EntryBudget.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Budget;
/**
* Class EntryBudget
*
* @package FireflyIII\Export\Entry
*/
class EntryBudget
{
/** @var int */
public $budgetId = '';
/** @var string */
public $name = '';
/**
* EntryBudget constructor.
*
* @param Budget $budget
*/
public function __construct(Budget $budget = null)
{
if (!is_null($budget)) {
$this->budgetId = $budget->id;
$this->name = $budget->name;
}
}
}

View File

@@ -1,42 +0,0 @@
<?php
/**
* EntryCategory.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Category;
/**
* Class EntryCategory
*
* @package FireflyIII\Export\Entry
*/
class EntryCategory
{
/** @var int */
public $categoryId = '';
/** @var string */
public $name = '';
/**
* EntryCategory constructor.
*
* @param Category $category
*/
public function __construct(Category $category = null)
{
if (!is_null($category)) {
$this->categoryId = $category->id;
$this->name = $category->name;
}
}
}

View File

@@ -16,7 +16,6 @@ namespace FireflyIII\Export\Exporter;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Export\Entry\EntryAccount;
use FireflyIII\Models\ExportJob;
use Illuminate\Support\Collection;
use League\Csv\Writer;
use SplFileObject;
@@ -62,110 +61,24 @@ class CsvExporter extends BasicExporter implements ExporterInterface
$writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w');
$rows = [];
// Count the maximum number of sources and destinations each entry has. May need to expand the number of export fields:
$maxSourceAccounts = 1;
$maxDestAccounts = 1;
/** @var Entry $entry */
foreach ($this->getEntries() as $entry) {
$sources = $entry->sourceAccounts->count();
$destinations = $entry->destinationAccounts->count();
$maxSourceAccounts = max($maxSourceAccounts, $sources);
$maxDestAccounts = max($maxDestAccounts, $destinations);
}
$rows[] = array_keys($this->getFieldsAndTypes($maxSourceAccounts, $maxDestAccounts));
// get field names for header row:
$first = $this->getEntries()->first();
$headers = array_keys(get_object_vars($first));
$rows[] = $headers;
/** @var Entry $entry */
foreach ($this->getEntries() as $entry) {
// order is defined in Entry::getFieldsAndTypes.
$current = [$entry->description, $entry->amount, $entry->date];
$sourceData = $this->getAccountData($maxSourceAccounts, $entry->sourceAccounts);
$current = array_merge($current, $sourceData);
$destData = $this->getAccountData($maxDestAccounts, $entry->destinationAccounts);
$current = array_merge($current, $destData);
$rest = [$entry->budget->budgetId, $entry->budget->name, $entry->category->categoryId, $entry->category->name, $entry->bill->billId,
$entry->bill->name];
$current = array_merge($current, $rest);
$rows[] = $current;
$line = [];
foreach ($headers as $header) {
$line[] = $entry->$header;
}
$rows[] = $line;
}
$writer->insertAll($rows);
return true;
}
/**
* @param int $max
* @param Collection $accounts
*
* @return array
*/
private function getAccountData(int $max, Collection $accounts): array
{
$current = [];
for ($i = 0; $i < $max; $i++) {
/** @var EntryAccount $source */
$source = $accounts->get($i);
$currentId = '';
$currentName = '';
$currentIban = '';
$currentType = '';
$currentNumber = '';
if ($source) {
$currentId = $source->accountId;
$currentName = $source->name;
$currentIban = $source->iban;
$currentType = $source->type;
$currentNumber = $source->number;
}
$current[] = $currentId;
$current[] = $currentName;
$current[] = $currentIban;
$current[] = $currentType;
$current[] = $currentNumber;
}
unset($source);
return $current;
}
/**
* @param int $sources
* @param int $destinations
*
* @return array
*/
private function getFieldsAndTypes(int $sources, int $destinations): array
{
// key = field name (see top of class)
// value = field type (see csv.php under 'roles')
$array = [
'description' => 'description',
'amount' => 'amount',
'date' => 'date-transaction',
];
for ($i = 0; $i < $sources; $i++) {
$array['source_account_' . $i . '_id'] = 'account-id';
$array['source_account_' . $i . '_name'] = 'account-name';
$array['source_account_' . $i . '_iban'] = 'account-iban';
$array['source_account_' . $i . '_type'] = '_ignore';
$array['source_account_' . $i . '_number'] = 'account-number';
}
for ($i = 0; $i < $destinations; $i++) {
$array['destination_account_' . $i . '_id'] = 'account-id';
$array['destination_account_' . $i . '_name'] = 'account-name';
$array['destination_account_' . $i . '_iban'] = 'account-iban';
$array['destination_account_' . $i . '_type'] = '_ignore';
$array['destination_account_' . $i . '_number'] = 'account-number';
}
$array['budget_id'] = 'budget-id';
$array['budget_name'] = 'budget-name';
$array['category_id'] = 'category-id';
$array['category_name'] = 'category-name';
$array['bill_id'] = 'bill-id';
$array['bill_name'] = 'bill-name';
return $array;
}
private function tempFile()
{

View File

@@ -15,13 +15,13 @@ namespace FireflyIII\Export;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Export\Collector\AttachmentCollector;
use FireflyIII\Export\Collector\JournalCollector;
use FireflyIII\Export\Collector\UploadCollector;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Collection;
use Log;
use Storage;
use ZipArchive;
@@ -40,8 +40,6 @@ class Processor
/** @var bool */
public $includeAttachments;
/** @var bool */
public $includeConfig;
/** @var bool */
public $includeOldUploads;
/** @var ExportJob */
public $job;
@@ -68,7 +66,6 @@ class Processor
$this->accounts = $settings['accounts'];
$this->exportFormat = $settings['exportFormat'];
$this->includeAttachments = $settings['includeAttachments'];
$this->includeConfig = $settings['includeConfig'];
$this->includeOldUploads = $settings['includeOldUploads'];
$this->job = $settings['job'];
$this->journals = new Collection;
@@ -84,8 +81,9 @@ class Processor
{
/** @var AttachmentCollector $attachmentCollector */
$attachmentCollector = app(AttachmentCollector::class, [$this->job]);
$attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']);
$attachmentCollector->run();
$this->files = $this->files->merge($attachmentCollector->getFiles());
$this->files = $this->files->merge($attachmentCollector->getEntries());
return true;
}
@@ -95,9 +93,13 @@ class Processor
*/
public function collectJournals(): bool
{
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$this->journals = $repository->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']);
/** @var JournalCollector $collector */
$collector = app(JournalCollector::class, [$this->job]);
$collector->setDates($this->settings['startDate'], $this->settings['endDate']);
$collector->setAccounts($this->settings['accounts']);
$collector->run();
$this->journals = $collector->getEntries();
Log::debug(sprintf('Count %d journals in collectJournals() ', $this->journals->count()));
return true;
}
@@ -111,7 +113,7 @@ class Processor
$uploadCollector = app(UploadCollector::class, [$this->job]);
$uploadCollector->run();
$this->files = $this->files->merge($uploadCollector->getFiles());
$this->files = $this->files->merge($uploadCollector->getEntries());
return true;
}
@@ -122,22 +124,11 @@ class Processor
public function convertJournals(): bool
{
$count = 0;
/** @var TransactionJournal $journal */
foreach ($this->journals as $journal) {
$this->exportEntries->push(Entry::fromJournal($journal));
foreach ($this->journals as $object) {
$this->exportEntries->push(Entry::fromObject($object));
$count++;
}
return true;
}
/**
* @return bool
*/
public function createConfigFile(): bool
{
$this->configurationMaker = app(ConfigurationFile::class, [$this->job]);
$this->files->push($this->configurationMaker->make());
Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count()));
return true;
}

View File

@@ -24,7 +24,6 @@ use Illuminate\Support\Collection;
*/
interface AccountChartGeneratorInterface
{
/**
* @param Collection $accounts
* @param Carbon $start
@@ -43,6 +42,15 @@ interface AccountChartGeneratorInterface
*/
public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Account $account
* @param array $labels

View File

@@ -83,6 +83,30 @@ class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array
{
$data = [
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.earned'),
'data' => []]]];
foreach ($accounts as $account) {
if ($account->difference > 0) {
$data['labels'][] = $account->name;
$data['datasets'][0]['data'][] = $account->difference;
}
}
return $data;
}
/**
* @param Account $account
* @param array $labels
@@ -105,5 +129,4 @@ class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
return $data;
}
}

View File

@@ -1,52 +0,0 @@
<?php
/**
* AttachUserRole.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UserRegistration;
use FireflyIII\Repositories\User\UserRepositoryInterface;
/**
* Class AttachUserRole
*
* @package FireflyIII\Handlers\Events
*/
class AttachUserRole
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserRegistration $event
*/
public function handle(UserRegistration $event)
{
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
// first user ever?
if ($repository->count() == 1) {
$repository->attachRole($event->user, 'owner');
}
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* BudgetLimitEventHandler.php
* BudgetEventHandler.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
@@ -13,36 +13,30 @@ declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\BudgetLimitStored;
use FireflyIII\Events\BudgetLimitUpdated;
use FireflyIII\Events\StoredBudgetLimit;
use FireflyIII\Events\UpdatedBudgetLimit;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\QueryException;
use Log;
/**
* Class BudgetLimitEventHandler
* Handles budget related events.
*
* Class BudgetEventHandler
*
* @package FireflyIII\Handlers\Events
*/
class BudgetLimitEventHandler
class BudgetEventHandler
{
/**
* Create the event listener.
* This method creates a new budget limit repetition when a new budget limit has been created.
*
*/
public function __construct()
{
}
/**
* In a perfect world, the store() routine should be different from the update()
* routine. It would not have to check count() == 0 because there could be NO
* limit repetitions at this point. However, the database can be wrong so we check.
* @param StoredBudgetLimit $event
*
* @param BudgetLimitStored $event
* @return bool
*/
public function store(BudgetLimitStored $event)
public function storeRepetition(StoredBudgetLimit $event):bool
{
$budgetLimit = $event->budgetLimit;
$end = $event->end;
@@ -71,12 +65,18 @@ class BudgetLimitEventHandler
}
return true;
}
/**
* @param BudgetLimitUpdated $event
* Updates, if present the budget limit repetition part of a budget limit.
*
* @param UpdatedBudgetLimit $event
*
* @return bool
*/
public function update(BudgetLimitUpdated $event)
public function updateRepetition(UpdatedBudgetLimit $event): bool
{
$budgetLimit = $event->budgetLimit;
$end = $event->end;
@@ -104,6 +104,7 @@ class BudgetLimitEventHandler
$repetition->save();
}
}
return true;
}
}

View File

@@ -1,72 +0,0 @@
<?php
/**
* ConnectJournalToPiggyBank.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\TransactionJournal;
/**
* Class ConnectJournalToPiggyBank
*
* @package FireflyIII\Handlers\Events
*/
class ConnectJournalToPiggyBank
{
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionJournalStored $event
*
* @return bool
*/
public function handle(TransactionJournalStored $event): bool
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
/** @var PiggyBank $piggyBank */
$piggyBank = auth()->user()->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank)) {
return true;
}
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
return true;
}
$amount = TransactionJournal::amountPositive($journal);
// if piggy account matches source account, the amount is positive
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
if (in_array($piggyBank->account_id, $sources)) {
$amount = bcmul($amount, '-1');
}
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
$repetition->save();
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);
return true;
}
}

View File

@@ -1,70 +0,0 @@
<?php
/**
* ConnectTransactionToPiggyBank.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionStored;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
/**
* Class ConnectTransactionToPiggyBank
*
* @package FireflyIII\Handlers\Events
*/
class ConnectTransactionToPiggyBank
{
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionStored $event
*
* @return bool
*/
public function handle(TransactionStored $event): bool
{
/** @var PiggyBankRepositoryInterface $repository */
$repository = app(PiggyBankRepositoryInterface::class);
$transaction = $event->transaction;
$piggyBank = $repository->find($transaction['piggy_bank_id']);
// valid piggy:
if (is_null($piggyBank->id)) {
return true;
}
$amount = strval($transaction['amount']);
// piggy bank account something with amount:
if ($transaction['source_account_id'] == $piggyBank->account_id) {
// if the source of this transaction is the same as the piggy bank,
// the money is being removed from the piggy bank. So the
// amount must be negative:
$amount = bcmul($amount, '-1');
}
$repetition = $piggyBank->currentRelevantRep();
// add or remove the money from the piggy bank:
$newAmount = bcadd(strval($repetition->currentamount), $amount);
$repetition->currentamount = $newAmount;
$repetition->save();
// now generate a piggy bank event:
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'date' => $transaction['date'], 'amount' => $newAmount]);
return true;
}
}

View File

@@ -1,68 +0,0 @@
<?php
/**
* FireRulesForStore.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
/**
* Class FireRulesForStore
*
* @package FireflyIII\Handlers\Events
*/
class FireRulesForStore
{
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionJournalStored $event
*
* @return bool
*/
public function handle(TransactionJournalStored $event): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
/** @var User $user */
$user = auth()->user();
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($event->journal);
if ($rule->stop_processing) {
return true;
}
}
}
return true;
}
}

View File

@@ -1,65 +0,0 @@
<?php
/**
* FireRulesForUpdate.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
/**
* Class FireRulesForUpdate
*
* @package FireflyIII\Handlers\Events
*/
class FireRulesForUpdate
{
/**
* Handle the event.
*
* @param TransactionJournalUpdated $event
*
* @return bool
*/
public function handle(TransactionJournalUpdated $event): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
/** @var User $user */
$user = auth()->user();
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'update-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($event->journal);
if ($rule->stop_processing) {
break;
}
}
}
return true;
}
}

View File

@@ -1,42 +0,0 @@
<?php
/**
* ScanForBillsAfterStore.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Support\Events\BillScanner;
/**
* Class RescanJournal
*
* @package FireflyIII\Handlers\Events
*/
class ScanForBillsAfterStore
{
/**
* Scan a transaction journal for possible links to bills, right after storing.
*
* @param TransactionJournalStored $event
*
* @return bool
*/
public function handle(TransactionJournalStored $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,41 +0,0 @@
<?php
/**
* ScanForBillsAfterUpdate.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Support\Events\BillScanner;
/**
* Class RescanJournal
*
* @package FireflyIII\Handlers\Events
*/
class ScanForBillsAfterUpdate
{
/**
* Scan a transaction journal for possibly related bills after it has been updated.
*
* @param TransactionJournalUpdated $event
*
* @return bool
*/
public function handle(TransactionJournalUpdated $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,69 +0,0 @@
<?php
/**
* SendRegistrationMail.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UserRegistration;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Swift_TransportException;
/**
* Class SendRegistrationMail
*
* @package FireflyIII\Handlers\Events
*/
class SendRegistrationMail
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserRegistration $event
*
* @return bool
*/
public function handle(UserRegistration $event): bool
{
$sendMail = env('SEND_REGISTRATION_MAIL', true);
if (!$sendMail) {
return true;
}
// get the email address
$email = $event->user->email;
$address = route('index');
$ipAddress = $event->ipAddress;
// send email.
try {
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
}

View File

@@ -0,0 +1,124 @@
<?php
/**
* StoredJournalEventHandler.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\StoredTransactionJournal;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\Processor;
use FireflyIII\Support\Events\BillScanner;
/**
* Class StoredJournalEventHandler
*
* @package FireflyIII\Handlers\Events
*/
class StoredJournalEventHandler
{
/**
* This method connects a new transfer to a piggy bank.
*
* @param StoredTransactionJournal $event
*
* @return bool
*/
public function connectToPiggyBank(StoredTransactionJournal $event): bool
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
/** @var PiggyBank $piggyBank */
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank)) {
return true;
}
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
return true;
}
$amount = TransactionJournal::amountPositive($journal);
// if piggy account matches source account, the amount is positive
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
if (in_array($piggyBank->account_id, $sources)) {
$amount = bcmul($amount, '-1');
}
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
$repetition->save();
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);
return true;
}
/**
* This method grabs all the users rules and processes them.
*
* @param StoredTransactionJournal $event
*
* @return bool
*/
public function processRules(StoredTransactionJournal $event): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($journal);
if ($rule->stop_processing) {
return true;
}
}
}
return true;
}
/**
* This method calls a special bill scanner that will check if the stored journal is part of a bill.
*
* @param StoredTransactionJournal $event
*
* @return bool
*/
public function scanBills(StoredTransactionJournal $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,74 +0,0 @@
<?php
/**
* UpdateJournalConnection.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\TransactionJournal;
/**
* Class UpdateJournalConnection
*
* @package FireflyIII\Handlers\Events
*/
class UpdateJournalConnection
{
/**
* Handle the event.
*
* @param TransactionJournalUpdated $event
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
*
* @return bool
*/
public function handle(TransactionJournalUpdated $event):bool
{
$journal = $event->journal;
if (!$journal->isTransfer()) {
return true;
}
// get the event connected to this journal:
/** @var PiggyBankEvent $event */
$event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first();
if (is_null($event)) {
return false;
}
$piggyBank = $event->piggyBank()->first();
$repetition = null;
if (!is_null($piggyBank)) {
/** @var PiggyBankRepetition $repetition */
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
}
if (is_null($repetition)) {
return false;
}
$amount = TransactionJournal::amount($journal);
$diff = bcsub($amount, $event->amount); // update current repetition
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
$repetition->save();
$event->amount = $amount;
$event->save();
return true;
}
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* UpdatedJournalEventHandler.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UpdatedTransactionJournal;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\Processor;
use FireflyIII\Support\Events\BillScanner;
/**
* Class UpdatedJournalEventHandler
*
* @package FireflyIII\Handlers\Events
*/
class UpdatedJournalEventHandler
{
/**
* This method will try to reconnect a journal to a piggy bank, updating the piggy bank repetition.
*
* @param UpdatedTransactionJournal $event
*
* @return bool
*/
public function connectToPiggyBank(UpdatedTransactionJournal $event): bool
{
$journal = $event->journal;
if (!$journal->isTransfer()) {
return true;
}
// get the event connected to this journal:
/** @var PiggyBankEvent $event */
$event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first();
if (is_null($event)) {
return false;
}
$piggyBank = $event->piggyBank()->first();
$repetition = null;
if (!is_null($piggyBank)) {
/** @var PiggyBankRepetition $repetition */
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
}
if (is_null($repetition)) {
return false;
}
$amount = TransactionJournal::amount($journal);
$diff = bcsub($amount, $event->amount); // update current repetition
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
$repetition->save();
$event->amount = $amount;
$event->save();
return true;
}
/**
* This method will check all the rules when a journal is updated.
*
* @param UpdatedTransactionJournal $event
*
* @return bool
*/
public function processRules(UpdatedTransactionJournal $event):bool
{
// get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'update-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($journal);
if ($rule->stop_processing) {
break;
}
}
}
return true;
}
/**
* This method calls a special bill scanner that will check if the updated journal is part of a bill.
*
* @param UpdatedTransactionJournal $event
*
* @return bool
*/
public function scanBills(UpdatedTransactionJournal $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,109 +0,0 @@
<?php
/**
* UserConfirmation.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Exception;
use FireflyIII\Events\ResendConfirmation;
use FireflyIII\Events\UserRegistration;
use FireflyIII\User;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Preferences;
use Swift_TransportException;
/**
* Class UserConfirmation
*
* @package FireflyIII\Handlers\Events
*/
class UserConfirmation
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* @param ResendConfirmation $event
*
* @return bool
*/
public function resendConfirmation(ResendConfirmation $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$this->doConfirm($user, $ipAddress);
return true;
}
/**
* Handle the event.
*
* @param UserRegistration $event
*
* @return bool
*/
public function sendConfirmation(UserRegistration $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$this->doConfirm($user, $ipAddress);
return true;
}
/**
* @param User $user
* @param string $ipAddress
*/
private function doConfirm(User $user, string $ipAddress)
{
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
if ($confirmAccount === false) {
Preferences::setForUser($user, 'user_confirmed', true);
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
Preferences::mark();
return;
}
$email = $user->email;
$code = str_random(16);
$route = route('do_confirm_account', [$code]);
Preferences::setForUser($user, 'user_confirmed', false);
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
Preferences::setForUser($user, 'user_confirmed_code', $code);
try {
Mail::send(
['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress],
function (Message $message) use ($email) {
$message->to($email, $email)->subject('Please confirm your Firefly III account');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
} catch (Exception $e) {
Log::error($e->getMessage());
}
return;
}
}

View File

@@ -0,0 +1,225 @@
<?php
/**
* UserEventHandler.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Exception;
use FireflyIII\Events\ConfirmedUser;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\ResentConfirmation;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Preferences;
use Session;
use Swift_TransportException;
/**
* Class UserEventHandler
*
* This class responds to any events that have anything to do with the User object.
*
* The method name reflects what is being done. This is in the present tense.
*
*
* @package FireflyIII\Handlers\Events
*/
class UserEventHandler
{
/**
* This method will bestow upon a user the "owner" role if he is the first user in the system.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function attachUserRole(RegisteredUser $event): bool
{
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
// first user ever?
if ($repository->count() === 1) {
$repository->attachRole($event->user, 'owner');
}
return true;
}
/**
* Handle user logout events.
*
* @return bool
*/
public function logoutUser(): bool
{
// dump stuff from the session:
Session::forget('twofactor-authenticated');
Session::forget('twofactor-authenticated-date');
return true;
}
/**
* This method will send a newly registered user a confirmation message, urging him or her to activate their account.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function sendConfirmationMessage(RegisteredUser $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
if ($confirmAccount === false) {
Preferences::setForUser($user, 'user_confirmed', true);
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
Preferences::mark();
return true;
}
$email = $user->email;
$code = str_random(16);
$route = route('do_confirm_account', [$code]);
Preferences::setForUser($user, 'user_confirmed', false);
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
Preferences::setForUser($user, 'user_confirmed_code', $code);
try {
Mail::send(
['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress],
function (Message $message) use ($email) {
$message->to($email, $email)->subject('Please confirm your Firefly III account');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
} catch (Exception $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* If the user has somehow lost his or her confirmation message, this event will send it to the user again.
*
* At the moment, this method is exactly the same as the ::sendConfirmationMessage method, but that will change.
*
* @param ResentConfirmation $event
*
* @return bool
*/
function sendConfirmationMessageAgain(ResentConfirmation $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
if ($confirmAccount === false) {
Preferences::setForUser($user, 'user_confirmed', true);
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
Preferences::mark();
return true;
}
$email = $user->email;
$code = str_random(16);
$route = route('do_confirm_account', [$code]);
Preferences::setForUser($user, 'user_confirmed', false);
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
Preferences::setForUser($user, 'user_confirmed_code', $code);
try {
Mail::send(
['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress],
function (Message $message) use ($email) {
$message->to($email, $email)->subject('Please confirm your Firefly III account');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
} catch (Exception $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* This method will send the user a registration mail, welcoming him or her to Firefly III.
* This message is only sent when the configuration of Firefly III says so.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function sendRegistrationMail(RegisteredUser $event)
{
$sendMail = env('SEND_REGISTRATION_MAIL', true);
if (!$sendMail) {
return true;
}
// get the email address
$email = $event->user->email;
$address = route('index');
$ipAddress = $event->ipAddress;
// send email.
try {
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* When the user is confirmed, this method stores the IP address of the user
* as a preference. Since this preference cannot be edited, it is effectively hidden
* from the user yet stored conveniently.
*
* @param ConfirmedUser $event
*
* @return bool
*/
public function storeConfirmationIpAddress(ConfirmedUser $event): bool
{
Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress);
return true;
}
/**
* This message stores the users IP address on registration, in much the same
* fashion as the previous method.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function storeRegistrationIpAddress(RegisteredUser $event): bool
{
Preferences::setForUser($event->user, 'registration_ip_address', $event->ipAddress);
return true;
}
}

View File

@@ -1,38 +0,0 @@
<?php
/**
* UserEventListener.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Session;
/**
* Class UserEventListener
*
* @package FireflyIII\Handlers\Events
*/
class UserEventListener
{
/**
* Handle user logout events.
*
* @return bool
*/
public function onUserLogout(): bool
{
// dump stuff from the session:
Session::forget('twofactor-authenticated');
Session::forget('twofactor-authenticated-date');
return true;
}
}

View File

@@ -1,63 +0,0 @@
<?php
/**
* UserSaveIpAddress.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UserIsConfirmed;
use FireflyIII\Events\UserRegistration;
use Preferences;
/**
* Class UserSaveIpAddress
*
* @package FireflyIII\Handlers\Events
*/
class UserSaveIpAddress
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserIsConfirmed $event
*
* @return bool
*/
public function saveFromConfirmation(UserIsConfirmed $event): bool
{
Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress);
return true;
}
/**
* Handle the event.
*
* @param UserRegistration $event
*
* @return bool
*/
public function saveFromRegistration(UserRegistration $event): bool
{
Preferences::setForUser($event->user, 'registration_ip_address', $event->ipAddress);
return true;
}
}

View File

@@ -14,7 +14,9 @@ namespace FireflyIII\Helpers\Help;
use Cache;
use League\CommonMark\CommonMarkConverter;
use Log;
use Requests;
use Requests_Exception;
use Route;
/**
@@ -26,47 +28,47 @@ class Help implements HelpInterface
{
/**
*
* @param string $key
* @param string $route
* @param string $language
*
* @return string
*/
public function getFromCache(string $key): string
public function getFromCache(string $route, string $language): string
{
return Cache::get($key);
return Cache::get('help.' . $route . '.' . $language);
}
/**
* @param string $language
* @param string $route
*
* @return array
* @return string
*/
public function getFromGithub(string $language, string $route): array
public function getFromGithub(string $language, string $route): string
{
$uri = sprintf('https://raw.githubusercontent.com/JC5/firefly-iii-help/master/%s/%s.md', $language, $route);
$routeIndex = str_replace('.', '-', $route);
$title = trans('help.' . $routeIndex);
$content = [
'text' => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>',
'title' => $title,
];
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
Log::debug(sprintf('Trying to get %s...', $uri));
$content = '';
try {
$result = Requests::get($uri);
} catch (Requests_Exception $e) {
Log::error($e);
return '';
}
$result = Requests::get($uri);
Log::debug(sprintf('Status code is %d', $result->status_code));
if ($result->status_code === 200) {
$content['text'] = $result->body;
$content = trim($result->body);
}
if (strlen(trim($content['text'])) == 0) {
$content['text'] = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
if (strlen($content) > 0) {
Log::debug('Content is longer than zero. Expect something.');
$converter = new CommonMarkConverter();
$content = $converter->convertToHtml($content);
}
$converter = new CommonMarkConverter();
$content['text'] = $converter->convertToHtml($content['text']);
return $content;
@@ -84,27 +86,37 @@ class Help implements HelpInterface
}
/**
*
* @param string $route
* @param string $language
*
* @return bool
*/
public function inCache(string $route):bool
public function inCache(string $route, string $language):bool
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
$result = Cache::has('help.' . $route . '.' . $language);
if ($result) {
Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language));
}
if (!$result) {
Log::debug(sprintf('Cache does not have this entry: %s', 'help.' . $route . '.' . $language));
}
return $result;
}
/**
*
* @param string $route
* @param string $language
* @param array $content
* @param string $content
*
* @internal param $title
*/
public function putInCache(string $route, string $language, array $content)
public function putInCache(string $route, string $language, string $content)
{
Cache::put('help.' . $route . '.text.' . $language, $content['text'], 10080); // a week.
Cache::put('help.' . $route . '.title.' . $language, $content['title'], 10080);
$key = 'help.' . $route . '.' . $language;
Log::debug(sprintf('Will store entry in cache: %s', $key));
Cache::put($key, $content, 10080); // a week.
}
}

View File

@@ -21,19 +21,20 @@ interface HelpInterface
{
/**
* @param string $key
* @param string $route
* @param string $language
*
* @return string
*/
public function getFromCache(string $key): string;
public function getFromCache(string $route, string $language): string;
/**
* @param string $language
* @param string $route
*
* @return array
* @return string
*/
public function getFromGithub(string $language, string $route):array;
public function getFromGithub(string $language, string $route):string;
/**
* @param string $route
@@ -44,15 +45,16 @@ interface HelpInterface
/**
* @param string $route
* @param string $language
*
* @return bool
*/
public function inCache(string $route): bool;
public function inCache(string $route, string $language ): bool;
/**
* @param string $route
* @param string $language
* @param array $content
* @param string $content
*/
public function putInCache(string $route, string $language, array $content);
public function putInCache(string $route, string $language, string $content);
}

View File

@@ -102,7 +102,8 @@ class ReportHelper implements ReportHelperInterface
$billLine->setHit(true);
}
if ($billLine->isActive()) {
// non active AND non hit? do not add:
if ($billLine->isActive() || $billLine->isHit()) {
$collection->addBill($billLine);
}
}

View File

@@ -15,9 +15,11 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties;
@@ -44,8 +46,16 @@ class AccountController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts'));
// translations:
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts'));
return $next($request);
}
);
}
/**
@@ -114,12 +124,11 @@ class AccountController extends Controller
}
/**
* @param ARI $repository
* @param Account $account
*
* @return View
*/
public function edit(ARI $repository, Account $account)
public function edit(Account $account)
{
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
@@ -147,7 +156,7 @@ class AccountController extends Controller
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalanceDate,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => round($account->virtual_balance, 2),
'virtualBalance' => $account->virtual_balance,
];
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts');
@@ -201,6 +210,9 @@ class AccountController extends Controller
*/
public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account)
{
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
return $this->redirectToOriginalAccount($account);
}
// show journals from current period only:
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$subTitle = $account->name;
@@ -298,23 +310,8 @@ class AccountController extends Controller
*/
public function store(AccountFormRequest $request, ARI $repository)
{
$accountData = [
'name' => trim($request->input('name')),
'accountType' => $request->input('what'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')),
'active' => true,
'user' => auth()->user()->id,
'iban' => trim($request->input('iban')),
'accountNumber' => trim($request->input('accountNumber')),
'accountRole' => $request->input('accountRole'),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
];
$account = $repository->store($accountData);
$data = $request->getAccountData();
$account = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name])));
Preferences::mark();
@@ -346,22 +343,8 @@ class AccountController extends Controller
*/
public function update(AccountFormRequest $request, ARI $repository, Account $account)
{
$accountData = [
'name' => $request->input('name'),
'active' => $request->input('active'),
'user' => auth()->user()->id,
'iban' => $request->input('iban'),
'accountNumber' => $request->input('accountNumber'),
'accountRole' => $request->input('accountRole'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
];
$repository->update($account, $accountData);
$data = $request->getAccountData();
$repository->update($account, $data);
Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
Preferences::mark();
@@ -393,4 +376,29 @@ class AccountController extends Controller
return '';
}
/**
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/
private function redirectToOriginalAccount(Account $account)
{
/** @var Transaction $transaction */
$transaction = $account->transactions()->first();
if (is_null($transaction)) {
throw new FireflyException('Expected a transaction. This account has none. BEEP, error.');
}
$journal = $transaction->transactionJournal;
/** @var Transaction $opposingTransaction */
$opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();
if (is_null($opposingTransaction)) {
throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.');
}
return redirect(route('accounts.show', [$opposingTransaction->account_id]));
}
}

View File

@@ -37,8 +37,15 @@ class ConfigurationController extends Controller
{
parent::__construct();
View::share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o');
$this->middleware(
function ($request, $next) {
View::share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request);
}
);
}
@@ -66,10 +73,10 @@ class ConfigurationController extends Controller
public function store(ConfigurationRequest $request)
{
// get config values:
$singleUserMode = intval($request->get('single_user_mode')) === 1 ? true : false;
$data = $request->getConfigurationData();
// store config values
FireflyConfig::set('single_user_mode', $singleUserMode);
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
// flash message
Session::flash('success', strval(trans('firefly.configuration_updated')));

View File

@@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\User;
use Illuminate\Http\Request;
@@ -83,6 +84,7 @@ class DomainController extends Controller
*/
public function toggleDomain(string $domain)
{
$domain = strtolower($domain);
$blocked = FireflyConfig::get('blocked-domains', [])->data;
if (in_array($domain, $blocked)) {
@@ -111,15 +113,16 @@ class DomainController extends Controller
*/
private function getKnownDomains(): array
{
$users = User::get();
$set = [];
$filtered = [];
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
$users = $repository->all();
$set = [];
$filtered = [];
/** @var User $user */
foreach ($users as $user) {
$email = $user->email;
$parts = explode('@', $email);
$domain = $parts[1];
$set[] = $domain;
$email = $user->email;
$parts = explode('@', $email);
$set[] = strtolower($parts[1]);
}
$set = array_unique($set);
// filter for already banned domains:
@@ -131,7 +134,6 @@ class DomainController extends Controller
$filtered[] = $domain;
}
}
asort($filtered);
return $filtered;
}

View File

@@ -78,5 +78,52 @@ class UserController extends Controller
}
/**
* @param UserRepositoryInterface $repository
* @param User $user
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function show(UserRepositoryInterface $repository, User $user)
{
$title = strval(trans('firefly.administration'));
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = strval(trans('firefly.single_user_administration', ['email' => $user->email]));
$subTitleIcon = 'fa-user';
// get IP info:
$defaultIp = '0.0.0.0';
$regPref = Preferences::getForUser($user, 'registration_ip_address');
$registration = $defaultIp;
$conPref = Preferences::getForUser($user, 'confirmation_ip_address');
$confirmation = $defaultIp;
if (!is_null($regPref)) {
$registration = $regPref->data;
}
if (!is_null($conPref)) {
$confirmation = $conPref->data;
}
$registrationHost = '';
$confirmationHost = '';
if ($registration != $defaultIp) {
$registrationHost = gethostbyaddr($registration);
}
if ($confirmation != $defaultIp) {
$confirmationHost = gethostbyaddr($confirmation);
}
$information = $repository->getUserData($user);
return view(
'admin.users.show',
compact(
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information',
'user', 'registration', 'confirmation', 'registrationHost', 'confirmationHost'
)
);
}
}

View File

@@ -42,8 +42,16 @@ class AttachmentController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-paperclip');
View::share('title', trans('firefly.attachments'));
// translations:
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-paperclip');
View::share('title', trans('firefly.attachments'));
return $next($request);
}
);
}
/**
@@ -164,14 +172,8 @@ class AttachmentController extends Controller
*/
public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
{
$attachmentData = [
'title' => $request->input('title'),
'description' => $request->input('description'),
'notes' => $request->input('notes'),
];
$repository->update($attachment, $attachmentData);
$data = $request->getAttachmentData();
$repository->update($attachment, $data);
Session::flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
Preferences::mark();

View File

@@ -13,8 +13,8 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Events\ResendConfirmation;
use FireflyIII\Events\UserIsConfirmed;
use FireflyIII\Events\ConfirmedUser;
use FireflyIII\Events\ResentConfirmation;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -56,7 +56,7 @@ class ConfirmationController extends Controller
if ($database === $code && ($now - $time <= $maxDiff)) {
// trigger user registration event:
event(new UserIsConfirmed(auth()->user(), $request->ip()));
event(new ConfirmedUser(auth()->user(), $request->ip()));
Preferences::setForUser(auth()->user(), 'user_confirmed', true);
Preferences::setForUser(auth()->user(), 'user_confirmed_confirmed', time());
@@ -80,7 +80,7 @@ class ConfirmationController extends Controller
$owner = env('SITE_OWNER', 'mail@example.com');
$view = 'auth.confirmation.no-resent';
if ($now - $time > $maxDiff) {
event(new ResendConfirmation(auth()->user(), $request->ip()));
event(new ResentConfirmation(auth()->user(), $request->ip()));
$view = 'auth.confirmation.resent';
}

View File

@@ -14,7 +14,7 @@ namespace FireflyIII\Http\Controllers\Auth;
use Auth;
use Config;
use FireflyIII\Events\UserRegistration;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\User;
@@ -102,7 +102,7 @@ class RegisterController extends Controller
$user = $this->create($request->all());
// trigger user registration event:
event(new UserRegistration($user, $request->ip()));
event(new RegisteredUser($user, $request->ip()));
Auth::login($user);

View File

@@ -41,6 +41,8 @@ class ResetPasswordController extends Controller
*/
public function __construct()
{
parent::__construct();
$this->middleware('guest');
}
}

View File

@@ -38,8 +38,16 @@ class BillController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o');
return $next($request);
}
);
}
/**
@@ -137,23 +145,15 @@ class BillController extends Controller
$bills = $repository->getBills();
$bills->each(
function (Bill $bill) use ($repository, $start, $end) {
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$bill->lastFoundMatch = $repository->lastFoundMatch($bill);
$journals = $repository->getJournalsInRange($bill, $start, $end);
// loop journals, find average:
$average = '0';
$count = $journals->count();
if ($count > 0) {
$sum = '0';
foreach ($journals as $journal) {
$sum = bcadd($sum, TransactionJournal::amountPositive($journal));
}
$average = bcdiv($sum, strval($count));
// paid in this period?
$bill->paidDates = $repository->getPaidDatesInRange($bill, $start, $end);
$bill->payDates = $repository->getPayDatesInRange($bill, $start, $end);
$lastDate = clone $start;
if ($bill->paidDates->count() >= $bill->payDates->count()) {
$lastDate = $end;
}
$bill->lastPaidAmount = $average;
$bill->paidInPeriod = ($start <= $bill->lastFoundMatch) && ($end >= $bill->lastFoundMatch);
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastDate);
}
);
@@ -204,7 +204,7 @@ class BillController extends Controller
$yearAverage = $repository->getYearAverage($bill, $date);
$overallAverage = $repository->getOverallAverage($bill);
$journals->setPath('/bills/show/' . $bill->id);
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon);
$hideBill = true;
$subTitle = e($bill->name);

View File

@@ -26,6 +26,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
use Log;
use Navigation;
use Preferences;
use Response;
@@ -47,9 +48,17 @@ class BudgetController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks');
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks');
return $next($request);
}
);
}
/**
@@ -191,10 +200,12 @@ class BudgetController extends Controller
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$startAsString = $start->format('Y-m-d');
$endAsString = $end->format('Y-m-d');
Log::debug('Now at /budgets');
// loop the budgets:
/** @var Budget $budget */
foreach ($budgets as $budget) {
Log::debug(sprintf('Now at budget #%d ("%s")', $budget->id, $budget->name));
$budget->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
$allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$otherRepetitions = new Collection;
@@ -282,13 +293,13 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param Budget $budget
*
* @return View
* @throws FireflyException
*/
public function show(BudgetRepositoryInterface $repository, Budget $budget)
public function show(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
{
/** @var Carbon $start */
$start = session('first', Carbon::create()->startOfYear());
@@ -300,6 +311,7 @@ class BudgetController extends Controller
$count = $journals->count();
$journals = $journals->slice($offset, $pageSize);
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$journals->setPath('/budgets/show/' . $budget->id);
@@ -310,7 +322,7 @@ class BudgetController extends Controller
/** @var LimitRepetition $entry */
foreach ($set as $entry) {
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $entry->startdate, $entry->enddate);
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->startdate, $entry->enddate);
$limits->push($entry);
}
@@ -318,15 +330,17 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return View
* @throws FireflyException
*/
public function showWithRepetition(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
public function showWithRepetition(
BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget, LimitRepetition $repetition
) {
if ($repetition->budgetLimit->budget->id != $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
@@ -340,11 +354,13 @@ class BudgetController extends Controller
$journals = $journals->slice($offset, $pageSize);
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id);
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
$limits = new Collection([$repetition]);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
@@ -359,11 +375,8 @@ class BudgetController extends Controller
*/
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
'name' => $request->input('name'),
'user' => auth()->user()->id,
];
$budget = $repository->store($budgetData);
$data = $request->getBudgetData();
$budget = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_new_budget', ['name' => e($budget->name)])));
Preferences::mark();
@@ -389,12 +402,8 @@ class BudgetController extends Controller
*/
public function update(BudgetFormRequest $request, BudgetRepositoryInterface $repository, Budget $budget)
{
$budgetData = [
'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1,
];
$repository->update($budget, $budgetData);
$data = $request->getBudgetData();
$repository->update($budget, $data);
Session::flash('success', strval(trans('firefly.updated_budget', ['name' => e($budget->name)])));
Preferences::mark();

View File

@@ -43,8 +43,16 @@ class CategoryController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart');
return $next($request);
}
);
}
/**
@@ -264,11 +272,8 @@ class CategoryController extends Controller
*/
public function store(CategoryFormRequest $request, CRI $repository)
{
$categoryData = [
'name' => trim($request->input('name')),
'user' => auth()->user()->id,
];
$category = $repository->store($categoryData);
$data = $request->getCategoryData();
$category = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_category', ['name' => e($category->name)])));
Preferences::mark();
@@ -292,11 +297,8 @@ class CategoryController extends Controller
*/
public function update(CategoryFormRequest $request, CRI $repository, Category $category)
{
$categoryData = [
'name' => $request->input('name'),
];
$repository->update($category, $categoryData);
$data = $request->getCategoryData();
$repository->update($category, $data);
Session::flash('success', strval(trans('firefly.updated_category', ['name' => e($category->name)])));
Preferences::mark();

View File

@@ -190,6 +190,56 @@ class AccountController extends Controller
return Response::json($data);
}
/**
* Shows the balances for all the user's revenue accounts.
*
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
*/
public function revenueAccounts(AccountRepositoryInterface $repository)
{
$start = clone session('start', Carbon::now()->startOfMonth());
$end = clone session('end', Carbon::now()->endOfMonth());
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('revenueAccounts');
$cache->addProperty('accounts');
if ($cache->has()) {
return Response::json($cache->get());
}
$accounts = $repository->getAccountsByType([AccountType::REVENUE]);
$start->subDay();
$ids = $accounts->pluck('id')->toArray();
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$accounts->each(
function (Account $account) use ($startBalances, $endBalances) {
$id = $account->id;
$startBalance = $startBalances[$id] ?? '0';
$endBalance = $endBalances[$id] ?? '0';
$diff = bcsub($endBalance, $startBalance);
$diff = bcmul($diff, '-1');
$account->difference = round($diff, 2);
}
);
$accounts = $accounts->sortByDesc(
function (Account $account) {
return $account->difference;
}
);
$data = $this->generator->revenueAccounts($accounts, $start, $end);
$cache->store($data);
return Response::json($data);
}
/**
* Shows an account's balance for a single month.
*

View File

@@ -46,10 +46,18 @@ class Controller extends BaseController
View::share('hideBills', false);
View::share('hideTags', false);
// save some formats:
$this->monthFormat = (string)trans('config.month');
$this->monthAndDayFormat = (string)trans('config.month_and_day');
$this->dateTimeFormat = (string)trans('config.date_time');
// translations:
$this->middleware(
function ($request, $next) {
$this->monthFormat = (string)trans('config.month');
$this->monthAndDayFormat = (string)trans('config.month_and_day');
$this->dateTimeFormat = (string)trans('config.date_time');
return $next($request);
}
);
}

View File

@@ -39,8 +39,16 @@ class CurrencyController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd');
return $next($request);
}
);
}
/**

View File

@@ -41,8 +41,16 @@ class ExportController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-file-archive-o');
View::share('title', trans('firefly.export_data'));
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-file-archive-o');
View::share('title', trans('firefly.export_data'));
return $next($request);
}
);
}
/**
@@ -133,7 +141,6 @@ class ExportController extends Controller
'endDate' => new Carbon($request->get('export_end_range')),
'exportFormat' => $request->get('exportFormat'),
'includeAttachments' => intval($request->get('include_attachments')) === 1,
'includeConfig' => intval($request->get('include_config')) === 1,
'includeOldUploads' => intval($request->get('include_old_uploads')) === 1,
'job' => $job,
];
@@ -177,15 +184,6 @@ class ExportController extends Controller
$job->change('export_status_collected_old_uploads');
}
/*
* Generate / collect config file.
*/
if ($settings['includeConfig']) {
$job->change('export_status_creating_config_file');
$processor->createConfigFile();
$job->change('export_status_created_config_file');
}
/*
* Create ZIP file:
*/

View File

@@ -41,11 +41,10 @@ class HelpController extends Controller
*/
public function show(HelpInterface $help, string $route)
{
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
$content = [
'text' => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>',
'title' => 'Help',
];
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
$alternative = false;
if (!$help->hasRoute($route)) {
Log::error('No such route: ' . $route);
@@ -53,17 +52,30 @@ class HelpController extends Controller
return Response::json($content);
}
if ($help->inCache($route)) {
$content = [
'text' => $help->getFromCache('help.' . $route . '.text.' . $language),
'title' => $help->getFromCache('help.' . $route . '.title.' . $language),
];
if ($help->inCache($route, $language)) {
$content = $help->getFromCache($route, $language);
Log::debug('Help text was in cache.');
return Response::json($content);
}
$content = $help->getFromGithub($language, $route);
// get backup language content (try English):
if (strlen($content) === 0) {
$language = 'en_US';
$content = $help->getFromGithub($language, $route);
$alternative = true;
}
if ($alternative && strlen($content) > 0) {
$content = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>' . $content;
}
if (strlen($content) === 0) {
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
}
$help->putInCache($route, $language, $content);
return Response::json($content);

View File

@@ -26,7 +26,7 @@ use Log;
use Preferences;
use Route;
use Session;
use View;
/**
* Class HomeController
@@ -41,6 +41,8 @@ class HomeController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', 'Firefly III');
View::share('mainTitleIcon', 'fa-fire');
}
/**
@@ -128,19 +130,18 @@ class HomeController extends Controller
return redirect(route('new-user.index'));
}
$title = 'Firefly';
$subTitle = trans('firefly.welcomeBack');
$mainTitleIcon = 'fa-fire';
$transactions = [];
$frontPage = Preferences::get(
$subTitle = trans('firefly.welcomeBack');
$transactions = [];
$frontPage = Preferences::get(
'frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray()
);
/** @var Carbon $start */
$start = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
$showTour = Preferences::get('tour', true)->data;
$accounts = $repository->getAccountsById($frontPage->data);
$end = session('end', Carbon::now()->endOfMonth());
$showTour = Preferences::get('tour', true)->data;
$accounts = $repository->getAccountsById($frontPage->data);
$showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data;
foreach ($accounts as $account) {
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
@@ -152,7 +153,7 @@ class HomeController extends Controller
}
return view(
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions')
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage')
);
}
@@ -163,38 +164,43 @@ class HomeController extends Controller
public function routes()
{
// these routes are not relevant for the help pages:
$ignore = [
$ignore = ['login', 'registe', 'logout', 'two-fac', 'lost-two', 'confirm', 'resend', 'do_confirm', 'testFla', 'json.', 'piggy-banks.add',
'piggy-banks.remove', 'preferences.', 'rules.rule.up', 'rules.rule.down', 'rules.rule-group.up', 'rules.rule-group.down', 'popup.report',
'admin.users.domains.block-', 'import.json', 'help.',
];
$routes = Route::getRoutes();
echo '<pre>';
/** @var \Illuminate\Routing\Route $route */
foreach ($routes as $route) {
$name = $route->getName();
$methods = $route->getMethods();
$search = [
'{account}', '{what}', '{rule}', '{tj}', '{category}', '{budget}', '{code}', '{date}', '{attachment}', '{bill}', '{limitrepetition}',
'{currency}', '{jobKey}', '{piggyBank}', '{ruleGroup}', '{rule}', '{route}', '{unfinishedJournal}',
'{reportType}', '{start_date}', '{end_date}', '{accountList}', '{tag}', '{journalList}',
];
$replace = [1, 'asset', 1, 1, 1, 1, 'abc', '2016-01-01', 1, 1, 1, 1, 1, 1, 1, 1, 'index', 1,
'default', '20160101', '20160131', '1,2', 1, '1,2',
];
if (count($search) != count($replace)) {
echo 'count';
exit;
}
$url = str_replace($search, $replace, $route->getUri());
if (!is_null($name) && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
echo '<a href="/' . $url . '" title="' . $name . '">' . $name . '</a><br>' . "\n";
if (!is_null($name) && strlen($name) > 0 && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
echo sprintf('touch %s.md', $name) . "\n";
}
}
echo '</pre>';
return '<hr>';
echo '<hr />';
return '&nbsp;';
}
/**
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function testFlash()
{
Session::flash('success', 'This is a success message.');
Session::flash('info', 'This is an info message.');
Session::flash('warning', 'This is a warning.');
Session::flash('error', 'This is an error!');
return redirect(route('home'));
}
/**
* @param array $array

View File

@@ -41,8 +41,15 @@ class ImportController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-archive');
View::share('title', trans('firefly.import_data_full'));
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-archive');
View::share('title', trans('firefly.import_data_full'));
return $next($request);
}
);
}
/**

View File

@@ -20,7 +20,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Input;
@@ -270,17 +270,17 @@ class JsonController extends Controller
}
/**
* @param JournalRepositoryInterface $repository
* @param JournalTaskerInterface $tasker
* @param $what
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function transactionJournals(JournalRepositoryInterface $repository, $what)
public function transactionJournals(JournalTaskerInterface $tasker, $what)
{
$descriptions = [];
$type = config('firefly.transactionTypesByWhat.' . $what);
$types = [$type];
$journals = $repository->getJournals($types, 1, 50);
$journals = $tasker->getJournals($types, 1, 50);
foreach ($journals as $j) {
$descriptions[] = $j->description;
}

View File

@@ -32,6 +32,13 @@ class NewUserController extends Controller
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
return $next($request);
}
);
}
@@ -107,7 +114,6 @@ class NewUserController extends Controller
'accountType' => 'asset',
'virtualBalance' => 0,
'active' => true,
'user' => auth()->user()->id,
'accountRole' => 'defaultAsset',
'openingBalance' => round($request->input('bank_balance'), 2),
'openingBalanceDate' => new Carbon,
@@ -133,7 +139,6 @@ class NewUserController extends Controller
'accountType' => 'asset',
'virtualBalance' => 0,
'active' => true,
'user' => auth()->user()->id,
'accountRole' => 'savingAsset',
'openingBalance' => round($request->input('savings_balance'), 2),
'openingBalanceDate' => new Carbon,
@@ -158,7 +163,6 @@ class NewUserController extends Controller
'accountType' => 'asset',
'virtualBalance' => round($request->get('credit_card_limit'), 2),
'active' => true,
'user' => auth()->user()->id,
'accountRole' => 'ccAsset',
'openingBalance' => null,
'openingBalanceDate' => null,

View File

@@ -45,8 +45,16 @@ class PiggyBankController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return $next($request);
}
);
}
/**
@@ -157,6 +165,7 @@ class PiggyBankController extends Controller
$subTitle = trans('firefly.update_piggy_title', ['name' => $piggyBank->name]);
$subTitleIcon = 'fa-pencil';
$targetDate = null;
$note = $piggyBank->notes()->first();
/*
* Flash some data to fill the form.
*/
@@ -168,6 +177,7 @@ class PiggyBankController extends Controller
'account_id' => $piggyBank->account_id,
'targetamount' => $piggyBank->targetamount,
'targetdate' => $targetDate,
'note' => is_null($note) ? '' : $note->text,
];
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'piggy-banks');
@@ -346,10 +356,11 @@ class PiggyBankController extends Controller
*/
public function show(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$note = $piggyBank->notes()->first();
$events = $repository->getEvents($piggyBank);
$subTitle = e($piggyBank->name);
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle'));
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'note'));
}
@@ -361,17 +372,8 @@ class PiggyBankController extends Controller
*/
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'name' => $request->get('name'),
'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')),
'targetamount' => round($request->get('targetamount'), 2),
'order' => $repository->getMaxOrder() + 1,
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
];
$piggyBank = $repository->store($piggyBankData);
$data = $request->getPiggyBankData();
$piggyBank = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => e($piggyBank->name)])));
Preferences::mark();
@@ -396,15 +398,8 @@ class PiggyBankController extends Controller
*/
public function update(PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request, PiggyBank $piggyBank)
{
$piggyBankData = [
'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')),
'targetamount' => round($request->get('targetamount'), 2),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
];
$piggyBank = $repository->update($piggyBank, $piggyBankData);
$data = $request->getPiggyBankData();
$piggyBank = $repository->update($piggyBank, $data);
Session::flash('success', strval(trans('firefly.updated_piggy_bank', ['name' => e($piggyBank->name)])));
Preferences::mark();

View File

@@ -173,7 +173,10 @@ class ReportController extends Controller
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$category = $repository->find(intval($attributes['categoryId']));
$journals = $repository->journalsInPeriod(new Collection([$category]), $attributes['accounts'], [], $attributes['startDate'], $attributes['endDate']);
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
$journals = $repository->journalsInPeriod(
new Collection([$category]), $attributes['accounts'], $types, $attributes['startDate'], $attributes['endDate']
);
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
return $view;

View File

@@ -35,8 +35,16 @@ class PreferencesController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear');
return $next($request);
}
);
}
/**
@@ -76,26 +84,27 @@ class PreferencesController extends Controller
*/
public function index(AccountRepositoryInterface $repository)
{
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$viewRangePref = Preferences::get('viewRange', '1M');
$viewRange = $viewRangePref->data;
$frontPageAccounts = Preferences::get('frontPageAccounts', []);
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
$transactionPageSize = Preferences::get('transactionPageSize', 50)->data;
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
$fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data;
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
$tjOptionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$is2faEnabled = Preferences::get('twoFactorAuthEnabled', 0)->data; // twoFactorAuthEnabled
$has2faSecret = !is_null(Preferences::get('twoFactorAuthSecret')); // hasTwoFactorAuthSecret
$showIncomplete = env('SHOW_INCOMPLETE_TRANSLATIONS', false) === true;
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$viewRangePref = Preferences::get('viewRange', '1M');
$viewRange = $viewRangePref->data;
$frontPageAccounts = Preferences::get('frontPageAccounts', []);
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
$transactionPageSize = Preferences::get('transactionPageSize', 50)->data;
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
$showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data;
$fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data;
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
$tjOptionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$is2faEnabled = Preferences::get('twoFactorAuthEnabled', 0)->data; // twoFactorAuthEnabled
$has2faSecret = !is_null(Preferences::get('twoFactorAuthSecret')); // hasTwoFactorAuthSecret
$showIncomplete = env('SHOW_INCOMPLETE_TRANSLATIONS', false) === true;
return view(
'preferences.index',
compact(
'language', 'accounts', 'frontPageAccounts', 'tjOptionalFields',
'viewRange', 'customFiscalYear', 'transactionPageSize', 'fiscalYearStart', 'is2faEnabled',
'has2faSecret', 'showIncomplete'
'has2faSecret', 'showIncomplete', 'showDepositsFrontpage'
)
);
}
@@ -145,6 +154,10 @@ class PreferencesController extends Controller
Preferences::set('customFiscalYear', $customFiscalYear);
Preferences::set('fiscalYearStart', $fiscalYearStart);
// show deposits frontpage:
$showDepositsFrontpage = intval($request->get('showDepositsFrontpage')) === 1;
Preferences::set('showDepositsFrontpage', $showDepositsFrontpage);
// save page size:
$transactionPageSize = intval($request->get('transactionPageSize'));
if ($transactionPageSize > 0 && $transactionPageSize < 1337) {

View File

@@ -13,7 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Events\UserIsDeleted;
use FireflyIII\Events\DeletedUser;
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest;
use FireflyIII\User;
@@ -35,6 +35,16 @@ class ProfileController extends Controller
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.profile'));
View::share('mainTitleIcon', 'fa-user');
return $next($request);
}
);
}
/**
@@ -42,9 +52,11 @@ class ProfileController extends Controller
*/
public function changePassword()
{
return view('profile.change-password')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.change_your_password'))->with(
'mainTitleIcon', 'fa-user'
);
$title = auth()->user()->email;
$subTitle = strval(trans('firefly.change_your_password'));
$subTitleIcon = 'fa-key';
return view('profile.change-password', compact('title', 'subTitle', 'subTitleIcon'));
}
/**
@@ -52,9 +64,11 @@ class ProfileController extends Controller
*/
public function deleteAccount()
{
return view('profile.delete-account')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.delete_account'))->with(
'mainTitleIcon', 'fa-user'
);
$title = auth()->user()->email;
$subTitle = strval(trans('firefly.delete_account'));
$subTitleIcon = 'fa-trash';
return view('profile.delete-account', compact('title', 'subTitle', 'subTitleIcon'));
}
/**
@@ -63,7 +77,10 @@ class ProfileController extends Controller
*/
public function index()
{
return view('profile.index')->with('title', trans('firefly.profile'))->with('subTitle', auth()->user()->email)->with('mainTitleIcon', 'fa-user');
$subTitle = auth()->user()->email;
$userId = auth()->user()->id;
return view('profile.index', compact('subTitle', 'userId'));
}
/**
@@ -110,9 +127,6 @@ class ProfileController extends Controller
return redirect(route('profile.delete-account'));
}
// respond to deletion:
event(new UserIsDeleted(auth()->user(), $request->ip()));
// store some stuff for the future:
$registration = Preferences::get('registration_ip_address')->data;
$confirmation = Preferences::get('confirmation_ip_address')->data;

View File

@@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
@@ -36,9 +37,23 @@ class AccountController extends Controller
*/
public function accountReport(Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$accountTasker = app(AccountTaskerInterface::class);
$accountReport = $accountTasker->getAccountReport($start, $end, $accounts);
return view('reports.partials.accounts', compact('accountReport'));
$result = view('reports.partials.accounts', compact('accountReport'))->render();
$cache->store($result);
return $result;
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* BalanceController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\BalanceReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class BalanceController
*
* @package FireflyIII\Http\Controllers\Report
*/
class BalanceController extends Controller
{
/**
* @param BalanceReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function balanceReport(BalanceReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('balance-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$balance = $helper->getBalanceReport($start, $end, $accounts);
$result = view('reports.partials.balance', compact('balance'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* BudgetController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class BudgetController
*
* @package FireflyIII\Http\Controllers\Report
*/
class BudgetController extends Controller
{
/**
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function budgetReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$budgets = $helper->getBudgetReport($start, $end, $accounts);
$result = view('reports.partials.budgets', compact('budgets'))->render();
$cache->store($result);
return $result;
}
/**
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function budgetYearOverview(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-year-overview');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$budgets = $helper->budgetYearOverview($start, $end, $accounts);
$result = view('reports.partials.budget-year-overview', compact('budgets'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* CategoryController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class CategoryController
*
* @package FireflyIII\Http\Controllers\Report
*/
class CategoryController extends Controller
{
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function categoryReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$categories = $helper->getCategoryReport($start, $end, $accounts);
$result = view('reports.partials.categories', compact('categories'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* InOutController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
/**
* Class InOutController
*
* @package FireflyIII\Http\Controllers\Report
*/
class InOutController extends Controller
{
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function inOutReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('in-out-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return Response::json($cache->get());
}
$incomes = $helper->getIncomeReport($start, $end, $accounts);
$expenses = $helper->getExpenseReport($start, $end, $accounts);
$result = [
'income' => view('reports.partials.income', compact('incomes'))->render(),
'expenses' => view('reports.partials.expenses', compact('expenses'))->render(),
'incomes_expenses' => view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render(),
];
$cache->store($result);
return Response::json($result);
}
}

View File

@@ -15,14 +15,11 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Report\BalanceReportHelperInterface;
use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
@@ -39,12 +36,6 @@ use View;
*/
class ReportController extends Controller
{
/** @var BalanceReportHelperInterface */
protected $balanceHelper;
/** @var BudgetReportHelperInterface */
protected $budgetHelper;
/** @var ReportHelperInterface */
protected $helper;
@@ -55,8 +46,17 @@ class ReportController extends Controller
{
parent::__construct();
View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart');
$this->helper = app(ReportHelperInterface::class);
return $next($request);
}
);
}
@@ -67,7 +67,7 @@ class ReportController extends Controller
*/
public function index(AccountRepositoryInterface $repository)
{
$this->createRepositories();
/** @var Carbon $start */
$start = clone session('first');
$months = $this->helper->listOfMonths($start);
@@ -98,7 +98,6 @@ class ReportController extends Controller
*/
public function report(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$this->createRepositories();
// throw an error if necessary.
if ($end < $start) {
throw new FireflyException('End date cannot be before start date, silly!');
@@ -153,8 +152,6 @@ class ReportController extends Controller
*/
private function auditReport(Carbon $start, Carbon $end, Collection $accounts)
{
/** @var ARI $repos */
$repos = app(ARI::class);
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
$auditData = [];
@@ -208,16 +205,6 @@ class ReportController extends Controller
return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow'));
}
/**
*
*/
private function createRepositories()
{
$this->helper = app(ReportHelperInterface::class);
$this->budgetHelper = app(BudgetReportHelperInterface::class);
$this->balanceHelper = app(BalanceReportHelperInterface::class);
}
/**
* @param $reportType
* @param Carbon $start
@@ -228,17 +215,8 @@ class ReportController extends Controller
*/
private function defaultMonth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$incomeTopLength = 8;
$expenseTopLength = 8;
// get report stuff!
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
$budgets = $this->budgetHelper->getBudgetReport($start, $end, $accounts);
$categories = $this->helper->getCategoryReport($start, $end, $accounts);
$balance = $this->balanceHelper->getBalanceReport($start, $end, $accounts);
$bills = $this->helper->getBillReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
$bills = $this->helper->getBillReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
// and some id's, joined:
$accountIds = join(',', $accounts->pluck('id')->toArray());
@@ -247,14 +225,11 @@ class ReportController extends Controller
return view(
'reports.default.month',
compact(
'start', 'end', 'reportType',
'start', 'end',
'tags',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills',
'accountIds', 'reportType'
'accountIds',
'reportType'
)
);
}
@@ -270,13 +245,8 @@ class ReportController extends Controller
private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$incomeTopLength = 8;
$expenseTopLength = 8;
// list of users stuff:
$budgets = app(BudgetRepositoryInterface::class)->getActiveBudgets();
$categories = app(CategoryRepositoryInterface::class)->getCategories();
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
// and some id's, joined:
@@ -290,9 +260,7 @@ class ReportController extends Controller
return view(
'reports.default.multi-year',
compact(
'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType',
'incomes', 'expenses',
'incomeTopLength', 'expenseTopLength', 'tags'
'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType', 'tags'
)
);
}
@@ -307,13 +275,7 @@ class ReportController extends Controller
*/
private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$incomeTopLength = 8;
$expenseTopLength = 8;
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
$budgets = $this->budgetHelper->budgetYearOverview($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
Session::flash('gaEventCategory', 'report');
Session::flash('gaEventAction', 'year');
@@ -330,8 +292,8 @@ class ReportController extends Controller
return view(
'reports.default.year',
compact(
'start', 'incomes', 'reportType', 'accountIds', 'end',
'expenses', 'incomeTopLength', 'expenseTopLength', 'tags', 'budgets'
'start', 'reportType',
'accountIds', 'end', 'tags'
)
);
}

View File

@@ -42,8 +42,16 @@ class RuleController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
return $next($request);
}
);
}
/**
@@ -238,23 +246,8 @@ class RuleController extends Controller
*/
public function store(RuleFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup)
{
// process the rule itself:
$data = [
'rule_group_id' => $ruleGroup->id,
'title' => $request->get('title'),
'user_id' => auth()->user()->id,
'trigger' => $request->get('trigger'),
'description' => $request->get('description'),
'rule-triggers' => $request->get('rule-trigger'),
'rule-trigger-values' => $request->get('rule-trigger-value'),
'rule-trigger-stop' => $request->get('rule-trigger-stop'),
'rule-actions' => $request->get('rule-action'),
'rule-action-values' => $request->get('rule-action-value'),
'rule-action-stop' => $request->get('rule-action-stop'),
'stop_processing' => $request->get('stop_processing'),
];
$data = $request->getRuleData();
$data['rule_group_id'] = $ruleGroup->id;
$rule = $repository->store($data);
Session::flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title]));
@@ -341,21 +334,7 @@ class RuleController extends Controller
*/
public function update(RuleRepositoryInterface $repository, RuleFormRequest $request, Rule $rule)
{
// process the rule itself:
$data = [
'title' => $request->get('title'),
'active' => intval($request->get('active')) == 1,
'trigger' => $request->get('trigger'),
'description' => $request->get('description'),
'rule-triggers' => $request->get('rule-trigger'),
'rule-trigger-values' => $request->get('rule-trigger-value'),
'rule-trigger-stop' => $request->get('rule-trigger-stop'),
'rule-actions' => $request->get('rule-action'),
'rule-action-values' => $request->get('rule-action-value'),
'rule-action-stop' => $request->get('rule-action-stop'),
'stop_processing' => intval($request->get('stop_processing')) == 1,
];
$data = $request->getRuleData();
$repository->update($rule, $data);
Session::flash('success', trans('firefly.updated_rule', ['title' => $rule->title]));
@@ -381,7 +360,6 @@ class RuleController extends Controller
$data = [
'rule_group_id' => $repository->getFirstRuleGroup()->id,
'stop_processing' => 0,
'user_id' => auth()->user()->id,
'title' => trans('firefly.default_rule_name'),
'description' => trans('firefly.default_rule_description'),
'trigger' => 'store-journal',
@@ -410,11 +388,10 @@ class RuleController extends Controller
{
/** @var RuleGroupRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface');
$repository = app(RuleGroupRepositoryInterface::class);
if ($repository->count() === 0) {
$data = [
'user_id' => auth()->user()->id,
'title' => trans('firefly.default_rule_group_name'),
'description' => trans('firefly.default_rule_group_description'),
];

View File

@@ -41,8 +41,16 @@ class RuleGroupController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
return $next($request);
}
);
}
/**
@@ -204,12 +212,7 @@ class RuleGroupController extends Controller
*/
public function store(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository)
{
$data = [
'title' => $request->input('title'),
'description' => $request->input('description'),
'user_id' => auth()->user()->id,
];
$data = $request->getRuleGroupData();
$ruleGroup = $repository->store($data);
Session::flash('success', strval(trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])));

View File

@@ -29,6 +29,13 @@ class SearchController extends Controller
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
return $next($request);
}
);
}
/**

View File

@@ -50,15 +50,22 @@ class TagController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', 'Tags');
View::share('mainTitleIcon', 'fa-tags');
View::share('hideTags', true);
$this->tagOptions = [
'nothing' => trans('firefly.regular_tag'),
'balancingAct' => trans('firefly.balancing_act'),
'advancePayment' => trans('firefly.advance_payment'),
];
View::share('tagOptions', $this->tagOptions);
$this->middleware(
function ($request, $next) {
View::share('title', 'Tags');
View::share('mainTitleIcon', 'fa-tags');
$this->tagOptions = [
'nothing' => trans('firefly.regular_tag'),
'balancingAct' => trans('firefly.balancing_act'),
'advancePayment' => trans('firefly.advance_payment'),
];
View::share('tagOptions', $this->tagOptions);
return $next($request);
}
);
}
/**

View File

@@ -0,0 +1,241 @@
<?php
/**
* ConvertController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Transaction;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Http\Request;
use Session;
use View;
/**
* Class ConvertController
*
* @package FireflyIII\Http\Controllers\Transaction
*/
class ConvertController extends Controller
{
/** @var AccountRepositoryInterface */
private $accounts;
/**
* ConvertController constructor.
*/
public function __construct()
{
parent::__construct();
// some useful repositories:
$this->middleware(
function ($request, $next) {
$this->accounts = app(AccountRepositoryInterface::class);
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-exchange');
return $next($request);
}
);
}
/**
* @param TransactionType $destinationType
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function convert(TransactionType $destinationType, TransactionJournal $journal)
{
$positiveAmount = TransactionJournal::amountPositive($journal);
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$sourceType = $journal->transactionType;
$subTitle = trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]);
$subTitleIcon = 'fa-exchange';
// cannot convert to its own type.
if ($sourceType->type === $destinationType->type) {
Session::flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type));
return redirect(route('transactions.show', [$journal->id]));
}
// cannot convert split.
if ($journal->transactions()->count() > 2) {
Session::flash('error', trans('firefly.cannot_convert_split_journl'));
return redirect(route('transactions.show', [$journal->id]));
}
// get source and destination account:
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
return view(
'transactions.convert',
compact(
'sourceType', 'destinationType', 'journal', 'assetAccounts',
'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType',
'subTitle', 'subTitleIcon'
)
);
// convert withdrawal to deposit requires a new source account ()
// or to transfer requires
}
/**
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param TransactionType $destinationType
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
{
$data = $request->all();
// cannot convert to its own type.
if ($journal->transactionType->type === $destinationType->type) {
Session::flash('error', trans('firefly.convert_is_already_type_' . $destinationType->type));
return redirect(route('transactions.show', [$journal->id]));
}
// cannot convert split.
if ($journal->transactions()->count() > 2) {
Session::flash('error', trans('firefly.cannot_convert_split_journl'));
return redirect(route('transactions.show', [$journal->id]));
}
// get the new source and destination account:
$source = $this->getSourceAccount($journal, $destinationType, $data);
$destination = $this->getDestinationAccount($journal, $destinationType, $data);
// update the journal:
$errors = $repository->convert($journal, $destinationType, $source, $destination);
if ($errors->count() > 0) {
return redirect(route('transactions.convert', [strtolower($destinationType->type), $journal->id]))->withErrors($errors)->withInput();
}
Session::flash('success', trans('firefly.converted_to_' . $destinationType->type));
return redirect(route('transactions.show', [$journal->id]));
}
/**
* @param TransactionJournal $journal
* @param TransactionType $destinationType
* @param array $data
*
* @return Account
* @throws FireflyException
*/
private function getDestinationAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
$sourceType = $journal->transactionType;
$destination = null;
$joined = $sourceType->type . '-' . $destinationType->type;
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined);
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
$destination = $sourceAccount;
break;
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
$destination = $accountRepository->find(intval($data['destination_account_asset']));
break;
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
$data = [
'name' => $data['destination_account_expense'],
'accountType' => 'expense',
'virtualBalance' => 0,
'active' => true,
'iban' => null,
];
$destination = $accountRepository->store($data);
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
$destination = $destinationAccount;
break;
}
return $destination;
}
/**
* @param TransactionJournal $journal
* @param TransactionType $destinationType
* @param array $data
*
* @return Account
* @throws FireflyException
*/
private function getSourceAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
$sourceType = $journal->transactionType;
$source = new Account;
$joined = $sourceType->type . '-' . $destinationType->type;
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined);
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
$data = [
'name' => $data['source_account_revenue'],
'accountType' => 'revenue',
'virtualBalance' => 0,
'active' => true,
'iban' => null,
];
$source = $accountRepository->store($data);
break;
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
$source = $sourceAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
$source = $destinationAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
$source = $accountRepository->find(intval($data['source_account_asset']));
break;
}
return $source;
}
}

View File

@@ -41,8 +41,16 @@ class MassController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
return $next($request);
}
);
}
/**
@@ -209,7 +217,6 @@ class MassController extends Controller
'destination_account_id' => intval($destAccountId),
'destination_account_name' => $destAccountName,
'amount' => round($request->get('amount')[$journal->id], 4),
'user' => auth()->user()->id,
'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
'date' => new Carbon($request->get('date')[$journal->id]),
'interest_date' => $journal->interest_date,

View File

@@ -0,0 +1,331 @@
<?php
/**
* SingleController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Transaction;
use ExpandedForm;
use FireflyIII\Events\StoredTransactionJournal;
use FireflyIII\Events\UpdatedTransactionJournal;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Log;
use Preferences;
use Session;
use Steam;
use URL;
use View;
/**
* Class SingleController
*
* @package FireflyIII\Http\Controllers\Transaction
*/
class SingleController extends Controller
{
/** @var AccountRepositoryInterface */
private $accounts;
/** @var AttachmentHelperInterface */
private $attachments;
/** @var BudgetRepositoryInterface */
private $budgets;
/** @var PiggyBankRepositoryInterface */
private $piggyBanks;
/**
*
*/
public function __construct()
{
parent::__construct();
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
View::share('uploadSize', $uploadSize);
// some useful repositories:
$this->middleware(
function ($request, $next) {
$this->accounts = app(AccountRepositoryInterface::class);
$this->budgets = app(BudgetRepositoryInterface::class);
$this->piggyBanks = app(PiggyBankRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
return $next($request);
}
);
}
/**
* @param string $what
*
* @return View
*/
public function create(string $what = TransactionType::DEPOSIT)
{
$what = strtolower($what);
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
$piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount();
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
$preFilled = Session::has('preFilled') ? session('preFilled') : [];
$subTitle = trans('form.add_new_' . $what);
$subTitleIcon = 'fa-plus';
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
Session::put('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "create another").
if (session('transactions.create.fromStore') !== true) {
$url = URL::previous();
Session::put('transactions.create.url', $url);
}
Session::forget('transactions.create.fromStore');
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'create-' . $what);
asort($piggies);
return view('transactions.create', compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields'));
}
/**
* Shows the form that allows a user to delete a transaction journal.
*
* @param TransactionJournal $journal
*
* @return View
*/
public function delete(TransactionJournal $journal)
{
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]);
// put previous url in session
Session::put('transactions.delete.url', URL::previous());
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'delete-' . $what);
return view('transactions.delete', compact('journal', 'subTitle', 'what'));
}
/**
* @param JournalRepositoryInterface $repository
* @param TransactionJournal $transactionJournal
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(JournalRepositoryInterface $repository, TransactionJournal $transactionJournal)
{
$type = TransactionJournal::transactionTypeStr($transactionJournal);
Session::flash('success', strval(trans('firefly.deleted_' . $type, ['description' => e($transactionJournal->description)])));
$repository->delete($transactionJournal);
Preferences::mark();
// redirect to previous URL:
return redirect(session('transactions.delete.url'));
}
/**
* @param TransactionJournal $journal
*
* @return mixed
*/
public function edit(TransactionJournal $journal)
{
$count = $journal->transactions()->count();
if ($count > 2) {
return redirect(route('transactions.edit-split', [$journal->id]));
}
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
$piggyBankList = ExpandedForm::makeSelectListWithEmpty($this->piggyBanks->getPiggyBanks());
// view related code
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
// journal related code
$sourceAccounts = TransactionJournal::sourceAccountList($journal);
$destinationAccounts = TransactionJournal::destinationAccountList($journal);
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$preFilled = [
'date' => TransactionJournal::dateAsString($journal),
'interest_date' => TransactionJournal::dateAsString($journal, 'interest_date'),
'book_date' => TransactionJournal::dateAsString($journal, 'book_date'),
'process_date' => TransactionJournal::dateAsString($journal, 'process_date'),
'category' => TransactionJournal::categoryAsString($journal),
'budget_id' => TransactionJournal::budgetId($journal),
'piggy_bank_id' => TransactionJournal::piggyBankId($journal),
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
'source_account_id' => $sourceAccounts->first()->id,
'source_account_name' => $sourceAccounts->first()->name,
'destination_account_id' => $destinationAccounts->first()->id,
'destination_account_name' => $destinationAccounts->first()->name,
'amount' => TransactionJournal::amountPositive($journal),
// new custom fields:
'due_date' => TransactionJournal::dateAsString($journal, 'due_date'),
'payment_date' => TransactionJournal::dateAsString($journal, 'payment_date'),
'invoice_date' => TransactionJournal::dateAsString($journal, 'invoice_date'),
'interal_reference' => $journal->getMeta('internal_reference'),
'notes' => $journal->getMeta('notes'),
];
if ($journal->isWithdrawal() && $destinationAccounts->first()->accountType->type == AccountType::CASH) {
$preFilled['destination_account_name'] = '';
}
if ($journal->isDeposit() && $sourceAccounts->first()->accountType->type == AccountType::CASH) {
$preFilled['source_account_name'] = '';
}
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'edit-' . $what);
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('transactions.edit.fromUpdate') !== true) {
Session::put('transactions.edit.url', URL::previous());
}
Session::forget('transactions.edit.fromUpdate');
return view(
'transactions.edit',
compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'piggyBankList', 'subTitle')
)->with('data', $preFilled);
}
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$doSplit = intval($request->get('split_journal')) === 1;
$createAnother = intval($request->get('create_another')) === 1;
$data = $request->getJournalData();
$journal = $repository->store($data);
if (is_null($journal->id)) {
// error!
Log::error('Could not store transaction journal: ', $journal->getErrors()->toArray());
Session::flash('error', $journal->getErrors()->first());
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
}
$this->attachments->saveAttachmentsForModel($journal);
// store the journal only, flash the rest.
if (count($this->attachments->getErrors()->get('attachments')) > 0) {
Session::flash('error', $this->attachments->getErrors()->get('attachments'));
}
// flash messages
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
Session::flash('info', $this->attachments->getMessages()->get('attachments'));
}
event(new StoredTransactionJournal($journal, $data['piggy_bank_id']));
Session::flash('success', strval(trans('firefly.stored_journal', ['description' => e($journal->description)])));
Preferences::mark();
if ($createAnother === true) {
// set value so create routine will not overwrite URL:
Session::put('transactions.create.fromStore', true);
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
}
if ($doSplit === true) {
// redirect to edit screen:
return redirect(route('transactions.edit-split', [$journal->id]));
}
// redirect to previous URL.
return redirect(session('transactions.create.url'));
}
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
* @param TransactionJournal $journal
*
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
{
$data = $request->getJournalData();
$journal = $repository->update($journal, $data);
$this->attachments->saveAttachmentsForModel($journal);
// flash errors
if (count($this->attachments->getErrors()->get('attachments')) > 0) {
Session::flash('error', $this->attachments->getErrors()->get('attachments'));
}
// flash messages
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
Session::flash('info', $this->attachments->getMessages()->get('attachments'));
}
event(new UpdatedTransactionJournal($journal));
// update, get events by date and sort DESC
$type = strtolower(TransactionJournal::transactionTypeStr($journal));
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => e($data['description'])])));
Preferences::mark();
// if wishes to split:
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('transactions.edit.fromUpdate', true);
return redirect(route('transactions.edit', [$journal->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect(session('transactions.edit.url'));
}
}

View File

@@ -15,18 +15,17 @@ namespace FireflyIII\Http\Controllers\Transaction;
use ExpandedForm;
use FireflyIII\Crud\Split\JournalInterface;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Events\UpdatedTransactionJournal;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\SplitJournalFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Log;
use Preferences;
use Session;
@@ -42,55 +41,43 @@ use View;
*/
class SplitController extends Controller
{
/** @var AccountRepositoryInterface */
private $accounts;
/** @var AttachmentHelperInterface */
private $attachments;
/** @var BudgetRepositoryInterface */
private $budgets;
/** @var CurrencyRepositoryInterface */
private $currencies;
/** @var JournalTaskerInterface */
private $tasker;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-share-alt');
View::share('title', trans('firefly.split-transactions'));
}
/**
* @param TransactionJournal $journal
*
* @return View
*/
public function create(TransactionJournal $journal)
{
$currencyRepository = app('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface');
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$piggyRepository = app('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface');
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
// some useful repositories:
$this->middleware(
function ($request, $next) {
$this->accounts = app(AccountRepositoryInterface::class);
$this->budgets = app(BudgetRepositoryInterface::class);
$this->tasker = app(JournalTaskerInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
$this->currencies = app(CurrencyRepositoryInterface::class);
View::share('mainTitleIcon', 'fa-share-alt');
View::share('title', trans('firefly.split-transactions'));
$assetAccounts = ExpandedForm::makeSelectList($accountRepository->getAccountsByType(['Default account', 'Asset account']));
$sessionData = session('journal-data', []);
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$currencies = ExpandedForm::makeSelectList($currencyRepository->get());
$budgets = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
$piggyBanks = ExpandedForm::makeSelectListWithEmpty($piggyRepository->getPiggyBanksWithAmount());
$subTitle = trans('form.add_new_' . $sessionData['what']);
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$subTitleIcon = 'fa-plus';
$preFilled = [
'what' => $sessionData['what'] ?? 'withdrawal',
'journal_amount' => $sessionData['amount'] ?? 0,
'journal_source_account_id' => $sessionData['source_account_id'] ?? 0,
'journal_source_account_name' => $sessionData['source_account_name'] ?? '',
'description' => [$journal->description],
'destination_account_name' => [$sessionData['destination_account_name']],
'destination_account_id' => [$sessionData['destination_account_id']],
'amount' => [$sessionData['amount']],
'budget_id' => [$sessionData['budget_id']],
'category' => [$sessionData['category']],
];
return view(
'split.journals.create',
compact('journal', 'piggyBanks', 'subTitle', 'optionalFields', 'subTitleIcon', 'preFilled', 'assetAccounts', 'currencies', 'budgets', 'uploadSize')
return $next($request);
}
);
}
@@ -102,17 +89,11 @@ class SplitController extends Controller
*/
public function edit(Request $request, TransactionJournal $journal)
{
$currencyRepository = app('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface');
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$currencies = ExpandedForm::makeSelectList($currencyRepository->get());
$assetAccounts = ExpandedForm::makeSelectList($accountRepository->getAccountsByType(['Default account', 'Asset account']));
$currencies = ExpandedForm::makeSelectList($this->currencies->get());
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$budgets = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
$preFilled = $this->arrayFromJournal($request, $journal);
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$subTitleIcon = 'fa-pencil';
@@ -127,7 +108,7 @@ class SplitController extends Controller
Session::forget('transactions.edit-split.fromUpdate');
return view(
'split.journals.edit',
'transactions.edit-split',
compact(
'subTitleIcon', 'currencies', 'optionalFields',
'preFilled', 'subTitle', 'amount', 'sourceAccounts', 'uploadSize', 'destinationAccounts', 'assetAccounts',
@@ -136,63 +117,31 @@ class SplitController extends Controller
);
}
/**
* @param JournalInterface $repository
* @param SplitJournalFormRequest $request
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function store(JournalInterface $repository, SplitJournalFormRequest $request, TransactionJournal $journal)
{
$data = $request->getSplitData();
foreach ($data['transactions'] as $transaction) {
$repository->storeTransaction($journal, $transaction);
}
$repository->markAsComplete($journal);
Session::flash('success', strval(trans('firefly.stored_journal', ['description' => e($journal->description)])));
Preferences::mark();
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('transactions.create.fromStore', true);
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
}
// redirect to previous URL.
return redirect(session('transactions.create.url'));
}
/**
* @param TransactionJournal $journal
* @param SplitJournalFormRequest $request
* @param JournalInterface $repository
* @param AttachmentHelperInterface $att
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(TransactionJournal $journal, SplitJournalFormRequest $request, JournalInterface $repository, AttachmentHelperInterface $att)
public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
{
$data = $request->getSplitData();
$journal = $repository->updateJournal($journal, $data);
$data = $this->arrayFromInput($request);
$journal = $repository->updateSplitJournal($journal, $data);
// save attachments:
$att->saveAttachmentsForModel($journal);
$this->attachments->saveAttachmentsForModel($journal);
event(new TransactionJournalUpdated($journal));
event(new UpdatedTransactionJournal($journal));
// update, get events by date and sort DESC
// flash messages
if (count($att->getMessages()->get('attachments')) > 0) {
Session::flash('info', $att->getMessages()->get('attachments'));
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
Session::flash('info', $this->attachments->getMessages()->get('attachments'));
}
$type = strtolower($journal->transaction_type_type ?? TransactionJournal::transactionTypeStr($journal));
$type = strtolower(TransactionJournal::transactionTypeStr($journal));
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => e($data['journal_description'])])));
Preferences::mark();
@@ -200,7 +149,7 @@ class SplitController extends Controller
// set value so edit routine will not overwrite URL:
Session::put('transactions.edit-split.fromUpdate', true);
return redirect(route('split.journal.edit', [$journal->id]))->withInput(['return_to_edit' => 1]);
return redirect(route('transactions.edit-split', [$journal->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
@@ -208,6 +157,39 @@ class SplitController extends Controller
}
/**
* @param Request $request
*
* @return array
*/
private function arrayFromInput(Request $request): array
{
$array = [
'journal_description' => $request->get('journal_description'),
'journal_source_account_id' => $request->get('journal_source_account_id'),
'journal_source_account_name' => $request->get('journal_source_account_name'),
'journal_destination_account_id' => $request->get('journal_destination_account_id'),
'currency_id' => $request->get('currency_id'),
'what' => $request->get('what'),
'date' => $request->get('date'),
// all custom fields:
'interest_date' => $request->get('interest_date'),
'book_date' => $request->get('book_date'),
'process_date' => $request->get('process_date'),
'due_date' => $request->get('due_date'),
'payment_date' => $request->get('payment_date'),
'invoice_date' => $request->get('invoice_date'),
'internal_reference' => $request->get('internal_reference'),
'notes' => $request->get('notes'),
'tags' => explode(',', $request->get('tags')),
// transactions.
'transactions' => $this->getTransactionDataFromRequest($request),
];
return $array;
}
/**
* @param Request $request
* @param TransactionJournal $journal
@@ -222,152 +204,83 @@ class SplitController extends Controller
'journal_description' => $request->old('journal_description', $journal->description),
'journal_amount' => TransactionJournal::amountPositive($journal),
'sourceAccounts' => $sourceAccounts,
'journal_source_account_id' => $sourceAccounts->first()->id,
'journal_source_account_name' => $sourceAccounts->first()->name,
'journal_destination_account_id' => $destinationAccounts->first()->id,
'transaction_currency_id' => $request->old('transaction_currency_id', $journal->transaction_currency_id),
'journal_source_account_id' => $request->old('journal_source_account_id', $sourceAccounts->first()->id),
'journal_source_account_name' => $request->old('journal_source_account_name', $sourceAccounts->first()->name),
'journal_destination_account_id' => $request->old('journal_destination_account_id', $destinationAccounts->first()->id),
'currency_id' => $request->old('currency_id', $journal->transaction_currency_id),
'destinationAccounts' => $destinationAccounts,
'what' => strtolower(TransactionJournal::transactionTypeStr($journal)),
'date' => $request->old('date', $journal->date),
'interest_date' => $request->old('interest_date', $journal->interest_date),
'book_date' => $request->old('book_date', $journal->book_date),
'process_date' => $request->old('process_date', $journal->process_date),
'description' => [],
'source_account_id' => [],
'source_account_name' => [],
'destination_account_id' => [],
'destination_account_name' => [],
'amount' => [],
'budget_id' => [],
'category' => [],
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
// all custom fields:
'interest_date' => $request->old('interest_date', $journal->getMeta('interest_date')),
'book_date' => $request->old('book_date', $journal->getMeta('book_date')),
'process_date' => $request->old('process_date', $journal->getMeta('process_date')),
'due_date' => $request->old('due_date', $journal->getMeta('due_date')),
'payment_date' => $request->old('payment_date', $journal->getMeta('payment_date')),
'invoice_date' => $request->old('invoice_date', $journal->getMeta('invoice_date')),
'internal_reference' => $request->old('internal_reference', $journal->getMeta('internal_reference')),
'notes' => $request->old('notes', $journal->getMeta('notes')),
// transactions.
'transactions' => $this->getTransactionDataFromJournal($journal),
];
// number of transactions present in old input:
$previousCount = count($request->old('description'));
if ($previousCount === 0) {
// build from scratch
$transactions = $this->transactionsFromJournal($request, $journal);
$array['description'] = $transactions['description'];
$array['source_account_id'] = $transactions['source_account_id'];
$array['source_account_name'] = $transactions['source_account_name'];
$array['destination_account_id'] = $transactions['destination_account_id'];
$array['destination_account_name'] = $transactions['destination_account_name'];
$array['amount'] = $transactions['amount'];
$array['budget_id'] = $transactions['budget_id'];
$array['category'] = $transactions['category'];
return $array;
}
$index = 0;
while ($index < $previousCount) {
$description = $request->old('description')[$index] ?? '';
$destinationId = $request->old('destination_account_id')[$index] ?? 0;
$destinationName = $request->old('destination_account_name')[$index] ?? '';
$sourceId = $request->old('source_account_id')[$index] ?? 0;
$sourceName = $request->old('source_account_name')[$index] ?? '';
$amount = $request->old('amount')[$index] ?? '';
$budgetId = $request->old('budget_id')[$index] ?? 0;
$categoryName = $request->old('category')[$index] ?? '';
// any transfer not from the source:
$array['description'][] = $description;
$array['source_account_id'][] = $sourceId;
$array['source_account_name'][] = $sourceName;
$array['destination_account_id'][] = $destinationId;
$array['destination_account_name'][] = $destinationName;
$array['amount'][] = $amount;
$array['budget_id'][] = intval($budgetId);
$array['category'][] = $categoryName;
$index++;
}
return $array;
}
/**
* @param Request $request
* @param TransactionJournal $journal
*
* @return array
*/
private function transactionsFromJournal(Request $request, TransactionJournal $journal): array
private function getTransactionDataFromJournal(TransactionJournal $journal): array
{
/** @var Collection $transactions */
$transactions = $journal->transactions()->get();
/*
* Splitted journals always have ONE source OR ONE destination.
* Withdrawals have ONE source (asset account)
* Deposits have ONE destination (asset account)
* Transfers have ONE of both (asset account)
*/
/** @var Account $singular */
$singular = TransactionJournal::sourceAccountList($journal)->first();
if ($journal->transactionType->type == TransactionType::DEPOSIT) {
/** @var Account $singular */
$singular = TransactionJournal::destinationAccountList($journal)->first();
}
/*
* Loop all transactions. Collect info ONLY from the transaction that is NOT related to
* the singular account.
*/
$index = 0;
$return = [
'description' => [],
'source_account_id' => [],
'source_account_name' => [],
'destination_account_id' => [],
'destination_account_name' => [],
'amount' => [],
'budget_id' => [],
'category' => [],
];
Log::debug('now at transactionsFromJournal');
/**
* @var int $current
* @var Transaction $transaction
*/
foreach ($transactions as $current => $transaction) {
$budget = $transaction->budgets()->first();
$category = $transaction->categories()->first();
$budgetId = 0;
$categoryName = '';
if (!is_null($budget)) {
$budgetId = $budget->id;
}
if (!is_null($category)) {
$categoryName = $category->name;
}
$budgetId = $request->old('budget_id')[$index] ?? $budgetId;
$categoryName = $request->old('category')[$index] ?? $categoryName;
$amount = $request->old('amount')[$index] ?? $transaction->amount;
$description = $request->old('description')[$index] ?? $transaction->description;
$destinationName = $request->old('destination_account_name')[$index] ?? $transaction->account->name;
$sourceName = $request->old('source_account_name')[$index] ?? $transaction->account->name;
$amount = bccomp($amount, '0') === -1 ? bcmul($amount, '-1') : $amount;
if ($transaction->account_id !== $singular->id) {
$return['description'][] = $description;
$return['destination_account_id'][] = $transaction->account_id;
$return['destination_account_name'][] = $destinationName;
$return['source_account_name'][] = $sourceName;
$return['amount'][] = $amount;
$return['budget_id'][] = intval($budgetId);
$return['category'][] = $categoryName;
// only add one when "valid" transaction
$index++;
}
$transactions = $this->tasker->getTransactionsOverview($journal);
$return = [];
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = [
'description' => $transaction['description'],
'source_account_id' => $transaction['source_account_id'],
'source_account_name' => $transaction['source_account_name'],
'destination_account_id' => $transaction['destination_account_id'],
'destination_account_name' => $transaction['destination_account_name'],
'amount' => round($transaction['destination_amount'], 2),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'],
];
}
return $return;
}
/**
* @param Request $request
*
* @return array
*/
private function getTransactionDataFromRequest(Request $request): array
{
$return = [];
$transactions = $request->get('transactions');
foreach ($transactions as $transaction) {
$return[] = [
'description' => $transaction['description'],
'source_account_id' => $transaction['source_account_id'] ?? 0,
'source_account_name' => $transaction['source_account_name'] ?? '',
'destination_account_id' => $transaction['destination_account_id'] ?? 0,
'destination_account_name' => $transaction['destination_account_name'] ?? '',
'amount' => round($transaction['amount'] ?? 0, 2),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'] ?? '',
];
}
Log::debug(sprintf('Found %d splits in request data.', count($return)));
return $return;
}
}

View File

@@ -14,22 +14,12 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use Illuminate\Http\Request;
use Preferences;
use Response;
use Session;
use Steam;
use URL;
use View;
/**
@@ -40,195 +30,39 @@ use View;
class TransactionController extends Controller
{
/**
*
* TransactionController constructor.
*/
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
View::share('uploadSize', $uploadSize);
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
return $next($request);
}
);
}
/**
* @param string $what
* @param Request $request
* @param JournalTaskerInterface $tasker
* @param string $what
*
* @return View
*/
public function create(string $what = TransactionType::DEPOSIT)
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$piggyRepository = app('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface');
$what = strtolower($what);
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$assetAccounts = ExpandedForm::makeSelectList($accountRepository->getActiveAccountsByType(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
$piggyBanks = $piggyRepository->getPiggyBanksWithAmount();
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
$preFilled = Session::has('preFilled') ? session('preFilled') : [];
$subTitle = trans('form.add_new_' . $what);
$subTitleIcon = 'fa-plus';
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
Session::put('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "create another").
if (session('transactions.create.fromStore') !== true) {
$url = URL::previous();
Session::put('transactions.create.url', $url);
}
Session::forget('transactions.create.fromStore');
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'create-' . $what);
asort($piggies);
return view('transactions.create', compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields'));
}
/**
* Shows the form that allows a user to delete a transaction journal.
*
* @param TransactionJournal $journal
*
* @return View
*/
public function delete(TransactionJournal $journal)
{
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]);
// put previous url in session
Session::put('transactions.delete.url', URL::previous());
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'delete-' . $what);
return view('transactions.delete', compact('journal', 'subTitle', 'what'));
}
/**
* @param JournalRepositoryInterface $repository
* @param TransactionJournal $transactionJournal
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(JournalRepositoryInterface $repository, TransactionJournal $transactionJournal)
{
$type = TransactionJournal::transactionTypeStr($transactionJournal);
Session::flash('success', strval(trans('firefly.deleted_' . $type, ['description' => e($transactionJournal->description)])));
$repository->delete($transactionJournal);
Preferences::mark();
// redirect to previous URL:
return redirect(session('transactions.delete.url'));
}
/**
* @param TransactionJournal $journal
*
* @return mixed
*/
public function edit(TransactionJournal $journal)
{
$count = $journal->transactions()->count();
if ($count > 2) {
return redirect(route('split.journal.edit', [$journal->id]));
}
// code to get list data:
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$piggyRepository = app('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$assetAccounts = ExpandedForm::makeSelectList($repository->getAccountsByType(['Default account', 'Asset account']));
$budgetList = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
$piggyBankList = ExpandedForm::makeSelectListWithEmpty($piggyRepository->getPiggyBanks());
// view related code
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
// journal related code
$sourceAccounts = TransactionJournal::sourceAccountList($journal);
$destinationAccounts = TransactionJournal::destinationAccountList($journal);
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$preFilled = [
'date' => TransactionJournal::dateAsString($journal),
'interest_date' => TransactionJournal::dateAsString($journal, 'interest_date'),
'book_date' => TransactionJournal::dateAsString($journal, 'book_date'),
'process_date' => TransactionJournal::dateAsString($journal, 'process_date'),
'category' => TransactionJournal::categoryAsString($journal),
'budget_id' => TransactionJournal::budgetId($journal),
'piggy_bank_id' => TransactionJournal::piggyBankId($journal),
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
'source_account_id' => $sourceAccounts->first()->id,
'source_account_name' => $sourceAccounts->first()->name,
'destination_account_id' => $destinationAccounts->first()->id,
'destination_account_name' => $destinationAccounts->first()->name,
'amount' => TransactionJournal::amountPositive($journal),
// new custom fields:
'due_date' => TransactionJournal::dateAsString($journal, 'due_date'),
'payment_date' => TransactionJournal::dateAsString($journal, 'payment_date'),
'invoice_date' => TransactionJournal::dateAsString($journal, 'invoice_date'),
'interal_reference' => $journal->getMeta('internal_reference'),
'notes' => $journal->getMeta('notes'),
];
if ($journal->isWithdrawal() && $destinationAccounts->first()->accountType->type == AccountType::CASH) {
$preFilled['destination_account_name'] = '';
}
if ($journal->isDeposit() && $sourceAccounts->first()->accountType->type == AccountType::CASH) {
$preFilled['source_account_name'] = '';
}
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'edit-' . $what);
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('transactions.edit.fromUpdate') !== true) {
Session::put('transactions.edit.url', URL::previous());
}
Session::forget('transactions.edit.fromUpdate');
return view(
'transactions.edit',
compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'piggyBankList', 'subTitle')
)->with('data', $preFilled);
}
/**
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param string $what
*
* @return View
*/
public function index(Request $request, JournalRepositoryInterface $repository, string $what)
public function index(Request $request, JournalTaskerInterface $tasker, string $what)
{
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what);
$subTitle = trans('firefly.title_' . $what);
$page = intval($request->get('page'));
$journals = $repository->getJournals($types, $page, $pageSize);
$journals = $tasker->getJournals($types, $page, $pageSize);
$journals->setPath('transactions/' . $what);
@@ -265,139 +99,20 @@ class TransactionController extends Controller
}
/**
* @param TransactionJournal $journal
* @param JournalRepositoryInterface $repository
* @param TransactionJournal $journal
* @param JournalTaskerInterface $tasker
*
* @return View
*/
public function show(TransactionJournal $journal, JournalRepositoryInterface $repository)
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
{
$events = $repository->getPiggyBankEvents($journal);
$transactions = $repository->getTransactions($journal);
$events = $tasker->getPiggyBankEvents($journal);
$transactions = $tasker->getTransactionsOverview($journal);
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
if ($transactions->count() > 2) {
return view('split.journals.show', compact('journal', 'events', 'subTitle', 'what', 'transactions'));
}
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions'));
}
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$att = app('FireflyIII\Helpers\Attachments\AttachmentHelperInterface');
$doSplit = intval($request->get('split_journal')) === 1;
$journalData = $request->getJournalData();
// store the journal only, flash the rest.
if ($doSplit) {
$journal = $repository->storeJournal($journalData);
$journal->completed = false;
$journal->save();
// store attachments:
$att->saveAttachmentsForModel($journal);
// flash errors
if (count($att->getErrors()->get('attachments')) > 0) {
Session::flash('error', $att->getErrors()->get('attachments'));
}
// flash messages
if (count($att->getMessages()->get('attachments')) > 0) {
Session::flash('info', $att->getMessages()->get('attachments'));
}
Session::put('journal-data', $journalData);
return redirect(route('split.journal.create', [$journal->id]));
}
// if not withdrawal, unset budgetid.
if ($journalData['what'] != strtolower(TransactionType::WITHDRAWAL)) {
$journalData['budget_id'] = 0;
}
$journal = $repository->store($journalData);
$att->saveAttachmentsForModel($journal);
// flash errors
if (count($att->getErrors()->get('attachments')) > 0) {
Session::flash('error', $att->getErrors()->get('attachments'));
}
// flash messages
if (count($att->getMessages()->get('attachments')) > 0) {
Session::flash('info', $att->getMessages()->get('attachments'));
}
event(new TransactionJournalStored($journal, intval($journalData['piggy_bank_id'])));
Session::flash('success', strval(trans('firefly.stored_journal', ['description' => e($journal->description)])));
Preferences::mark();
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('transactions.create.fromStore', true);
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
}
// redirect to previous URL.
return redirect(session('transactions.create.url'));
}
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
* @param AttachmentHelperInterface $att
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att, TransactionJournal $journal)
{
$journalData = $request->getJournalData();
$repository->update($journal, $journalData);
// save attachments:
$att->saveAttachmentsForModel($journal);
// flash errors
if (count($att->getErrors()->get('attachments')) > 0) {
Session::flash('error', $att->getErrors()->get('attachments'));
}
// flash messages
if (count($att->getMessages()->get('attachments')) > 0) {
Session::flash('info', $att->getMessages()->get('attachments'));
}
event(new TransactionJournalUpdated($journal));
// update, get events by date and sort DESC
$type = strtolower($journal->transaction_type_type ?? TransactionJournal::transactionTypeStr($journal));
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => e($journalData['description'])])));
Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('transactions.edit.fromUpdate', true);
return redirect(route('transactions.edit', [$journal->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect(session('transactions.edit.url'));
}
}

View File

@@ -70,17 +70,29 @@ class Range
// set start, end and finish:
$this->setRange();
// set view variables.
$this->configureView();
// get variables for date range:
$this->datePicker();
// set view variables.
$this->configureView();
// set more view variables:
$this->configureList();
}
return $theNext($request);
}
/**
*
*/
private function configureList()
{
$pref = Preferences::get('list-length', config('firefly.list_length', 10))->data;
View::share('listLength', $pref);
}
private function configureView()
{
$pref = Preferences::get('language', config('firefly.default_language', 'en_US'));

View File

@@ -13,8 +13,8 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use Input;
use Carbon\Carbon;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
/**
* Class AccountFormRequest
@@ -33,20 +33,44 @@ class AccountFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getAccountData(): array
{
return [
'name' => trim($this->input('name')),
'active' => intval($this->input('active')) === 1,
'accountType' => $this->input('what'),
'virtualBalance' => round($this->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')),
'iban' => trim($this->input('iban')),
'accountNumber' => trim($this->input('accountNumber')),
'accountRole' => $this->input('accountRole'),
'openingBalance' => round($this->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$this->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($this->input('amount_currency_id_openingBalance')),
'ccType' => $this->input('ccType'),
'ccMonthlyPaymentDate' => $this->input('ccMonthlyPaymentDate'),
];
}
/**
* @return array
*/
public function rules()
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accountRoles = join(',', array_keys(config('firefly.accountRoles')));
$types = join(',', array_keys(config('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = join(',', array_keys(config('firefly.ccTypes')));
$nameRule = 'required|min:1|uniqueAccountForUser';
$idRule = '';
if (Account::find(Input::get('id'))) {
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$idRule = 'belongsToUser:accounts';
$nameRule = 'required|min:1|uniqueAccountForUser:' . Input::get('id');
$nameRule = 'required|min:1|uniqueAccountForUser:' . $this->get('id');
}
return [

View File

@@ -30,6 +30,18 @@ class AttachmentFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getAttachmentData(): array
{
return [
'title' => trim($this->input('title')),
'description' => trim($this->input('description')),
'notes' => trim($this->input('notes')),
];
}
/**
* @return array
*/

View File

@@ -14,7 +14,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Carbon\Carbon;
use Input;
/**
* Class BillFormRequest
@@ -46,7 +45,6 @@ class BillFormRequest extends Request
'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')),
'amount_max' => round($this->get('amount_max'), 2),
'date' => new Carbon($this->get('date')),
'user' => auth()->user()->id,
'repeat_freq' => $this->get('repeat_freq'),
'skip' => intval($this->get('skip')),
'automatch' => intval($this->get('automatch')) === 1,
@@ -61,9 +59,9 @@ class BillFormRequest extends Request
{
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name';
$matchRule = 'required|between:1,255|uniqueObjectForUser:bills,match';
if (intval(Input::get('id')) > 0) {
$nameRule .= ',' . intval(Input::get('id'));
$matchRule .= ',' . intval(Input::get('id'));
if (intval($this->get('id')) > 0) {
$nameRule .= ',' . intval($this->get('id'));
$matchRule .= ',' . intval($this->get('id'));
}
$rules = [

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Budget;
use Input;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
/**
* Class BudgetFormRequest
@@ -29,19 +28,30 @@ class BudgetFormRequest extends Request
*/
public function authorize()
{
// Only allow logged in users
return auth()->check();
}
/**
* @return array
*/
public function getBudgetData(): array
{
return [
'name' => trim($this->input('name')),
'active' => intval($this->input('active')) == 1,
];
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name';
if (Budget::find(Input::get('id'))) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,' . intval(Input::get('id'));
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name';
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,' . intval($this->get('id'));
}
return [

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Category;
use Input;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
/**
* Class CategoryFormRequest
@@ -33,15 +32,26 @@ class CategoryFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getCategoryData(): array
{
return [
'name' => trim($this->input('name')),
];
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name';
if (Category::find(Input::get('id'))) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,' . intval(Input::get('id'));
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name';
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,' . intval($this->get('id'));
}
return [

View File

@@ -30,6 +30,16 @@ class ConfigurationRequest extends Request
return auth()->check() && auth()->user()->hasRole('owner');
}
/**
* @return array
*/
public function getConfigurationData(): array
{
return [
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
];
}
/**
* @return array
*/

View File

@@ -13,8 +13,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Input;
/**
* Class BillFormRequest
*
@@ -55,7 +53,7 @@ class CurrencyFormRequest extends Request
'name' => 'required|max:48|min:1|unique:transaction_currencies,name',
'symbol' => 'required|min:1|max:8|unique:transaction_currencies,symbol',
];
if (intval(Input::get('id')) > 0) {
if (intval($this->get('id')) > 0) {
$rules = [
'code' => 'required|min:3|max:3',
'name' => 'required|max:48|min:1',

View File

@@ -14,10 +14,8 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionType;
use Input;
/**
* Class JournalFormRequest
@@ -37,86 +35,109 @@ class JournalFormRequest extends Request
}
/**
* Returns and validates the data required to store a new journal. Can handle both single transaction journals and split journals.
*
* @return array
*/
public function getJournalData()
{
$tags = $this->getFieldOrEmptyString('tags');
$data = [
'what' => $this->get('what'), // type. can be 'deposit', 'withdrawal' or 'transfer'
'date' => new Carbon($this->get('date')),
'tags' => explode(',', $this->getFieldOrEmptyString('tags')),
'currency_id' => intval($this->get('amount_currency_id_amount')),
return [
'what' => $this->get('what'),
'description' => trim($this->get('description')),
'source_account_id' => intval($this->get('source_account_id')),
'source_account_name' => trim($this->getFieldOrEmptyString('source_account_name')),
'destination_account_id' => intval($this->get('destination_account_id')),
'destination_account_name' => trim($this->getFieldOrEmptyString('destination_account_name')),
'amount' => round($this->get('amount'), 2),
'user' => auth()->user()->id,
'amount_currency_id_amount' => intval($this->get('amount_currency_id_amount')),
'date' => new Carbon($this->get('date')),
'interest_date' => $this->getDateOrNull('interest_date'),
'book_date' => $this->getDateOrNull('book_date'),
'process_date' => $this->getDateOrNull('process_date'),
'budget_id' => intval($this->get('budget_id')),
'category' => trim($this->getFieldOrEmptyString('category')),
'tags' => explode(',', $tags),
'piggy_bank_id' => intval($this->get('piggy_bank_id')),
// all custom fields:
'interest_date' => $this->getDateOrNull('interest_date'),
'book_date' => $this->getDateOrNull('book_date'),
'process_date' => $this->getDateOrNull('process_date'),
'due_date' => $this->getDateOrNull('due_date'),
'payment_date' => $this->getDateOrNull('payment_date'),
'invoice_date' => $this->getDateOrNull('invoice_date'),
'internal_reference' => trim(strval($this->get('internal_reference'))),
'notes' => trim(strval($this->get('notes'))),
// new custom fields here:
'due_date' => $this->getDateOrNull('due_date'),
'payment_date' => $this->getDateOrNull('payment_date'),
'invoice_date' => $this->getDateOrNull('invoice_date'),
'internal_reference' => trim(strval($this->get('internal_reference'))),
'notes' => trim(strval($this->get('notes'))),
// transaction / journal data:
'description' => $this->getFieldOrEmptyString('description'),
'amount' => round($this->get('amount'), 2),
'budget_id' => intval($this->get('budget_id')),
'category' => $this->getFieldOrEmptyString('category'),
'source_account_id' => intval($this->get('source_account_id')),
'source_account_name' => $this->getFieldOrEmptyString('source_account_name'),
'destination_account_id' => $this->getFieldOrEmptyString('destination_account_id'),
'destination_account_name' => $this->getFieldOrEmptyString('destination_account_name'),
'piggy_bank_id' => intval($this->get('piggy_bank_id')),
];
return $data;
}
/**
* @return array
* @throws Exception
*/
public function rules()
{
$what = Input::get('what');
$what = $this->get('what');
$rules = [
'description' => 'required|min:1,max:255',
'what' => 'required|in:withdrawal,deposit,transfer',
'amount' => 'numeric|required|min:0.01',
'date' => 'required|date',
'process_date' => 'date',
'book_date' => 'date',
'interest_date' => 'date',
'category' => 'between:1,255',
'amount_currency_id_amount' => 'required|exists:transaction_currencies,id',
'piggy_bank_id' => 'numeric',
'what' => 'required|in:withdrawal,deposit,transfer',
'date' => 'required|date',
// new custom fields here:
'due_date' => 'date',
'payment_date' => 'date',
'internal_reference' => 'min:1,max:255',
'notes' => 'min:1,max:65536',
// then, custom fields:
'interest_date' => 'date',
'book_date' => 'date',
'process_date' => 'date',
'due_date' => 'date',
'payment_date' => 'date',
'invoice_date' => 'date',
'internal_reference' => 'min:1,max:255',
'notes' => 'min:1,max:50000',
// and then transaction rules:
'description' => 'required|between:1,255',
'amount' => 'numeric|required|min:0.01',
'budget_id' => 'mustExist:budgets,id|belongsToUser:budgets,id',
'category' => 'between:1,255',
'source_account_id' => 'numeric|belongsToUser:accounts,id',
'source_account_name' => 'between:1,255',
'destination_account_id' => 'numeric|belongsToUser:accounts,id',
'destination_account_name' => 'between:1,255',
'piggy_bank_id' => 'between:1,255',
];
// some rules get an upgrade depending on the type of data:
$rules = $this->enhanceRules($what, $rules);
return $rules;
}
/**
* Inspired by https://www.youtube.com/watch?v=WwnI0RS6J5A
*
* @param string $what
* @param array $rules
*
* @return array
* @throws FireflyException
*/
private function enhanceRules(string $what, array $rules): array
{
switch ($what) {
case strtolower(TransactionType::WITHDRAWAL):
$rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
$rules['destination_account_name'] = 'between:1,255';
if (intval(Input::get('budget_id')) != 0) {
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
}
break;
case strtolower(TransactionType::DEPOSIT):
$rules['source_account_name'] = 'between:1,255';
$rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
break;
case strtolower(TransactionType::TRANSFER):
// this may not work:
$rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:destination_account_id';
$rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:source_account_id';
break;
default:
throw new FireflyException('Cannot handle transaction type of type ' . e($what) . '.');
throw new FireflyException('Cannot handle transaction type of type ' . e($what) . ' . ');
}
return $rules;

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