mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-19 02:52:44 +00:00
Compare commits
294 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fdb8f61e37 | ||
|
69422cc796 | ||
|
5f9a9bc89a | ||
|
4d0d05e0f8 | ||
|
0113fedbd4 | ||
|
a7d35cd1c3 | ||
|
43600fe6cb | ||
|
0b5e25960f | ||
|
0c8a1b51e9 | ||
|
cb49f5e8d8 | ||
|
a0e3088ca3 | ||
|
b86be6f52f | ||
|
4c573e1300 | ||
|
1a3d77f117 | ||
|
2656da13b1 | ||
|
d272ebd95c | ||
|
7612f1f91a | ||
|
22a2fe3f61 | ||
|
1ebb59b352 | ||
|
77e2cf40df | ||
|
0edffd8ea1 | ||
|
ee6e047596 | ||
|
bd55636b3f | ||
|
b24e97a449 | ||
|
d45355fc3f | ||
|
b2206f640a | ||
|
962cad33e2 | ||
|
d65214b75a | ||
|
7b4c151df5 | ||
|
28d6f51961 | ||
|
d2f9deb82b | ||
|
d9b05b5f59 | ||
|
a8f4b33c57 | ||
|
ee849ea12f | ||
|
f9d3cf231f | ||
|
0713ca7709 | ||
|
1e2124c5ed | ||
|
37435da459 | ||
|
05dbd30bbd | ||
|
4b947638a7 | ||
|
3d113b9aae | ||
|
d1b3681bf3 | ||
|
9dd4b07314 | ||
|
3814f0f3c3 | ||
|
b1e907fae9 | ||
|
5c03a1a9c8 | ||
|
20ac07a386 | ||
|
13e1292bb7 | ||
|
8e542531b3 | ||
|
43afdb021a | ||
|
aeca2ef3b2 | ||
|
205a593721 | ||
|
46649fe228 | ||
|
adb97fcb05 | ||
|
98160e9b63 | ||
|
9c5d192d90 | ||
|
47bebb614e | ||
|
5f7fb77db2 | ||
|
1d15bc0b10 | ||
|
7bc4c6d115 | ||
|
45973a53f5 | ||
|
8e5e3de8b0 | ||
|
8738cd4b04 | ||
|
24a7dac235 | ||
|
a3088f6806 | ||
|
72f7b5f3ea | ||
|
a636c508a2 | ||
|
599db95f73 | ||
|
f5f78ab79b | ||
|
9af9383c29 | ||
|
4b97b86c09 | ||
|
11fb46830c | ||
|
e8dec6d95c | ||
|
bb4ee7470d | ||
|
2e8071db9e | ||
|
4d2901aa02 | ||
|
37bbfab20a | ||
|
fb9161b82d | ||
|
000c9d8974 | ||
|
878b664930 | ||
|
afe28b5581 | ||
|
4106b2e4c0 | ||
|
e1be4909b9 | ||
|
7a0347c0c2 | ||
|
a7e0e3fc15 | ||
|
5e480eca36 | ||
|
6c8d594df7 | ||
|
e24f5ec9f3 | ||
|
1379c0652e | ||
|
1f87b0bd2d | ||
|
787a437ca4 | ||
|
c0bdb35cb3 | ||
|
4b9cf67413 | ||
|
86ff3be741 | ||
|
8bc8e8d9fe | ||
|
227a12d75d | ||
|
2ddd4314f1 | ||
|
b980b5baea | ||
|
4ba34ab511 | ||
|
5be317d73c | ||
|
af16205965 | ||
|
39917b77c1 | ||
|
124ecb1372 | ||
|
33c0c1bea6 | ||
|
a66990459e | ||
|
fecbdc7fbf | ||
|
0369ace5f7 | ||
|
1657048181 | ||
|
b9bdaa7a56 | ||
|
f28d07e17b | ||
|
8b8bf1debc | ||
|
aff1c1e3ef | ||
|
169bb2c9bb | ||
|
fb1eafef43 | ||
|
bfe26ceb39 | ||
|
050f305e80 | ||
|
63a6a4f823 | ||
|
a3b167cab5 | ||
|
48327948e2 | ||
|
93856d4577 | ||
|
7ff068aa95 | ||
|
b2f00c869e | ||
|
b717cab8f6 | ||
|
adaff52707 | ||
|
54050edcc6 | ||
|
9acbb69a6a | ||
|
a5e6de047a | ||
|
3d8d35207b | ||
|
0a95f59813 | ||
|
43a3d28dbd | ||
|
685cb7a505 | ||
|
dd82466d07 | ||
|
2cbe4a013e | ||
|
fb85341844 | ||
|
116b3ecdad | ||
|
af85fbf0a3 | ||
|
1d250593c0 | ||
|
ed33a054ad | ||
|
4e3e015912 | ||
|
7821c52842 | ||
|
0a6f299ae6 | ||
|
73f87e30c2 | ||
|
838ece2c89 | ||
|
d8b88ea2c0 | ||
|
5908951b75 | ||
|
0b41f4c4d2 | ||
|
35439d4fbc | ||
|
fdce40310f | ||
|
6b4785ae32 | ||
|
f74e8e9cb7 | ||
|
5a4eb7e09e | ||
|
3bc4df03cc | ||
|
5d585132fb | ||
|
eff4905883 | ||
|
8923ac4fe3 | ||
|
073535e5ed | ||
|
d304b90ca6 | ||
|
816c26e14e | ||
|
b1244ffa01 | ||
|
fc1342bff9 | ||
|
d7b95194b5 | ||
|
b58bdeccd2 | ||
|
f260b9bdee | ||
|
fb1bdc9ec5 | ||
|
697eff48fc | ||
|
c05019339a | ||
|
8438efaf41 | ||
|
81c019cc99 | ||
|
c773fdc435 | ||
|
c1406f51f1 | ||
|
92affd3440 | ||
|
d3da0652ef | ||
|
e3fbbd6cf1 | ||
|
fcff13470c | ||
|
e3061ee7e7 | ||
|
0ee305fc4a | ||
|
58b93fd0c4 | ||
|
b30217fa2d | ||
|
ae48eec3a2 | ||
|
948233ba27 | ||
|
c2db9b183a | ||
|
6d2b88fa0b | ||
|
1d5da825c5 | ||
|
330c9b53d6 | ||
|
751fe7d4fb | ||
|
9df1fc6e5d | ||
|
8d660f1701 | ||
|
4d61d3c4aa | ||
|
0457088c99 | ||
|
8e575da74e | ||
|
48ed28888e | ||
|
4084b1124e | ||
|
60ba607027 | ||
|
3df2c11b4a | ||
|
c93221923a | ||
|
375317e932 | ||
|
7ce527957a | ||
|
6946521199 | ||
|
18ee20e680 | ||
|
c53da15219 | ||
|
d4995e342f | ||
|
c9f14da294 | ||
|
e9c2446cba | ||
|
35f179625c | ||
|
39749aa113 | ||
|
ba65e982fd | ||
|
b50e5d7e59 | ||
|
a3148dc172 | ||
|
73f1491d2d | ||
|
28eb54dc96 | ||
|
21fb426524 | ||
|
d5710ca809 | ||
|
0ba6cdda17 | ||
|
afdcfa8525 | ||
|
5db4f8512b | ||
|
dc0c1b73bc | ||
|
f999257095 | ||
|
7182909e28 | ||
|
fe3f015171 | ||
|
5bb668be63 | ||
|
01de147900 | ||
|
a7e5fcc806 | ||
|
e2d187d74b | ||
|
48b0620629 | ||
|
19e9f382e4 | ||
|
446eaf6588 | ||
|
78deb1420d | ||
|
e092515dff | ||
|
81f6fef978 | ||
|
6a2f8fa9ee | ||
|
a79a8c8874 | ||
|
c39659b064 | ||
|
9a30fbd05a | ||
|
83f48418f6 | ||
|
bcd7b41c91 | ||
|
cefb7d12bc | ||
|
3c0c15103e | ||
|
a8a8afc2be | ||
|
49e32abd3f | ||
|
7977eefaca | ||
|
f1fa6c3108 | ||
|
2fa0d55f39 | ||
|
5bff509346 | ||
|
a147e9b74a | ||
|
0d87f7c4ca | ||
|
8c675615df | ||
|
7edd1bff40 | ||
|
3bfcb1f3ab | ||
|
7b6c63e6a8 | ||
|
5500e5b0aa | ||
|
e4d249e73c | ||
|
091f6e918b | ||
|
5d9b68c3e7 | ||
|
12a6a61100 | ||
|
7ce3b8d4ef | ||
|
3d9b855849 | ||
|
2346d2ec05 | ||
|
a4c081c8a5 | ||
|
316980efbd | ||
|
a05bc0eed0 | ||
|
4d1c271da6 | ||
|
0dd7ecbfbe | ||
|
0dc188b083 | ||
|
6a553f77f3 | ||
|
a74cef439b | ||
|
9a3cd27700 | ||
|
801c7c0ab6 | ||
|
a95a4e783a | ||
|
af1ee9db93 | ||
|
fcdb6fd2a7 | ||
|
97c0fb389d | ||
|
a9c3992331 | ||
|
a38e057fa7 | ||
|
f83aaf77f1 | ||
|
d92768ecbf | ||
|
b9308cd74a | ||
|
78b577bc9d | ||
|
7d247897ed | ||
|
5dcbdec491 | ||
|
9bf980431e | ||
|
da60bfbcff | ||
|
92553cbc7e | ||
|
8e48e53f17 | ||
|
2f9a4bb79a | ||
|
ac968dd6cd | ||
|
6e4f2c0c8a | ||
|
d662c18ed7 | ||
|
e4ea234707 | ||
|
0b526c0168 | ||
|
2acde5c72a | ||
|
ec8cf2c459 | ||
|
3598780d54 | ||
|
35dd8ac6e6 | ||
|
5ff7c7ffab |
74
CHANGELOG.md
74
CHANGELOG.md
@@ -2,7 +2,81 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [4.1.6] - 2016-11-06
|
||||
### Added
|
||||
- New budget table for multi year report.
|
||||
|
||||
### Changed
|
||||
- Greatly expanded help pages and their function.
|
||||
- Built a new transaction collector, which I think was the idea of @roberthorlings originally.
|
||||
- Rebuilt seach engine.
|
||||
|
||||
### Fixed
|
||||
- #375, thanks to @schoentoon which made it impossible to resurrect currencies.
|
||||
- #370 thanks to @ksmolder
|
||||
- #378, thanks to @HomelessAvatar
|
||||
|
||||
## [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
42
Dockerfile
Normal 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
|
152
app/Console/Commands/CreateImport.php
Normal file
152
app/Console/Commands/CreateImport.php
Normal 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;
|
||||
}
|
||||
}
|
@@ -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,13 +57,16 @@ 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;
|
||||
}
|
||||
|
||||
$this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type);
|
||||
$this->line(sprintf('Going to import job with key "%s" of type "%s"', $job->key, $job->file_type));
|
||||
|
||||
$monolog = Log::getMonolog();
|
||||
$handler = new CommandHandler($this);
|
||||
|
86
app/Console/Commands/MoveRepository.php
Normal file
86
app/Console/Commands/MoveRepository.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* MoveRepository.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 Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class MoveRepository
|
||||
*
|
||||
* @package FireflyIII\Console\Commands
|
||||
*/
|
||||
class MoveRepository extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Alerts the user that the Github repository will move, if they are interested to know this.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly:github-move';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$moveDate = new Carbon('2017-01-01');
|
||||
$final = new Carbon('2017-03-01');
|
||||
$now = new Carbon;
|
||||
|
||||
// display message before 2017-01-01
|
||||
if ($moveDate > $now) {
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line('The Github repository for Firefly III will MOVE');
|
||||
$this->line('This move will be on January 1st 2017');
|
||||
$this->line('');
|
||||
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
|
||||
$this->line('');
|
||||
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
// display message after 2017-01-01 but before 2017-03-01
|
||||
if ($moveDate <= $now && $now <= $final) {
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line('The Github repository for Firefly III has MOVED');
|
||||
$this->line('This move was on January 1st 2017!');
|
||||
$this->line('');
|
||||
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
|
||||
$this->line('');
|
||||
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
121
app/Console/Commands/UpgradeDatabase.php
Normal file
121
app/Console/Commands/UpgradeDatabase.php
Normal 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(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
||||
$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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -63,21 +63,20 @@ class UpgradeFireflyInstructions extends Command
|
||||
|
||||
}
|
||||
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
|
||||
if (is_null($text)) {
|
||||
$this->line('Thank you for installing Firefly III, v' . $version);
|
||||
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
|
||||
$this->info('There are no extra upgrade instructions.');
|
||||
$this->line('Firefly III should be ready for use.');
|
||||
} else {
|
||||
$this->line('Thank you for installing Firefly III, v' . $version);
|
||||
$this->line('If you are upgrading from a previous version,');
|
||||
$this->line('please follow these upgrade instructions carefully:');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
|
||||
$this->info(wordwrap($text));
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,8 +127,11 @@ class VerifyDatabase extends Command
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name)
|
||||
. '") which has no budget limits.';
|
||||
|
||||
$line = sprintf(
|
||||
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$entry->user_id, $entry->email, $entry->id, Crypt::decrypt($entry->name)
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
@@ -202,6 +209,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:
|
||||
*/
|
||||
|
@@ -13,9 +13,12 @@ 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\MoveRepository;
|
||||
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,9 +60,11 @@ class Kernel extends ConsoleKernel
|
||||
UpgradeFireflyInstructions::class,
|
||||
VerifyDatabase::class,
|
||||
Import::class,
|
||||
CreateImport::class,
|
||||
EncryptFile::class,
|
||||
ScanAttachments::class,
|
||||
|
||||
UpgradeDatabase::class,
|
||||
MoveRepository::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@@ -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];
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
}
|
||||
|
348
app/Export/Collector/JournalExportCollector.php
Normal file
348
app/Export/Collector/JournalExportCollector.php
Normal file
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
/**
|
||||
* JournalExportCollector.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 JournalExportCollector
|
||||
*
|
||||
* @package FireflyIII\Export\Collector
|
||||
*/
|
||||
class JournalExportCollector 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',
|
||||
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,9 +14,7 @@ declare(strict_types = 1);
|
||||
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 +60,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()
|
||||
{
|
||||
|
@@ -15,13 +15,13 @@ namespace FireflyIII\Export;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Export\Collector\AttachmentCollector;
|
||||
use FireflyIII\Export\Collector\JournalExportCollector;
|
||||
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,15 +40,11 @@ class Processor
|
||||
/** @var bool */
|
||||
public $includeAttachments;
|
||||
/** @var bool */
|
||||
public $includeConfig;
|
||||
/** @var bool */
|
||||
public $includeOldUploads;
|
||||
/** @var ExportJob */
|
||||
public $job;
|
||||
/** @var array */
|
||||
public $settings;
|
||||
/** @var \FireflyIII\Export\ConfigurationFile */
|
||||
private $configurationMaker;
|
||||
/** @var Collection */
|
||||
private $exportEntries;
|
||||
/** @var Collection */
|
||||
@@ -68,7 +64,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 +79,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 +91,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 JournalExportCollector $collector */
|
||||
$collector = app(JournalExportCollector::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 +111,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 +122,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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -61,13 +61,13 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface
|
||||
$minAmount = [];
|
||||
$maxAmount = [];
|
||||
$actualAmount = [];
|
||||
/** @var TransactionJournal $entry */
|
||||
/** @var Transaction $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry->date->formatLocalized($format);
|
||||
$minAmount[] = round($bill->amount_min, 2);
|
||||
$maxAmount[] = round($bill->amount_max, 2);
|
||||
// journalAmount has been collected in BillRepository::getJournals
|
||||
$actualAmount[] = round(TransactionJournal::amountPositive($entry), 2);
|
||||
$actualAmount[] = bcmul($entry->transaction_amount, '-1');
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
|
@@ -38,13 +38,6 @@ interface BudgetChartGeneratorInterface
|
||||
*/
|
||||
public function frontpage(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $viewRange
|
||||
|
@@ -100,40 +100,6 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array
|
||||
{
|
||||
// dataset:
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
// get labels from one of the budgets (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
$keys = array_keys($first['budgeted']);
|
||||
foreach ($keys as $year) {
|
||||
$data['labels'][] = strval($year);
|
||||
}
|
||||
|
||||
// then, loop all entries and create datasets:
|
||||
foreach ($entries as $entry) {
|
||||
$name = $entry['name'];
|
||||
$spent = $entry['spent'];
|
||||
$budgeted = $entry['budgeted'];
|
||||
$data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)];
|
||||
$data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)];
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $viewRange
|
||||
|
@@ -45,13 +45,6 @@ interface CategoryChartGeneratorInterface
|
||||
*/
|
||||
public function frontpage(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
|
@@ -117,34 +117,6 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array
|
||||
{
|
||||
// get labels from one of the categories (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
$data = ['count' => 0, 'labels' => array_keys($first['spent']), 'datasets' => [],];
|
||||
|
||||
// then, loop all entries and create datasets:
|
||||
foreach ($entries as $entry) {
|
||||
$name = $entry['name'];
|
||||
$spent = $entry['spent'];
|
||||
$earned = $entry['earned'];
|
||||
if (array_sum(array_values($spent)) != 0) {
|
||||
$data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)];
|
||||
}
|
||||
if (array_sum(array_values($earned)) != 0) {
|
||||
$data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)];
|
||||
}
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Collection $entries
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
164
app/Handlers/Events/StoredJournalEventHandler.php
Normal file
164
app/Handlers/Events/StoredJournalEventHandler.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?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;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
|
||||
|
||||
/** @var PiggyBank $piggyBank */
|
||||
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
||||
|
||||
if (is_null($piggyBank)) {
|
||||
Log::error('No such piggy bank!');
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name));
|
||||
// update piggy bank rep for date of transaction journal.
|
||||
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
||||
if (is_null($repetition)) {
|
||||
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$amount = TransactionJournal::amountPositive($journal);
|
||||
Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
// 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');
|
||||
Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id));
|
||||
}
|
||||
|
||||
// if the amount is positive:
|
||||
// make sure it fits in piggy bank:
|
||||
if (bccomp($amount, '0') === 1) {
|
||||
// amount is positive
|
||||
$room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount));
|
||||
Log::debug(sprintf('Room in piggy bank for extra money is %f', $room));
|
||||
if (bccomp($room, $amount) === -1) {
|
||||
// $room is smaller than $amount
|
||||
Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $room));
|
||||
$amount = $room;
|
||||
}
|
||||
}
|
||||
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
// amount is negative
|
||||
Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount));
|
||||
$compare = bcmul($repetition->currentamount, '-1');
|
||||
if (bccomp($compare, $amount) === 1) {
|
||||
// $currentamount is smaller than $amount
|
||||
Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $compare));
|
||||
$amount = $compare;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
|
||||
$repetition->save();
|
||||
|
||||
/** @var PiggyBankEvent $event */
|
||||
$event = PiggyBankEvent::create(
|
||||
['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]
|
||||
);
|
||||
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* FireRulesForUpdate.php
|
||||
* UpdatedJournalEventHandler.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
@@ -13,32 +13,33 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\TransactionJournalUpdated;
|
||||
|
||||
use FireflyIII\Events\UpdatedTransactionJournal;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\User;
|
||||
use FireflyIII\Support\Events\BillScanner;
|
||||
|
||||
/**
|
||||
* Class FireRulesForUpdate
|
||||
* Class UpdatedJournalEventHandler
|
||||
*
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class FireRulesForUpdate
|
||||
class UpdatedJournalEventHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
* This method will check all the rules when a journal is updated.
|
||||
*
|
||||
* @param TransactionJournalUpdated $event
|
||||
* @param UpdatedTransactionJournal $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handle(TransactionJournalUpdated $event): bool
|
||||
public function processRules(UpdatedTransactionJournal $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();
|
||||
$journal = $event->journal;
|
||||
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
|
||||
//
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
@@ -51,7 +52,7 @@ class FireRulesForUpdate
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
$processor = Processor::make($rule);
|
||||
$processor->handleTransactionJournal($event->journal);
|
||||
$processor->handleTransactionJournal($journal);
|
||||
|
||||
if ($rule->stop_processing) {
|
||||
break;
|
||||
@@ -62,4 +63,19 @@ class FireRulesForUpdate
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
225
app/Handlers/Events/UserEventHandler.php
Normal file
225
app/Handlers/Events/UserEventHandler.php
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -17,10 +17,8 @@ use FireflyIII\Models\Attachment;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Input;
|
||||
use Log;
|
||||
use Storage;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Class AttachmentHelper
|
||||
@@ -236,13 +234,8 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
private function getFiles()
|
||||
{
|
||||
$files = null;
|
||||
try {
|
||||
if (Input::hasFile('attachments')) {
|
||||
$files = Input::file('attachments');
|
||||
}
|
||||
} catch (TypeError $e) {
|
||||
// Log it, do nothing else.
|
||||
Log::error($e->getMessage());
|
||||
if (Input::hasFile('attachments')) {
|
||||
$files = Input::file('attachments');
|
||||
}
|
||||
|
||||
return $files;
|
||||
|
485
app/Helpers/Collector/JournalCollector.php
Normal file
485
app/Helpers/Collector/JournalCollector.php
Normal file
@@ -0,0 +1,485 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Helpers\Collector;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Maybe this is a good idea after all...
|
||||
*
|
||||
* Class JournalCollector
|
||||
*
|
||||
* @package FireflyIII\Helpers\Collector
|
||||
*/
|
||||
class JournalCollector
|
||||
{
|
||||
|
||||
/** @var int */
|
||||
private $count = 0;
|
||||
|
||||
/** @var array */
|
||||
private $fields
|
||||
= [
|
||||
'transaction_journals.id as journal_id',
|
||||
'transaction_journals.description',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.encrypted',
|
||||
//'transaction_journals.transaction_currency_id',
|
||||
'transaction_currencies.code as transaction_currency_code',
|
||||
//'transaction_currencies.symbol as transaction_currency_symbol',
|
||||
'transaction_types.type as transaction_type_type',
|
||||
'transaction_journals.bill_id',
|
||||
'bills.name as bill_name',
|
||||
'transactions.id as id',
|
||||
'transactions.amount as transaction_amount',
|
||||
'transactions.description as transaction_description',
|
||||
'transactions.account_id',
|
||||
'transactions.identifier',
|
||||
'transactions.transaction_journal_id',
|
||||
'accounts.name as account_name',
|
||||
'accounts.encrypted as account_encrypted',
|
||||
'account_types.type as account_type',
|
||||
];
|
||||
/** @var bool */
|
||||
private $filterTransfers = false;
|
||||
/** @var bool */
|
||||
private $joinedBudget = false;
|
||||
/** @var bool */
|
||||
private $joinedCategory = false;
|
||||
/** @var bool */
|
||||
private $joinedTag = false;
|
||||
/** @var int */
|
||||
private $limit;
|
||||
/** @var int */
|
||||
private $offset;
|
||||
/** @var int */
|
||||
private $page = 1;
|
||||
/** @var EloquentBuilder */
|
||||
private $query;
|
||||
/** @var bool */
|
||||
private $run = false;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* JournalCollector constructor.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->query = $this->startQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
if ($this->run === true) {
|
||||
throw new FireflyException('Cannot count after run in JournalCollector.');
|
||||
}
|
||||
|
||||
$countQuery = clone $this->query;
|
||||
|
||||
// dont need some fields:
|
||||
$countQuery->getQuery()->limit = null;
|
||||
$countQuery->getQuery()->offset = null;
|
||||
$countQuery->getQuery()->unionLimit = null;
|
||||
$countQuery->getQuery()->groups = null;
|
||||
$countQuery->getQuery()->orders = null;
|
||||
$countQuery->groupBy('accounts.user_id');
|
||||
$this->count = $countQuery->count();
|
||||
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournals(): Collection
|
||||
{
|
||||
$this->run = true;
|
||||
$set = $this->query->get(array_values($this->fields));
|
||||
$set = $this->filterTransfers($set);
|
||||
|
||||
// loop for decryption.
|
||||
$set->each(
|
||||
function (Transaction $transaction) {
|
||||
$transaction->date = new Carbon($transaction->date);
|
||||
$transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description;
|
||||
$transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : '';
|
||||
}
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LengthAwarePaginator
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getPaginatedJournals():LengthAwarePaginator
|
||||
{
|
||||
if ($this->run === true) {
|
||||
throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.');
|
||||
}
|
||||
$this->count();
|
||||
$set = $this->getJournals();
|
||||
$journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page);
|
||||
|
||||
return $journals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): JournalCollector
|
||||
{
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$this->query->whereIn('transactions.account_id', $accountIds);
|
||||
}
|
||||
|
||||
if ($accounts->count() > 1) {
|
||||
$this->filterTransfers = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setAllAssetAccounts(): JournalCollector
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class, [$this->user]);
|
||||
$accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$this->query->whereIn('transactions.account_id', $accountIds);
|
||||
}
|
||||
|
||||
if ($accounts->count() > 1) {
|
||||
$this->filterTransfers = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $bills
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setBills(Collection $bills): JournalCollector
|
||||
{
|
||||
if ($bills->count() > 0) {
|
||||
$billIds = $bills->pluck('id')->toArray();
|
||||
$this->query->whereIn('transaction_journals.bill_id', $billIds);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setBudget(Budget $budget): JournalCollector
|
||||
{
|
||||
$this->joinBudgetTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($budget) {
|
||||
$q->where('budget_transaction.budget_id', $budget->id);
|
||||
$q->orWhere('budget_transaction_journal.budget_id', $budget->id);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setCategory(Category $category): JournalCollector
|
||||
{
|
||||
$this->joinCategoryTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($category) {
|
||||
$q->where('category_transaction.category_id', $category->id);
|
||||
$q->orWhere('category_transaction_journal.category_id', $category->id);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setLimit(int $limit): JournalCollector
|
||||
{
|
||||
$this->limit = $limit;
|
||||
$this->query->limit($limit);
|
||||
Log::debug(sprintf('Set limit to %d', $limit));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setOffset(int $offset): JournalCollector
|
||||
{
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setPage(int $page): JournalCollector
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
if ($page > 0) {
|
||||
$page--;
|
||||
}
|
||||
Log::debug(sprintf('Page is %d', $page));
|
||||
|
||||
if (!is_null($this->limit)) {
|
||||
$offset = ($this->limit * $page);
|
||||
$this->offset = $offset;
|
||||
$this->query->skip($offset);
|
||||
Log::debug(sprintf('Changed offset to %d', $offset));
|
||||
}
|
||||
if (is_null($this->limit)) {
|
||||
Log::debug('The limit is zero, cannot set the page.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setRange(Carbon $start, Carbon $end): JournalCollector
|
||||
{
|
||||
if ($start <= $end) {
|
||||
$this->query->where('transaction_journals.date', '>=', $start->format('Y-m-d'));
|
||||
$this->query->where('transaction_journals.date', '<=', $end->format('Y-m-d'));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setTag(Tag $tag): JournalCollector
|
||||
{
|
||||
$this->joinTagTables();
|
||||
$this->query->where('tag_transaction_journal.tag_id', $tag->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function setTypes(array $types): JournalCollector
|
||||
{
|
||||
if (count($types) > 0) {
|
||||
$this->query->whereIn('transaction_types.type', $types);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function withoutBudget(): JournalCollector
|
||||
{
|
||||
$this->joinBudgetTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('budget_transaction.budget_id');
|
||||
$q->whereNull('budget_transaction_journal.budget_id');
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollector
|
||||
*/
|
||||
public function withoutCategory(): JournalCollector
|
||||
{
|
||||
$this->joinCategoryTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('category_transaction.category_id');
|
||||
$q->whereNull('category_transaction_journal.category_id');
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the set of accounts used by the collector includes more than one asset
|
||||
* account, chances are the set include double entries: transfers get selected
|
||||
* on both the source, and then again on the destination account.
|
||||
*
|
||||
* This method filters them out.
|
||||
*
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function filterTransfers(Collection $set): Collection
|
||||
{
|
||||
if ($this->filterTransfers) {
|
||||
$set = $set->filter(
|
||||
function (Transaction $transaction) {
|
||||
if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) {
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Included journal #%d (transaction #%d) because its a %s with amount %f',
|
||||
$transaction->transaction_journal_id,
|
||||
$transaction->id,
|
||||
$transaction->transaction_type_type,
|
||||
$transaction->transaction_amount
|
||||
)
|
||||
);
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Removed journal #%d (transaction #%d) because its a %s with amount %f',
|
||||
$transaction->transaction_journal_id,
|
||||
$transaction->id,
|
||||
$transaction->transaction_type_type,
|
||||
$transaction->transaction_amount
|
||||
)
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function joinBudgetTables()
|
||||
{
|
||||
if (!$this->joinedBudget) {
|
||||
// join some extra tables:
|
||||
$this->joinedBudget = true;
|
||||
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function joinCategoryTables()
|
||||
{
|
||||
if (!$this->joinedCategory) {
|
||||
// join some extra tables:
|
||||
$this->joinedCategory = true;
|
||||
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function joinTagTables()
|
||||
{
|
||||
if (!$this->joinedTag) {
|
||||
// join some extra tables:
|
||||
$this->joinedTag = true;
|
||||
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EloquentBuilder
|
||||
*/
|
||||
private function startQuery(): EloquentBuilder
|
||||
{
|
||||
|
||||
$query = Transaction
|
||||
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC');
|
||||
|
||||
return $query;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -14,7 +14,9 @@ namespace FireflyIII\Helpers\Help;
|
||||
|
||||
use Cache;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
use Log;
|
||||
use Requests;
|
||||
use Requests_Exception;
|
||||
use Route;
|
||||
|
||||
/**
|
||||
@@ -24,49 +26,57 @@ use Route;
|
||||
*/
|
||||
class Help implements HelpInterface
|
||||
{
|
||||
/** @var string */
|
||||
protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36';
|
||||
|
||||
/**
|
||||
*
|
||||
* @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);
|
||||
$line = sprintf('help.%s.%s', $route, $language);
|
||||
|
||||
return Cache::get($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
$opt = ['useragent' => $this->userAgent];
|
||||
$content = '';
|
||||
try {
|
||||
$result = Requests::get($uri, [], $opt);
|
||||
} 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);
|
||||
}
|
||||
if (strlen($content) === 0) {
|
||||
Log::warning('Raw content length is zero.');
|
||||
}
|
||||
$converter = new CommonMarkConverter();
|
||||
$content['text'] = $converter->convertToHtml($content['text']);
|
||||
|
||||
return $content;
|
||||
|
||||
@@ -84,27 +94,42 @@ 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');
|
||||
$line = sprintf('help.%s.%s', $route, $language);
|
||||
$result = Cache::has($line);
|
||||
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 = sprintf('help.%s.%s', $route, $language);
|
||||
if (strlen($content) > 0) {
|
||||
Log::debug(sprintf('Will store entry in cache: %s', $key));
|
||||
Cache::put($key, $content, 10080); // a week.
|
||||
return;
|
||||
}
|
||||
Log::info(sprintf('Will not cache %s because content is empty.', $key));
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -15,13 +15,17 @@ namespace FireflyIII\Helpers\Report;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
|
||||
use FireflyIII\Helpers\Collection\BudgetLine;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class BudgetReportHelper
|
||||
@@ -97,6 +101,66 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array
|
||||
{
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$query = TransactionJournal
|
||||
::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin(
|
||||
'transactions', function (JoinClause $join) {
|
||||
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
|
||||
}
|
||||
)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->where('transaction_types.type', 'Withdrawal')
|
||||
->where('transaction_journals.user_id', auth()->user()->id);
|
||||
|
||||
if (count($accountIds) > 0) {
|
||||
$query->whereIn('transactions.account_id', $accountIds);
|
||||
}
|
||||
$query->groupBy(['budget_transaction_journal.budget_id', 'the_year']);
|
||||
$queryResult = $query->get(
|
||||
[
|
||||
'budget_transaction_journal.budget_id',
|
||||
DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'),
|
||||
DB::raw('SUM(transactions.amount) as sum_of_period'),
|
||||
]
|
||||
);
|
||||
|
||||
$data = [];
|
||||
$budgets = $this->repository->getBudgets();
|
||||
$years = $this->listOfYears($start, $end);
|
||||
|
||||
// do budget "zero"
|
||||
$emptyBudget = new Budget;
|
||||
$emptyBudget->id = 0;
|
||||
$emptyBudget->name = strval(trans('firefly.no_budget'));
|
||||
$budgets->push($emptyBudget);
|
||||
|
||||
|
||||
// get all budgets and years.
|
||||
foreach ($budgets as $budget) {
|
||||
$data[$budget->id] = [
|
||||
'name' => $budget->name,
|
||||
'entries' => $this->filterAmounts($queryResult, $budget->id, $years),
|
||||
'sum' => '0',
|
||||
];
|
||||
}
|
||||
// filter out empty ones and fill sum:
|
||||
$data = $this->getBudgetMultiYearMeta($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@@ -183,30 +247,21 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
|
||||
* and sum up everything in the array in the given range.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
|
||||
public function listOfYears(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$sum = '0';
|
||||
$currentStart = clone $start; // to not mess with the original one
|
||||
$currentEnd = clone $end; // to not mess with the original one
|
||||
|
||||
while ($currentStart <= $currentEnd) {
|
||||
$date = $currentStart->format('Y-m-d');
|
||||
if (isset($array[$date])) {
|
||||
$sum = bcadd($sum, $array[$date]);
|
||||
}
|
||||
$currentStart->addDay();
|
||||
$begin = clone $start;
|
||||
$years = [];
|
||||
while ($begin < $end) {
|
||||
$years[] = $begin->year;
|
||||
$begin->addYear();
|
||||
}
|
||||
|
||||
return $sum;
|
||||
return $years;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +302,59 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
* @param int $budgetId
|
||||
* @param array $years
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filterAmounts(Collection $set, int $budgetId, array $years):array
|
||||
{
|
||||
$arr = [];
|
||||
foreach ($years as $year) {
|
||||
/** @var stdClass $object */
|
||||
$result = $set->filter(
|
||||
function (TransactionJournal $object) use ($budgetId, $year) {
|
||||
return intval($object->the_year) === $year && $budgetId === intval($object->budget_id);
|
||||
}
|
||||
);
|
||||
$amount = '0';
|
||||
if (!is_null($result->first())) {
|
||||
$amount = $result->first()->sum_of_period;
|
||||
}
|
||||
|
||||
$arr[$year] = $amount;
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBudgetMultiYearMeta(array $data): array
|
||||
{
|
||||
/**
|
||||
* @var int $budgetId
|
||||
* @var array $set
|
||||
*/
|
||||
foreach ($data as $budgetId => $set) {
|
||||
$sum = '0';
|
||||
foreach ($set['entries'] as $amount) {
|
||||
$sum = bcadd($amount, $sum);
|
||||
}
|
||||
$data[$budgetId]['sum'] = $sum;
|
||||
if (bccomp('0', $sum) === 0) {
|
||||
unset($data[$budgetId]);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $current
|
||||
* @param Carbon $end
|
||||
|
@@ -34,6 +34,15 @@ interface BudgetReportHelperInterface
|
||||
*/
|
||||
public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@@ -52,4 +61,12 @@ interface BudgetReportHelperInterface
|
||||
*/
|
||||
public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listOfYears(Carbon $start, Carbon $end): array;
|
||||
|
||||
}
|
||||
|
@@ -19,11 +19,12 @@ use FireflyIII\Helpers\Collection\BillLine;
|
||||
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
|
||||
use FireflyIII\Helpers\Collection\Expense;
|
||||
use FireflyIII\Helpers\Collection\Income;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\FiscalHelperInterface;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
@@ -77,7 +78,9 @@ class ReportHelper implements ReportHelperInterface
|
||||
/** @var BillRepositoryInterface $repository */
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$bills = $repository->getBillsForAccounts($accounts);
|
||||
$journals = $repository->getAllJournalsInRange($bills, $start, $end);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills);
|
||||
$journals = $collector->getJournals();
|
||||
$collection = new BillCollection;
|
||||
|
||||
/** @var Bill $bill */
|
||||
@@ -91,18 +94,19 @@ class ReportHelper implements ReportHelperInterface
|
||||
// is hit in period?
|
||||
|
||||
$entry = $journals->filter(
|
||||
function (TransactionJournal $journal) use ($bill) {
|
||||
return $journal->bill_id === $bill->id;
|
||||
function (Transaction $transaction) use ($bill) {
|
||||
return $transaction->bill_id === $bill->id;
|
||||
}
|
||||
);
|
||||
$first = $entry->first();
|
||||
if (!is_null($first)) {
|
||||
$billLine->setTransactionJournalId($first->id);
|
||||
$billLine->setAmount($first->journalAmount);
|
||||
$billLine->setAmount($first->transaction_amount);
|
||||
$billLine->setHit(true);
|
||||
}
|
||||
|
||||
if ($billLine->isActive()) {
|
||||
// non active AND non hit? do not add:
|
||||
if ($billLine->isActive() || $billLine->isHit()) {
|
||||
$collection->addBill($billLine);
|
||||
}
|
||||
}
|
||||
@@ -304,30 +308,4 @@ class ReportHelper implements ReportHelperInterface
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
|
||||
* and sum up everything in the array in the given range.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
|
||||
{
|
||||
$sum = '0';
|
||||
$currentStart = clone $start; // to not mess with the original one
|
||||
$currentEnd = clone $end; // to not mess with the original one
|
||||
|
||||
while ($currentStart <= $currentEnd) {
|
||||
$date = $currentStart->format('Y-m-d');
|
||||
if (isset($array[$date])) {
|
||||
$sum = bcadd($sum, $array[$date]);
|
||||
}
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
}
|
||||
|
@@ -15,13 +15,15 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
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;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Navigation;
|
||||
@@ -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;
|
||||
@@ -209,13 +221,13 @@ class AccountController extends Controller
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$page = intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id);
|
||||
|
||||
// grouped other months thing:
|
||||
@@ -263,27 +275,25 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountTaskerInterface $tasker
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showWithDate(AccountTaskerInterface $tasker, Account $account, string $date)
|
||||
public function showWithDate(Account $account, string $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')';
|
||||
$page = intval(Input::get('page'));
|
||||
$page = $page === 0 ? 1 : $page;
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
|
||||
|
||||
return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon'));
|
||||
@@ -298,23 +308,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 +341,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 +374,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]));
|
||||
}
|
||||
}
|
||||
|
@@ -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')));
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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';
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -41,6 +41,8 @@ class ResetPasswordController extends Controller
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware('guest');
|
||||
}
|
||||
}
|
||||
|
@@ -14,10 +14,12 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\BillFormRequest;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Preferences;
|
||||
use Session;
|
||||
@@ -38,8 +40,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 +147,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);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -200,11 +202,16 @@ class BillController extends Controller
|
||||
$year = $date->year;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$journals = $repository->getJournals($bill, $page, $pageSize);
|
||||
$yearAverage = $repository->getYearAverage($bill, $date);
|
||||
$overallAverage = $repository->getOverallAverage($bill);
|
||||
|
||||
// use collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/bills/show/' . $bill->id);
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
|
||||
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon);
|
||||
$hideBill = true;
|
||||
$subTitle = e($bill->name);
|
||||
|
||||
|
@@ -17,15 +17,16 @@ use Amount;
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
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;
|
||||
@@ -236,31 +247,28 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function noBudget(BudgetRepositoryInterface $repository)
|
||||
public function noBudget()
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$journals = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget
|
||||
$count = $journals->count();
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$list = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = trans(
|
||||
'firefly.without_budget_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
$list->setPath('/budgets/list/noBudget');
|
||||
|
||||
return view('budgets.noBudget', compact('list', 'subTitle'));
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/list/noBudget');
|
||||
|
||||
return view('budgets.no-budget', compact('journals', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,25 +290,25 @@ 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());
|
||||
$end = new Carbon;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget
|
||||
$count = $journals->count();
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
// collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/show/' . $budget->id);
|
||||
|
||||
|
||||
@@ -310,7 +318,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,33 +326,36 @@ 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.');
|
||||
}
|
||||
$start = $repetition->startdate;
|
||||
$end = $repetition->enddate;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget
|
||||
$count = $journals->count();
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
|
||||
// collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$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 +370,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 +397,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();
|
||||
|
@@ -14,13 +14,13 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\CategoryFormRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Navigation;
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,23 +147,25 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function noCategory(CRI $repository)
|
||||
public function noCategory()
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->startOfMonth());
|
||||
$list = $repository->journalsInPeriodWithoutCategory(new Collection(), [], $start, $end); // category
|
||||
$end = session('end', Carbon::now()->startOfMonth());
|
||||
|
||||
// new collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
|
||||
$journals = $collector->getJournals();
|
||||
$subTitle = trans(
|
||||
'firefly.without_category_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
|
||||
return view('categories.noCategory', compact('list', 'subTitle'));
|
||||
return view('categories.no-category', compact('journals', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,16 +182,17 @@ class CategoryController extends Controller
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = $category->name;
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
|
||||
// use journal collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id);
|
||||
|
||||
// oldest transaction in category:
|
||||
@@ -210,7 +221,7 @@ class CategoryController extends Controller
|
||||
|
||||
|
||||
$categoryCollection = new Collection([$category]);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
|
||||
while ($end >= $start) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
@@ -225,18 +236,16 @@ class CategoryController extends Controller
|
||||
}
|
||||
$cache->store($entries);
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle'));
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @param $date
|
||||
* @param Category $category
|
||||
* @param $date
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showWithDate(CRI $repository, Category $category, string $date)
|
||||
public function showWithDate(Category $category, string $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
@@ -244,15 +253,16 @@ class CategoryController extends Controller
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $category->name;
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// new collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
||||
|
||||
|
||||
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
|
||||
}
|
||||
|
||||
@@ -264,11 +274,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 +299,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();
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -15,11 +15,13 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
@@ -64,12 +66,11 @@ class BillController extends Controller
|
||||
/**
|
||||
* Shows the overview for a bill. The min/max amount and matched journals.
|
||||
*
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param Bill $bill
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function single(BillRepositoryInterface $repository, Bill $bill)
|
||||
public function single(Bill $bill)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('single');
|
||||
@@ -80,12 +81,14 @@ class BillController extends Controller
|
||||
}
|
||||
|
||||
// get first transaction or today for start:
|
||||
$results = $repository->getJournals($bill, 1, 200);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]));
|
||||
$results = $collector->getJournals();
|
||||
|
||||
// resort:
|
||||
$results = $results->sortBy(
|
||||
function (TransactionJournal $journal) {
|
||||
return $journal->date->format('U');
|
||||
function (Transaction $transaction) {
|
||||
return $transaction->date->format('U');
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -15,10 +15,12 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -175,7 +177,7 @@ class BudgetController extends Controller
|
||||
$allEntries = $allEntries->merge($collection);
|
||||
|
||||
}
|
||||
$entry = $this->spentInPeriodWithout($repository, $start, $end);
|
||||
$entry = $this->spentInPeriodWithout($start, $end);
|
||||
$allEntries->push($entry);
|
||||
$data = $this->generator->frontpage($allEntries);
|
||||
$cache->store($data);
|
||||
@@ -183,80 +185,6 @@ class BudgetController extends Controller
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection $budgets
|
||||
*
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function multiYear(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets)
|
||||
{
|
||||
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($budgets);
|
||||
$cache->addProperty('multiYearBudget');
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
$budgetIds = $budgets->pluck('id')->toArray();
|
||||
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$budgeted = [];
|
||||
$entries = new Collection;
|
||||
// filter budgets once:
|
||||
$repetitions = $repetitions->filter(
|
||||
function (LimitRepetition $repetition) use ($budgetIds) {
|
||||
if (in_array(strval($repetition->budget_id), $budgetIds)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$year = $repetition->startdate->year;
|
||||
if (isset($budgeted[$repetition->budget_id][$year])) {
|
||||
$budgeted[$repetition->budget_id][$year] = bcadd($budgeted[$repetition->budget_id][$year], $repetition->amount);
|
||||
continue;
|
||||
}
|
||||
$budgeted[$repetition->budget_id][$year] = $repetition->amount;
|
||||
}
|
||||
|
||||
foreach ($budgets as $budget) {
|
||||
$currentStart = clone $start;
|
||||
$entry = ['name' => $budget->name, 'spent' => [], 'budgeted' => []];
|
||||
while ($currentStart < $end) {
|
||||
// fix the date:
|
||||
$currentEnd = clone $currentStart;
|
||||
$year = $currentStart->year;
|
||||
$currentEnd->endOfYear();
|
||||
|
||||
$spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $currentStart, $currentEnd);
|
||||
|
||||
// jump to next year.
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
|
||||
$entry['spent'][$year] = round($spent * -1, 2);
|
||||
$entry['budgeted'][$year] = isset($budgeted[$budget->id][$year]) ? round($budgeted[$budget->id][$year], 2) : 0;
|
||||
}
|
||||
$entries->push($entry);
|
||||
}
|
||||
$data = $this->generator->multiYear($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
@@ -393,19 +321,22 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function spentInPeriodWithout(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end):array
|
||||
private function spentInPeriodWithout(Carbon $start, Carbon $end):array
|
||||
{
|
||||
$list = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget
|
||||
$sum = '0';
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($list as $entry) {
|
||||
$sum = bcadd(TransactionJournal::amount($entry), $sum);
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$types = [TransactionType::WITHDRAWAL];
|
||||
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
|
||||
$journals = $collector->getJournals();
|
||||
$sum = '0';
|
||||
/** @var Transaction $entry */
|
||||
foreach ($journals as $entry) {
|
||||
$sum = bcadd($entry->transaction_amount, $sum);
|
||||
}
|
||||
|
||||
return [trans('firefly.no_budget'), '0', '0', $sum, '0', '0'];
|
||||
|
@@ -153,78 +153,6 @@ class CategoryController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function multiYear(Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
|
||||
{
|
||||
|
||||
/** @var CRI $repository */
|
||||
$repository = app(CRI::class);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($categories);
|
||||
$cache->addProperty('multiYearCategory');
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$entries = new Collection;
|
||||
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
$entry = ['name' => '', 'spent' => [], 'earned' => []];
|
||||
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart < $end) {
|
||||
// fix the date:
|
||||
$year = $currentStart->year;
|
||||
$currentEnd = clone $currentStart;
|
||||
$currentEnd->endOfYear();
|
||||
|
||||
// get data:
|
||||
if (is_null($category->id)) {
|
||||
$entry['name'] = trans('firefly.noCategory');
|
||||
$entry['spent'][$year] = ($repository->spentInPeriodWithoutCategory($accounts, $currentStart, $currentEnd) * -1);
|
||||
$entry['earned'][$year] = $repository->earnedInPeriodWithoutCategory($accounts, $currentStart, $currentEnd);
|
||||
|
||||
// jump to next year.
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
continue;
|
||||
|
||||
}
|
||||
// alternative is a normal category:
|
||||
$entry['name'] = $category->name;
|
||||
$entry['spent'][$year] = ($repository->spentInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd) * -1);
|
||||
$entry['earned'][$year] = $repository->earnedInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd);
|
||||
|
||||
// jump to next year.
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
}
|
||||
$entries->push($entry);
|
||||
}
|
||||
|
||||
// generate chart with data:
|
||||
$data = $this->generator->multiYear($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param Category $category
|
||||
|
@@ -13,7 +13,6 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
@@ -46,38 +45,19 @@ 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:
|
||||
|
||||
/**
|
||||
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
|
||||
* and sum up everything in the array in the given range.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
|
||||
{
|
||||
$sum = '0';
|
||||
$currentStart = clone $start; // to not mess with the original one
|
||||
$currentEnd = clone $end; // to not mess with the original one
|
||||
$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');
|
||||
|
||||
while ($currentStart <= $currentEnd) {
|
||||
$date = $currentStart->format('Y-m-d');
|
||||
if (isset($array[$date])) {
|
||||
$sum = bcadd($sum, $array[$date]);
|
||||
return $next($request);
|
||||
}
|
||||
$currentStart->addDay();
|
||||
}
|
||||
);
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +129,7 @@ class CurrencyController extends Controller
|
||||
|
||||
Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
|
||||
if (auth()->user()->hasRole('owner')) {
|
||||
$currency->delete();
|
||||
$currency->forceDelete();
|
||||
}
|
||||
|
||||
return redirect(session('currency.delete.url'));
|
||||
|
@@ -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:
|
||||
*/
|
||||
|
@@ -41,11 +41,9 @@ 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',
|
||||
];
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
|
||||
if (!$help->hasRoute($route)) {
|
||||
Log::error('No such route: ' . $route);
|
||||
@@ -53,17 +51,32 @@ 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(sprintf('Help text %s was in cache.', $language));
|
||||
|
||||
return Response::json($content);
|
||||
}
|
||||
|
||||
$content = $help->getFromGithub($language, $route);
|
||||
|
||||
// get backup language content (try English):
|
||||
if (strlen($content) === 0) {
|
||||
$language = 'en_US';
|
||||
if ($help->inCache($route, $language)) {
|
||||
Log::debug(sprintf('Help text %s was in cache.', $language));
|
||||
$content = $help->getFromCache($route, $language);
|
||||
}
|
||||
if (!$help->inCache($route, $language)) {
|
||||
$content = $help->getFromGithub($language, $route);
|
||||
$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);
|
||||
|
@@ -15,10 +15,10 @@ namespace FireflyIII\Http\Controllers;
|
||||
use Artisan;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,12 +115,11 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ARI $repository
|
||||
* @param AccountTaskerInterface $tasker
|
||||
* @param ARI $repository
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function index(ARI $repository, AccountTaskerInterface $tasker)
|
||||
public function index(ARI $repository)
|
||||
{
|
||||
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
@@ -128,23 +129,23 @@ 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);
|
||||
$set = $set->splice(0, 10);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1);
|
||||
$set = $collector->getJournals();
|
||||
|
||||
if (count($set) > 0) {
|
||||
$transactions[] = [$set, $account];
|
||||
@@ -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 ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -15,12 +15,12 @@ namespace FireflyIII\Http\Controllers;
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Models\AccountType;
|
||||
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\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Input;
|
||||
@@ -270,17 +270,20 @@ class JsonController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param $what
|
||||
* @param $what
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function transactionJournals(JournalRepositoryInterface $repository, $what)
|
||||
public function transactionJournals($what)
|
||||
{
|
||||
$descriptions = [];
|
||||
$type = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$types = [$type];
|
||||
$journals = $repository->getJournals($types, 1, 50);
|
||||
|
||||
// use journal collector instead:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setTypes($types)->setLimit(100)->setPage(1);
|
||||
$journals = $collector->getJournals();
|
||||
foreach ($journals as $j) {
|
||||
$descriptions[] = $j->description;
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,6 +107,12 @@ class PiggyBankController extends Controller
|
||||
$subTitle = trans('firefly.new_piggy_bank');
|
||||
$subTitleIcon = 'fa-plus';
|
||||
|
||||
if (count($accounts) === 0) {
|
||||
Session::flash('error', strval(trans('firefly.need_at_least_one_account')));
|
||||
|
||||
return redirect(route('new-user.index'));
|
||||
}
|
||||
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (session('piggy-banks.create.fromStore') !== true) {
|
||||
Session::put('piggy-banks.create.url', URL::previous());
|
||||
@@ -157,6 +171,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 +183,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 +362,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 +378,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 +404,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();
|
||||
|
@@ -17,12 +17,12 @@ namespace FireflyIII\Http\Controllers\Popup;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\Binder\AccountList;
|
||||
@@ -98,20 +98,39 @@ class ReportController extends Controller
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::WITHDRAWAL];
|
||||
|
||||
switch (true) {
|
||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
|
||||
$journals = $budgetRepository->journalsInPeriod(
|
||||
new Collection([$budget]), new Collection([$account]), $attributes['startDate'], $attributes['endDate']
|
||||
);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->setBudget($budget);
|
||||
$journals = $collector->getJournals();
|
||||
|
||||
break;
|
||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
|
||||
$budget->name = strval(trans('firefly.no_budget'));
|
||||
$journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setTypes($types)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->withoutBudget();
|
||||
$journals = $collector->getJournals();
|
||||
break;
|
||||
case ($role === BalanceLine::ROLE_DIFFROLE):
|
||||
// journals no budget, not corrected by a tag.
|
||||
$journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setTypes($types)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->withoutBudget();
|
||||
$journals = $collector->getJournals();
|
||||
|
||||
$budget->name = strval(trans('firefly.leftUnbalanced'));
|
||||
$journals = $journals->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
@@ -148,14 +167,21 @@ class ReportController extends Controller
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$budget = $repository->find(intval($attributes['budgetId']));
|
||||
if (is_null($budget->id)) {
|
||||
$journals = $repository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
} else {
|
||||
// get all expenses in budget in period:
|
||||
$journals = $repository->journalsInPeriod(new Collection([$budget]), $attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
}
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
|
||||
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
|
||||
$collector
|
||||
->setAccounts($attributes['accounts'])
|
||||
->setRange($attributes['startDate'], $attributes['endDate']);
|
||||
|
||||
if (is_null($budget->id)) {
|
||||
$collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
|
||||
}
|
||||
if (!is_null($budget->id)) {
|
||||
// get all expenses in budget in period:
|
||||
$collector->setBudget($budget);
|
||||
}
|
||||
$journals = $collector->getJournals();
|
||||
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
|
||||
|
||||
return $view;
|
||||
}
|
||||
@@ -173,8 +199,15 @@ 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']);
|
||||
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
// get journal collector instead:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($attributes['accounts'])->setTypes($types)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->setCategory($category);
|
||||
$journals = $collector->getJournals(); // 7193
|
||||
|
||||
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
|
||||
|
||||
return $view;
|
||||
}
|
||||
@@ -189,14 +222,14 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function expenseEntry(array $attributes): string
|
||||
{
|
||||
/** @var AccountTaskerInterface $tasker */
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']);
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
||||
$journals = $collector->getJournals();
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
|
||||
// filter for transfers and withdrawals TO the given $account
|
||||
@@ -225,14 +258,14 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function incomeEntry(array $attributes): string
|
||||
{
|
||||
/** @var AccountTaskerInterface $tasker */
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
||||
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']);
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
||||
$journals = $collector->getJournals();
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
|
||||
// filter the set so the destinations outside of $attributes['accounts'] are not included.
|
||||
$journals = $journals->filter(
|
||||
|
@@ -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) {
|
||||
|
@@ -13,7 +13,6 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Events\UserIsDeleted;
|
||||
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
|
||||
use FireflyIII\Http\Requests\ProfileFormRequest;
|
||||
use FireflyIII\User;
|
||||
@@ -35,6 +34,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 +51,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 +63,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 +76,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 +126,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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
60
app/Http/Controllers/Report/BalanceController.php
Normal file
60
app/Http/Controllers/Report/BalanceController.php
Normal 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;
|
||||
}
|
||||
}
|
120
app/Http/Controllers/Report/BudgetController.php
Normal file
120
app/Http/Controllers/Report/BudgetController.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?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 mixed|string
|
||||
*/
|
||||
public function budgetMultiYear(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget-mult-year-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
|
||||
$years = $helper->listOfYears($start, $end);
|
||||
$budgetMultiYear = $helper->getBudgetMultiYear($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.budget-multi-year', compact('budgetMultiYear', 'years'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
}
|
||||
|
||||
}
|
59
app/Http/Controllers/Report/CategoryController.php
Normal file
59
app/Http/Controllers/Report/CategoryController.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
119
app/Http/Controllers/Report/InOutController.php
Normal file
119
app/Http/Controllers/Report/InOutController.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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 expenseReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('expense-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$expenses = $helper->getExpenseReport($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.expenses', compact('expenses'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function incExpReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('inc-exp-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$incomes = $helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $helper->getExpenseReport($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function incomeReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('income-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$incomes = $helper->getIncomeReport($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.income', compact('incomes'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -15,17 +15,12 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Report\BalanceReportHelperInterface;
|
||||
use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
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;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Session;
|
||||
@@ -39,12 +34,6 @@ use View;
|
||||
*/
|
||||
class ReportController extends Controller
|
||||
{
|
||||
|
||||
/** @var BalanceReportHelperInterface */
|
||||
protected $balanceHelper;
|
||||
|
||||
/** @var BudgetReportHelperInterface */
|
||||
protected $budgetHelper;
|
||||
/** @var ReportHelperInterface */
|
||||
protected $helper;
|
||||
|
||||
@@ -55,8 +44,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 +65,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 +96,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,10 +150,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 = [];
|
||||
$dayBefore = clone $start;
|
||||
$dayBefore->subDay();
|
||||
@@ -165,9 +158,11 @@ class ReportController extends Controller
|
||||
// balance the day before:
|
||||
$id = $account->id;
|
||||
$dayBeforeBalance = Steam::balance($account, $dayBefore);
|
||||
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
|
||||
$journals = $journals->reverse();
|
||||
$startBalance = $dayBeforeBalance;
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end);
|
||||
$journals = $collector->getJournals();
|
||||
$journals = $journals->reverse();
|
||||
$startBalance = $dayBeforeBalance;
|
||||
|
||||
|
||||
/** @var Transaction $journal */
|
||||
@@ -208,16 +203,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 +213,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 +223,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'
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -269,15 +242,9 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// need all budgets
|
||||
// need all years.
|
||||
|
||||
$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:
|
||||
$accountIds = [];
|
||||
@@ -290,9 +257,7 @@ class ReportController extends Controller
|
||||
return view(
|
||||
'reports.default.multi-year',
|
||||
compact(
|
||||
'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType',
|
||||
'incomes', 'expenses',
|
||||
'incomeTopLength', 'expenseTopLength', 'tags'
|
||||
'accounts', 'start', 'end', 'accountIds', 'reportType'
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -307,14 +272,6 @@ 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);
|
||||
|
||||
Session::flash('gaEventCategory', 'report');
|
||||
Session::flash('gaEventAction', 'year');
|
||||
Session::flash('gaEventLabel', $start->format('Y'));
|
||||
@@ -330,8 +287,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'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -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]));
|
||||
@@ -313,7 +306,7 @@ class RuleController extends Controller
|
||||
}
|
||||
|
||||
// Return json response
|
||||
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
|
||||
$view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render();
|
||||
|
||||
return Response::json(['html' => $view, 'warning' => $warning]);
|
||||
}
|
||||
@@ -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'),
|
||||
];
|
||||
|
@@ -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])));
|
||||
|
@@ -14,7 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Input;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class SearchController
|
||||
@@ -29,6 +29,7 @@ class SearchController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,16 +39,21 @@ class SearchController extends Controller
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function index(SearchInterface $searcher)
|
||||
public function index(Request $request, SearchInterface $searcher)
|
||||
{
|
||||
|
||||
$minSearchLen = 1;
|
||||
$subTitle = null;
|
||||
$query = null;
|
||||
$result = [];
|
||||
$title = trans('firefly.search');
|
||||
$limit = 20;
|
||||
$mainTitleIcon = 'fa-search';
|
||||
if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) {
|
||||
$query = trim(Input::get('q'));
|
||||
|
||||
// set limit for search:
|
||||
$searcher->setLimit($limit);
|
||||
|
||||
if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) {
|
||||
$query = trim(strtolower($request->get('q')));
|
||||
$words = explode(' ', $query);
|
||||
$subTitle = trans('firefly.search_results_for', ['query' => $query]);
|
||||
|
||||
@@ -60,7 +66,7 @@ class SearchController extends Controller
|
||||
|
||||
}
|
||||
|
||||
return view('search.index', compact('title', 'subTitle', 'mainTitleIcon', 'query', 'result'));
|
||||
return view('search.index', compact('title', 'subTitle', 'limit', 'mainTitleIcon', 'query', 'result'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -13,10 +13,11 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\TagFormRequest;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
@@ -50,15 +51,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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,19 +227,27 @@ class TagController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
* @param TagRepositoryInterface $repository
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function show(Tag $tag, TagRepositoryInterface $repository)
|
||||
public function show(Tag $tag)
|
||||
{
|
||||
$subTitle = $tag->tag;
|
||||
$subTitleIcon = 'fa-tag';
|
||||
$journals = $repository->getJournals($tag);
|
||||
$sum = $journals->sum(
|
||||
function (TransactionJournal $journal) {
|
||||
return TransactionJournal::amount($journal);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// use collector:
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('tags/show/' . $tag->id);
|
||||
|
||||
$sum = $journals->sum(
|
||||
function (Transaction $transaction) {
|
||||
return $transaction->transaction_amount;
|
||||
}
|
||||
);
|
||||
|
||||
|
240
app/Http/Controllers/Transaction/ConvertController.php
Normal file
240
app/Http/Controllers/Transaction/ConvertController.php
Normal file
@@ -0,0 +1,240 @@
|
||||
<?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;
|
||||
$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;
|
||||
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user