Merge branch 'release/4.7.4'

This commit is contained in:
James Cole 2018-06-03 09:16:56 +02:00
commit 78a5dae2a0
767 changed files with 30947 additions and 16214 deletions

View File

@ -95,4 +95,5 @@ DEMO_PASSWORD=
IS_DOCKER=true
IS_SANDSTORM=false
IS_HEROKU=false
BUNQ_USE_SANDBOX=false
TZ=${TZ}

View File

@ -98,4 +98,5 @@ DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=false
BUNQ_USE_SANDBOX=false
IS_HEROKU=false

View File

@ -98,4 +98,5 @@ DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=false
BUNQ_USE_SANDBOX=false
IS_HEROKU=true

View File

@ -98,4 +98,5 @@ DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=true
BUNQ_USE_SANDBOX=false
IS_HEROKU=false

View File

@ -94,4 +94,5 @@ DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=false
BUNQ_USE_SANDBOX=true
IS_HEROKU=false

22
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@ -0,0 +1,22 @@
---
name: Bug report
about: Create a report to help Firefly III improve
---
**Bug description**
I am running Firefly III version x.x.x
(please give a clear and concise description of what the bug is)
**Steps to reproduce**
What do you need to do to trigger this bug?
**Extra info**
Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom).
**Bonus points**
Earn bonus points by:
- Post a stacktrace from your log files
- Add a screenshot

25
.github/ISSUE_TEMPLATE/Custom.md vendored Normal file
View File

@ -0,0 +1,25 @@
---
name: I have a question or a problem
about: Ask away
---
I am running Firefly III version x.x.x
**Description of my issue:**
**Steps to reproduce**
(please include if this problem also exists on the demo site: https://demo.firefly-iii.org/ )
**Extra info**
Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom).
**Bonus points**
Earn bonus points by:
- Post a stacktrace from your log files
- Add a screenshot
- Post nginx or Apache configuration

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea or feature for Firefly III
---
**Description**
Please describe your feature request:
- I would like Firefly III to do X.
- What if you would add Y?
**Solution**
Describe what your feature would add to Firefly III.
**What are alternatives?**
Please describe what alternatives currently exist.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,11 +0,0 @@
I am running Firefly III version x.x.x
#### Description of my issue:
#### Steps to reproduce
(please include if this problem also exists on the demo site)
#### Other important details (log files, system info):
Please click the version number in the right corner of any Firefly III page to get debug information.

View File

@ -1,7 +1,31 @@
# 4.7.4
- [Issue 1409](https://github.com/firefly-iii/firefly-iii/issues/1409), add Indian Rupee and explain that users can do this themselves [issue 1413](https://github.com/firefly-iii/firefly-iii/issues/1413)
- [Issue 1445](https://github.com/firefly-iii/firefly-iii/issues/1445), upgrade Curl in Docker image.
- [Issue 1386](https://github.com/firefly-iii/firefly-iii/issues/1386), quick links to often used pages.
- [Issue 1405](https://github.com/firefly-iii/firefly-iii/issues/1405), show proposed amount to piggy banks.
- [Issue 1416](https://github.com/firefly-iii/firefly-iii/issues/1416), ability to delete lost attachments.
- A completely rewritten import routine that can handle bunq (thanks everybody for testing!), CSV files and Spectre. Please make sure you read about this at http://bit.ly/FF3-new-import
- [Issue 1392](https://github.com/firefly-iii/firefly-iii/issues/1392), explicitly mention rules are inactive (when they are).
- [Issue 1406](https://github.com/firefly-iii/firefly-iii/issues/1406), bill conversion to rules will be smarter about the rules they create.
- [Issue 1369](https://github.com/firefly-iii/firefly-iii/issues/1369), you can now properly order piggy banks again.
- [Issue 1389](https://github.com/firefly-iii/firefly-iii/issues/1389), null-pointer in the import routine.
- [Issue 1400](https://github.com/firefly-iii/firefly-iii/issues/1400), missing translation.
- [Issue 1403](https://github.com/firefly-iii/firefly-iii/issues/1403), bill would always be marked as inactive in edit screen.
- [Issue 1418](https://github.com/firefly-iii/firefly-iii/issues/1418), missing note text on bill page.
- Export routine would break when encountering un-decryptable files.
- [Issue 1425](https://github.com/firefly-iii/firefly-iii/issues/1425), empty fields when edit multiple transactions at once.
- [Issue 1449](https://github.com/firefly-iii/firefly-iii/issues/1449), bad calculations in "budget left to spend" view.
- [Issue 1451](https://github.com/firefly-iii/firefly-iii/issues/1451), same but in another view.
- [Issue 1453](https://github.com/firefly-iii/firefly-iii/issues/1453), same as [issue 1403](https://github.com/firefly-iii/firefly-iii/issues/1403).
- [Issue 1455](https://github.com/firefly-iii/firefly-iii/issues/1455), could add income to a budget.
- [Issue 1442](https://github.com/firefly-iii/firefly-iii/issues/1442), issues with editing a split deposit.
- [Issue 1452](https://github.com/firefly-iii/firefly-iii/issues/1452), date range problems with tags.
- [Issue 1458](https://github.com/firefly-iii/firefly-iii/issues/1458), same for transactions.
- [Issue 1415](https://github.com/firefly-iii/firefly-iii/issues/1415), will email you when OAuth2 keys are generated.
# 4.7.3.2
- Forgot to increase the version number :(.
# 4.7.3.1
- Fixed a critical bug where the rules-engine would fire inadvertently.

View File

@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = (
manifest = (
appTitle = (defaultText = "Firefly III"),
appVersion = 12,
appMarketingVersion = (defaultText = "4.7.3.2"),
appVersion = 13,
appMarketingVersion = (defaultText = "4.7.4"),
actions = [
# Define your "new document" handlers here.

View File

@ -3,6 +3,8 @@ FROM php:7.1-apache
# set working dir
ENV FIREFLY_PATH /var/www/firefly-iii
ENV CURL_VERSION 7.60.0
ENV OPENSSL_VERSION 1.1.1-pre6
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
@ -11,7 +13,8 @@ RUN apt-get update -y && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev \
zlib1g-dev \
libjpeg62-turbo-dev \
libpng12-dev \
wget \
libpng-dev \
libicu-dev \
libedit-dev \
libtidy-dev \
@ -24,6 +27,26 @@ RUN apt-get update -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Setup the Composer installer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Install latest curl
RUN cd /tmp && \
wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz && \
tar -xvf openssl-${OPENSSL_VERSION}.tar.gz && \
cd openssl-${OPENSSL_VERSION} && \
./config && \
make && \
make install
RUN cd /tmp && \
wget https://curl.haxx.se/download/curl-${CURL_VERSION}.tar.gz && \
tar -xvf curl-${CURL_VERSION}.tar.gz && \
cd curl-${CURL_VERSION} && \
./configure --with-ssl && \
make && \
make install
# Install PHP exentions.
RUN docker-php-ext-install -j$(nproc) curl gd intl json readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2 pdo_pgsql
@ -42,9 +65,6 @@ RUN a2enmod ssl
# Create volumes for several directories:
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Setup the Composer installer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
@ -58,4 +78,5 @@ RUN composer install --prefer-dist --no-dev --no-scripts --no-suggest
EXPOSE 80
# Run entrypoint thing
ENTRYPOINT [".deploy/docker/entrypoint.sh"]
ENTRYPOINT [".deploy/docker/entrypoint.sh"]

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* AboutController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* AccountController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* BillController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\BillRequest;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* Controller.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TransactionController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* UserController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* AccountRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* BillRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;

View File

@ -80,4 +80,4 @@ class CurrencyRequest extends Request
return $rules;
}
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* Request.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Http\Requests\Request as FireflyIIIRequest;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TransactionRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* UserRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* CreateExport.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Carbon\Carbon;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* CreateImport.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,15 +20,18 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Import\Storage\ImportArrayStorage;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Services\Internal\File\EncryptService;
use Illuminate\Console\Command;
use Illuminate\Support\MessageBag;
use Log;
use Preferences;
@ -52,18 +55,17 @@ class CreateImport extends Command
*/
protected $signature
= 'firefly:create-import
{file : The file to import.}
{configuration : The configuration file to use for the import.}
{file? : The file to import.}
{configuration? : The configuration file to use for the import.}
{--type=csv : The file type of the import.}
{--user= : The user ID that the import should import for.}
{--provider=file : The file type of the import.}
{--user=1 : The user ID that the import should import for.}
{--token= : The user\'s access token.}
{--start : Starts the job immediately.}';
/**
* Run the command.
*
* @noinspection MultipleReturnStatementsInspection
*
* @throws FireflyException
*/
public function handle(): int
@ -74,77 +76,155 @@ class CreateImport extends Command
return 1;
}
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = $this->argument('file');
$configuration = $this->argument('configuration');
$user = $userRepository->findNull((int)$this->option('user'));
$cwd = getcwd();
$type = strtolower($this->option('type'));
$userRepository = app(UserRepositoryInterface::class);
$file = (string)$this->argument('file');
$configuration = (string)$this->argument('configuration');
$user = $userRepository->findNull((int)$this->option('user'));
$cwd = getcwd();
$type = strtolower((string)$this->option('type'));
$provider = strtolower((string)$this->option('provider'));
$configurationData = [];
if (!$this->validArguments()) {
$this->errorLine('Invalid arguments.');
return 1;
}
if (\strlen($configuration) > 0) {
$configurationData = json_decode(file_get_contents($configuration), true);
if (null === $configurationData) {
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
$configurationData = json_decode(file_get_contents($configuration), true);
if (null === $configurationData) {
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return 1;
return 1;
}
}
$this->infoLine(sprintf('Going to create a job to import file: %s', $file));
$this->infoLine(sprintf('Using configuration file: %s', $configuration));
$this->infoLine(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
$this->infoLine(sprintf('Type of import: %s', $type));
$this->infoLine(sprintf('Type of import: %s', $provider));
/** @var ImportJobRepositoryInterface $jobRepository */
$jobRepository = app(ImportJobRepositoryInterface::class);
$jobRepository->setUser($user);
$job = $jobRepository->create($type);
$this->infoLine(sprintf('Created job "%s"', $job->key));
$importJob = $jobRepository->create($provider);
$this->infoLine(sprintf('Created job "%s"', $importJob->key));
/** @var EncryptService $service */
$service = app(EncryptService::class);
$service->encrypt($file, $job->key);
$this->infoLine('Stored import data...');
$jobRepository->setConfiguration($job, $configurationData);
$jobRepository->updateStatus($job, 'configured');
$this->infoLine('Stored configuration...');
if (true === $this->option('start')) {
$this->infoLine('The import will start in a moment. This process is not visible...');
Log::debug('Go for import!');
// normally would refer to other firefly:start-import but that doesn't seem to work all to well...
// start the actual routine:
$type = 'csv' === $job->file_type ? 'file' : $job->file_type;
$key = sprintf('import.routine.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
// make sure that job has no prerequisites.
if ((bool)config(sprintf('import.has_prereq.%s', $provider))) {
// make prerequisites thing.
$class = (string)config(sprintf('import.prerequisites.%s', $provider));
if (!class_exists($class)) {
throw new FireflyException(sprintf('No class to handle prerequisites for "%s".', $provider)); // @codeCoverageIgnore
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setJob($job);
$routine->run();
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser($user);
if (!$object->isComplete()) {
$this->errorLine(sprintf('Import provider "%s" has prerequisites that can only be filled in using the browser.', $provider));
// give feedback.
/** @var MessageBag $error */
foreach ($routine->getErrors() as $index => $error) {
$this->errorLine(sprintf('Error importing line #%d: %s', $index, $error));
return 1;
}
$this->infoLine(
sprintf(
'The import has finished. %d transactions have been imported out of %d records.', $routine->getJournals()->count(), $routine->getLines()
)
);
}
// store file as attachment.
if (\strlen($file) > 0) {
$messages = $jobRepository->storeCLIUpload($importJob, 'import_file', $file);
if ($messages->count() > 0) {
$this->errorLine($messages->first());
return 1;
}
$this->infoLine('File content saved.');
}
$this->infoLine('Job configuration saved.');
$jobRepository->setConfiguration($importJob, $configurationData);
$jobRepository->setStatus($importJob, 'ready_to_run');
if (true === $this->option('start')) {
$this->infoLine('The has started. The process is not visible. Please wait.');
Log::debug('Go for import!');
// run it!
$key = sprintf('import.routine.%s', $provider);
$className = config($key);
if (null === $className || !class_exists($className)) {
// @codeCoverageIgnoreStart
$this->errorLine(sprintf('No routine for provider "%s"', $provider));
return 1;
// @codeCoverageIgnoreEnd
}
// keep repeating this call until job lands on "provider_finished"
$valid = ['provider_finished'];
$count = 0;
while (!\in_array($importJob->status, $valid, true) && $count < 6) {
Log::debug(sprintf('Now in loop #%d.', $count + 1));
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setImportJob($importJob);
try {
$routine->run();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$jobRepository->setStatus($importJob, 'error');
$this->errorLine($message);
return 1;
}
$count++;
}
if ($importJob->status === 'provider_finished') {
$this->infoLine('Import has finished. Please wait for storage of data.');
// set job to be storing data:
$jobRepository->setStatus($importJob, 'storing_data');
/** @var ImportArrayStorage $storage */
$storage = app(ImportArrayStorage::class);
$storage->setImportJob($importJob);
try {
$storage->store();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$jobRepository->setStatus($importJob, 'error');
$this->errorLine($message);
return 1;
}
// set storage to be finished:
$jobRepository->setStatus($importJob, 'storage_finished');
}
// give feedback:
$this->infoLine('Job has finished.');
if (null !== $importJob->tag) {
$this->infoLine(sprintf('%d transaction(s) have been imported.', $importJob->tag->transactionJournals->count()));
$this->infoLine(sprintf('You can find your transactions under tag "%s"', $importJob->tag->tag));
}
if (null === $importJob->tag) {
$this->errorLine('No transactions have been imported :(.');
}
if (\count($importJob->errors) > 0) {
$this->infoLine(sprintf('%d error(s) occurred:', \count($importJob->errors)));
foreach ($importJob->errors as $err) {
$this->errorLine('- ' . $err);
}
}
}
// clear cache for user:
Preferences::setForUser($user, 'lastActivity', microtime());
@ -185,20 +265,28 @@ class CreateImport extends Command
$cwd = getcwd();
$validTypes = config('import.options.file.import_formats');
$type = strtolower($this->option('type'));
$provider = strtolower($this->option('provider'));
$enabled = (bool)config(sprintf('import.enabled.%s', $provider));
if (!\in_array($type, $validTypes, true)) {
if (false === $enabled) {
$this->errorLine(sprintf('Provider "%s" is not enabled.', $provider));
return false;
}
if ($provider === 'file' && !\in_array($type, $validTypes, true)) {
$this->errorLine(sprintf('Cannot import file of type "%s"', $type));
return false;
}
if (!file_exists($file)) {
if ($provider === 'file' && !file_exists($file)) {
$this->errorLine(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd));
return false;
}
if (!file_exists($configuration)) {
if ($provider === 'file' && !file_exists($configuration)) {
$this->errorLine(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return false;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* DecryptAttachment.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* EncryptFile.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Exceptions\FireflyException;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* Import.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Exceptions\FireflyException;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* ScanAttachments.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Crypt;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* UpgradeDatabase.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use DB;
@ -159,28 +161,41 @@ class UpgradeDatabase extends Command
'order' => 2,
]
);
// add triggers for amounts:
RuleTrigger::create(
[
'rule_id' => $rule->id,
'trigger_type' => 'amount_less',
'trigger_value' => round($bill->amount_max, $currency->decimal_places),
'active' => 1,
'stop_processing' => 0,
'order' => 3,
]
);
RuleTrigger::create(
[
'rule_id' => $rule->id,
'trigger_type' => 'amount_more',
'trigger_value' => round($bill->amount_min, $currency->decimal_places),
'active' => 1,
'stop_processing' => 0,
'order' => 4,
]
);
if ($bill->amount_max !== $bill->amount_min) {
// add triggers for amounts:
RuleTrigger::create(
[
'rule_id' => $rule->id,
'trigger_type' => 'amount_less',
'trigger_value' => round($bill->amount_max, $currency->decimal_places),
'active' => 1,
'stop_processing' => 0,
'order' => 3,
]
);
RuleTrigger::create(
[
'rule_id' => $rule->id,
'trigger_type' => 'amount_more',
'trigger_value' => round($bill->amount_min, $currency->decimal_places),
'active' => 1,
'stop_processing' => 0,
'order' => 4,
]
);
}
if($bill->amount_max === $bill->amount_min) {
RuleTrigger::create(
[
'rule_id' => $rule->id,
'trigger_type' => 'amount_exactly',
'trigger_value' => round($bill->amount_min, $currency->decimal_places),
'active' => 1,
'stop_processing' => 0,
'order' => 3,
]
);
}
// create action
RuleAction::create(
@ -600,12 +615,16 @@ class UpgradeDatabase extends Command
$opposing->transaction_currency_id = $currency->id;
$transaction->save();
$opposing->save();
Log::debug(sprintf('Currency for account "%s" is %s, and currency for account "%s" is also
Log::debug(
sprintf(
'Currency for account "%s" is %s, and currency for account "%s" is also
%s, so %s #%d (#%d and #%d) has been verified to be to %s exclusively.',
$opposing->account->name, $opposingCurrency->code,
$transaction->account->name, $transaction->transactionCurrency->code,
$journal->transactionType->type, $journal->id,
$transaction->id, $opposing->id, $currency->code));
$opposing->account->name, $opposingCurrency->code,
$transaction->account->name, $transaction->transactionCurrency->code,
$journal->transactionType->type, $journal->id,
$transaction->id, $opposing->id, $currency->code
)
);
return;
}

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* UpgradeFireflyInstructions.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command;

View File

@ -1,9 +1,8 @@
<?php
declare(strict_types=1);
/**
* UseEncryption.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -21,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* VerifiesAccessToken.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Repositories\User\UserRepositoryInterface;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* VerifyDatabase.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Crypt;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* Kernel.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console;
use Illuminate\Console\Scheduling\Schedule;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* AdminRequestedTestMessage.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\User;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* Event.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
/**

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* RegisteredUser.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\User;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* RequestedNewPassword.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\User;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* RequestedVersionCheckStatus.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* StoredTransactionJournal.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\Models\TransactionJournal;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* UpdatedTransactionJournal.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\Models\TransactionJournal;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* UserChangedEmail.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\User;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* FireflyException.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Exceptions;
use Exception;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* Handler.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Exceptions;
use ErrorException;
@ -28,6 +30,7 @@ use FireflyIII\Jobs\MailError;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -79,6 +82,11 @@ class Handler extends ExceptionHandler
return response()->json(['message' => 'Unauthenticated', 'exception' => 'AuthenticationException'], 401);
}
if ($exception instanceof OAuthServerException && $request->expectsJson()) {
// somehow Laravel handler does not catch this:
return response()->json(['message' => $exception->getMessage(), 'exception' => 'OAuthServerException'], 401);
}
if ($request->expectsJson()) {
$isDebug = config('app.debug', false);
if ($isDebug) {
@ -96,7 +104,7 @@ class Handler extends ExceptionHandler
return response()->json(['message' => 'Internal Firefly III Exception. See log files.', 'exception' => \get_class($exception)], 500);
}
if ($exception instanceof FireflyException || $exception instanceof ErrorException) {
if ($exception instanceof FireflyException || $exception instanceof ErrorException || $exception instanceof OAuthServerException) {
$isDebug = env('APP_DEBUG', false);
return response()->view('errors.FireflyException', ['exception' => $exception, 'debug' => $isDebug], 500);
@ -120,8 +128,25 @@ class Handler extends ExceptionHandler
*/
public function report(Exception $exception)
{
$doMailError = env('SEND_ERROR_MESSAGE', true);
if (($exception instanceof FireflyException || $exception instanceof ErrorException) && $doMailError) {
if (
// if the user wants us to mail:
$doMailError === true &&
((
// and if is one of these error instances
$exception instanceof FireflyException
|| $exception instanceof ErrorException
|| $exception instanceof OAuthServerException
)
|| (
// or this one, but it's a JSON exception.
$exception instanceof AuthenticationException
&& Request::expectsJson() === true
))
) {
// then, send email
$userData = [
'id' => 0,
'email' => 'unknown@example.com',
@ -139,6 +164,9 @@ class Handler extends ExceptionHandler
'line' => $exception->getLine(),
'code' => $exception->getCode(),
'version' => config('firefly.version'),
'url' => Request::fullUrl(),
'userAgent' => Request::userAgent(),
'json' => Request::acceptsJson(),
];
// create job that will mail.

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* NotImplementedException.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Exceptions;
/**

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* ValidationException.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Exceptions;
/**

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* AttachmentCollector.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Collector;
use Carbon\Carbon;
@ -94,7 +96,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
*/
private function exportAttachment(Attachment $attachment): bool
{
$file = $attachment->fileName();
$file = $attachment->fileName();
$decrypted = false;
if ($this->uploadDisk->exists($file)) {
try {
$decrypted = Crypt::decrypt($this->uploadDisk->get($file));
@ -104,6 +107,9 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
return false;
}
}
if ($decrypted === false) {
return false;
}
$exportFile = $this->exportFileName($attachment);
$this->exportDisk->put($exportFile, $decrypted);
$this->getEntries()->push($exportFile);

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* BasicCollector.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Collector;
use FireflyIII\Models\ExportJob;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* CollectorInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Collector;
use FireflyIII\Models\ExportJob;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* UploadCollector.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Collector;
use Crypt;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* Entry.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Transaction;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* ExpandedProcessor.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export;
use Crypt;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* BasicExporter.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Exporter;
use FireflyIII\Models\ExportJob;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* CsvExporter.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Exporter;
use FireflyIII\Export\Entry\Entry;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* ExporterInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export\Exporter;
use FireflyIII\Models\ExportJob;

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* ProcessorInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Export;
use Illuminate\Support\Collection;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* AccountFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* AccountMetaFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* BillFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* BudgetFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,8 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* CategoryFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* PiggyBankEventFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* PiggyBankFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TagFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TransactionCurrencyFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;
@ -68,7 +69,8 @@ class TransactionCurrencyFactory
$currencyCode = (string)$currencyCode;
$currencyId = (int)$currencyId;
if (\strlen($currencyCode) === 0 && (int)$currencyId === 0) {
if ('' === $currencyCode && $currencyId === 0) {
Log::warning('Cannot find anything on empty currency code and empty currency ID!');
return null;
}
@ -78,6 +80,7 @@ class TransactionCurrencyFactory
if (null !== $currency) {
return $currency;
}
Log::warning(sprintf('Currency ID is %d but found nothing!', $currencyId));
}
// then by code:
if (\strlen($currencyCode) > 0) {
@ -85,7 +88,9 @@ class TransactionCurrencyFactory
if (null !== $currency) {
return $currency;
}
Log::warning(sprintf('Currency code is %d but found nothing!', $currencyCode));
}
Log::warning('Found nothing for currency.');
return null;
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TransactionFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,16 +20,19 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Services\Internal\Support\TransactionServiceTrait;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
/**
* Class TransactionFactory
@ -45,10 +48,21 @@ class TransactionFactory
* @param array $data
*
* @return Transaction
* @throws FireflyException
*/
public function create(array $data): Transaction
public function create(array $data): ?Transaction
{
$currencyId = isset($data['currency']) ? $data['currency']->id : $data['currency_id'];
Log::debug('Start of TransactionFactory::create()');
$currencyId = $data['currency_id'] ?? null;
$currencyId = isset($data['currency']) ? $data['currency']->id : $currencyId;
if ('' === $data['amount']) {
throw new FireflyException('Amount is an empty string, which Firefly III cannot handle. Apologies.'); // @codeCoverageIgnore
}
if (null === $currencyId) {
throw new FireflyException('Cannot store transaction without currency information.'); // @codeCoverageIgnore
}
$data['foreign_amount'] = '' === (string)$data['foreign_amount'] ? null : $data['foreign_amount'];
Log::debug(sprintf('Create transaction for account #%d ("%s") with amount %s', $data['account']->id, $data['account']->name, $data['amount']));
return Transaction::create(
[
@ -72,19 +86,23 @@ class TransactionFactory
* @param array $data
*
* @return Collection
* @throws FireflyException
*/
public function createPair(TransactionJournal $journal, array $data): Collection
{
Log::debug('Start of TransactionFactory::createPair()');
// all this data is the same for both transactions:
$currency = $this->findCurrency($data['currency_id'], $data['currency_code']);
$description = $journal->description === $data['description'] ? null : $data['description'];
// type of source account depends on journal type:
$sourceType = $this->accountType($journal, 'source');
Log::debug(sprintf('Expect source account to be of type %s', $sourceType));
$sourceAccount = $this->findAccount($sourceType, $data['source_id'], $data['source_name']);
// same for destination account:
$destinationType = $this->accountType($journal, 'destination');
Log::debug(sprintf('Expect source destination to be of type %s', $destinationType));
$destinationAccount = $this->findAccount($destinationType, $data['destination_id'], $data['destination_name']);
// first make a "negative" (source) transaction based on the data in the array.
$source = $this->create(
@ -125,7 +143,7 @@ class TransactionFactory
}
// set budget:
if ($journal->transactionType->type === TransactionType::TRANSFER) {
if ($journal->transactionType->type !== TransactionType::WITHDRAWAL) {
$data['budget_id'] = null;
$data['budget_name'] = null;
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TransactionJournalFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;
@ -51,6 +52,7 @@ class TransactionJournalFactory
// store basic journal first.
$type = $this->findTransactionType($data['type']);
$defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user);
Log::debug(sprintf('Going to store a %s', $type->type));
$journal = TransactionJournal::create(
[
'user_id' => $data['user'],
@ -70,8 +72,10 @@ class TransactionJournalFactory
$factory = app(TransactionFactory::class);
$factory->setUser($this->user);
Log::debug(sprintf('Found %d transactions in array.', \count($data['transactions'])));
/** @var array $trData */
foreach ($data['transactions'] as $trData) {
foreach ($data['transactions'] as $index => $trData) {
Log::debug(sprintf('Now storing transaction %d of %d', $index + 1, \count($data['transactions'])));
$factory->createPair($journal, $trData);
}
$journal->completed = true;
@ -91,7 +95,7 @@ class TransactionJournalFactory
// store date meta fields (if present):
$fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date',
'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id','importHash'];
'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id', 'importHash','importHashV2', 'external_id'];
foreach ($fields as $field) {
$this->storeMeta($journal, $data, $field);

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TransactionJournalMetaFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;
@ -70,6 +71,7 @@ class TransactionJournalMetaFactory
}
if (null === $entry) {
Log::debug(sprintf('Going to create new meta-data entry to store "%s".', $data['name']));
$entry = new TransactionJournalMeta();
$entry->transactionJournal()->associate($data['journal']);
$entry->name = $data['name'];

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TransactionTypeFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@ -20,6 +20,7 @@ declare(strict_types=1);
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Factory;

View File

@ -0,0 +1,80 @@
<?php
/**
* APIEventHandler.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Handlers\Events;
use Exception;
use FireflyIII\Mail\AccessTokenCreatedMail;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Laravel\Passport\Events\AccessTokenCreated;
use Laravel\Passport\Token;
use Log;
use Mail;
use Request;
use Session;
/**
* Class APIEventHandler
*/
class APIEventHandler
{
/**
* @param AccessTokenCreated $event
*
* @return bool
*/
public function accessTokenCreated(AccessTokenCreated $event): bool
{
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
$user = $repository->findNull((int)$event->userId);
if (null === $user) {
Log::error('Access Token generated but no user associated.');
return true;
}
$email = $user->email;
$ipAddress = Request::ip();
Log::debug(sprintf('Now in APIEventHandler::accessTokenCreated. Email is %s, IP is %s', $email, $ipAddress));
try {
Log::debug('Trying to send message...');
Mail::to($email)->send(new AccessTokenCreatedMail($email, $ipAddress));
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::debug('Send message failed! :(');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
Session::flash('error', 'Possible email error: ' . $e->getMessage());
}
Log::debug('If no error above this line, message was sent.');
// @codeCoverageIgnoreEnd
return true;
}
}

View File

@ -24,6 +24,8 @@ namespace FireflyIII\Helpers\Attachments;
use Crypt;
use FireflyIII\Models\Attachment;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
@ -64,6 +66,26 @@ class AttachmentHelper implements AttachmentHelperInterface
$this->uploadDisk = Storage::disk('upload');
}
/**
* @codeCoverageIgnore
*
* @param Attachment $attachment
*
* @return string
*/
public function getAttachmentContent(Attachment $attachment): string
{
try {
$content = Crypt::decrypt($this->uploadDisk->get(sprintf('at-%d.data', $attachment->id)));
} catch (DecryptException|FileNotFoundException $e) {
Log::error(sprintf('Could not decrypt data of attachment #%d', $attachment->id));
return '';
}
return $content;
}
/**
* @param Attachment $attachment
*

View File

@ -39,6 +39,13 @@ interface AttachmentHelperInterface
*/
public function getAttachmentLocation(Attachment $attachment): string;
/**
* @param Attachment $attachment
*
* @return string
*/
public function getAttachmentContent(Attachment $attachment): string;
/**
* @return Collection
*/

View File

@ -32,6 +32,7 @@ use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
use FireflyIII\Helpers\Filter\SplitIndicatorFilter;
use FireflyIII\Helpers\Filter\TransactionViewFilter;
use FireflyIII\Helpers\Filter\TransferFilter;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
@ -60,6 +61,7 @@ use Steam;
*/
class JournalCollector implements JournalCollectorInterface
{
/** @var array */
private $accountIds = [];
/** @var int */
@ -106,7 +108,8 @@ class JournalCollector implements JournalCollectorInterface
];
/** @var array */
private $filters = [InternalTransferFilter::class];
/** @var bool */
private $ignoreCache = false;
/** @var bool */
private $joinedBudget = false;
/** @var bool */
@ -257,12 +260,15 @@ class JournalCollector implements JournalCollectorInterface
foreach ($this->filters as $filter) {
$cache->addProperty((string)$filter);
}
if ($cache->has()) {
if (false === $this->ignoreCache && $cache->has()) {
Log::debug(sprintf('Return cache of query with ID "%s".', $key));
return $cache->get(); // @codeCoverageIgnore
}
}
if (true === $this->ignoreCache) {
Log::debug('Ignore cache in journal collector.');
}
/** @var Collection $set */
$set = $this->query->get(array_values($this->fields));
@ -315,6 +321,16 @@ class JournalCollector implements JournalCollectorInterface
return $journals;
}
/**
* @return JournalCollectorInterface
*/
public function ignoreCache(): JournalCollectorInterface
{
$this->ignoreCache = true;
return $this;
}
/**
* @param string $filter
*
@ -768,6 +784,7 @@ class JournalCollector implements JournalCollectorInterface
NegativeAmountFilter::class => new NegativeAmountFilter,
SplitIndicatorFilter::class => new SplitIndicatorFilter,
CountAttachmentsFilter::class => new CountAttachmentsFilter,
TransactionViewFilter::class => new TransactionViewFilter,
];
Log::debug(sprintf('Will run %d filters on the set.', \count($this->filters)));
foreach ($this->filters as $enabled) {
@ -784,7 +801,7 @@ class JournalCollector implements JournalCollectorInterface
/**
*
*/
private function joinBudgetTables()
private function joinBudgetTables(): void
{
if (!$this->joinedBudget) {
// join some extra tables:
@ -809,7 +826,7 @@ class JournalCollector implements JournalCollectorInterface
/**
*
*/
private function joinCategoryTables()
private function joinCategoryTables(): void
{
if (!$this->joinedCategory) {
// join some extra tables:
@ -839,7 +856,7 @@ class JournalCollector implements JournalCollectorInterface
/**
*
*/
private function joinOpposingTables()
private function joinOpposingTables(): void
{
if (!$this->joinedOpposing) {
Log::debug('joinedOpposing is false');
@ -871,7 +888,7 @@ class JournalCollector implements JournalCollectorInterface
/**
*
*/
private function joinTagTables()
private function joinTagTables(): void
{
if (!$this->joinedTag) {
// join some extra tables:

View File

@ -78,6 +78,11 @@ interface JournalCollectorInterface
*/
public function getPaginatedJournals(): LengthAwarePaginator;
/**
* @return JournalCollectorInterface
*/
public function ignoreCache(): JournalCollectorInterface;
/**
* @param string $filter
*

View File

@ -0,0 +1,80 @@
<?php
/**
* TransactionViewFilter.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Helpers\Filter;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Log;
/**
* Class TransactionViewFilter.
*
* This filter removes the entry with a negative amount when it's a withdrawal
* And the positive amount when it's a deposit or transfer
*
* This is used in the mass-edit routine.
*
*/
class TransactionViewFilter implements FilterInterface
{
/**
* @param Collection $set
*
* @return Collection
*/
public function filter(Collection $set): Collection
{
return $set->filter(
function (Transaction $transaction) {
// remove if amount is less than zero and type is withdrawal.
if ($transaction->transaction_type_type === TransactionType::WITHDRAWAL && 1 === bccomp($transaction->transaction_amount, '0')) {
Log::debug(
sprintf(
'Filtered #%d because amount is %f and type is %s.', $transaction->id, $transaction->transaction_amount,
$transaction->transaction_type_type
)
);
return null;
}
if ($transaction->transaction_type_type === TransactionType::DEPOSIT && -1 === bccomp($transaction->transaction_amount, '0')) {
Log::debug(
sprintf(
'Filtered #%d because amount is %f and type is %s.', $transaction->id, $transaction->transaction_amount,
$transaction->transaction_type_type
)
);
return null;
}
Log::debug(
sprintf('#%d: amount is %f and type is %s.', $transaction->id, $transaction->transaction_amount, $transaction->transaction_type_type)
);
return $transaction;
}
);
}
}

View File

@ -210,18 +210,7 @@ class AccountController extends Controller
$request->session()->flash('preFilled', $preFilled);
return view(
'accounts.edit',
compact(
'account',
'currency',
'subTitle',
'subTitleIcon',
'what',
'roles',
'preFilled'
)
);
return view('accounts.edit', compact('account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles', 'preFilled'));
}
/**
@ -297,8 +286,10 @@ class AccountController extends Controller
throw new FireflyException('End is after start!'); // @codeCoverageIgnore
}
$what = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type)); // used for menu
$today = new Carbon;
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$currencyId = (int)$this->repository->getMetaValue($account, 'currency_id');
@ -320,7 +311,7 @@ class AccountController extends Controller
return view(
'accounts.show',
compact('account', 'showAll', 'currency', 'today', 'periods', 'subTitleIcon', 'transactions', 'subTitle', 'start', 'end', 'chartUri')
compact('account', 'showAll', 'what', 'currency', 'today', 'periods', 'subTitleIcon', 'transactions', 'subTitle', 'start', 'end', 'chartUri')
);
}
@ -338,7 +329,7 @@ class AccountController extends Controller
public function showAll(Request $request, Account $account)
{
if (AccountType::INITIAL_BALANCE === $account->accountType->type) {
return $this->redirectToOriginalAccount($account);
return $this->redirectToOriginalAccount($account); // @codeCoverageIgnore
}
$end = new Carbon;
$today = new Carbon;

View File

@ -22,14 +22,11 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use ExpandedForm;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Note;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\BillTransformer;
@ -50,6 +47,10 @@ class BillController extends Controller
{
/** @var AttachmentHelperInterface Helper for attachments. */
private $attachments;
/** @var BillRepositoryInterface */
private $billRepository;
/** @var RuleGroupRepositoryInterface */
private $ruleGroupRepos;
/**
*
@ -67,7 +68,9 @@ class BillController extends Controller
function ($request, $next) {
app('view')->share('title', trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->attachments = app(AttachmentHelperInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
$this->billRepository = app(BillRepositoryInterface::class);
$this->ruleGroupRepos = app(RuleGroupRepositoryInterface::class);
return $next($request);
}
@ -75,21 +78,20 @@ class BillController extends Controller
}
/**
* @param Request $request
*
* @param CurrencyRepositoryInterface $repository
* @param Request $request
*
* @return View
*/
public function create(Request $request, CurrencyRepositoryInterface $repository)
public function create(Request $request)
{
$periods = [];
foreach (config('firefly.bill_periods') as $current) {
/** @var array $billPeriods */
$billPeriods = config('firefly.bill_periods');
foreach ($billPeriods as $current) {
$periods[$current] = strtolower((string)trans('firefly.repeat_freq_' . $current));
}
$subTitle = trans('firefly.create_new_bill');
$defaultCurrency = app('amount')->getDefaultCurrency();
$currencies = ExpandedForm::makeSelectList($repository->get());
// put previous url in session if not redirect from store (not "create another").
if (true !== session('bills.create.fromStore')) {
@ -97,7 +99,7 @@ class BillController extends Controller
}
$request->session()->forget('bills.create.fromStore');
return view('bills.create', compact('periods', 'subTitle', 'currencies', 'defaultCurrency'));
return view('bills.create', compact('periods', 'subTitle', 'defaultCurrency'));
}
/**
@ -115,16 +117,15 @@ class BillController extends Controller
}
/**
* @param Request $request
* @param BillRepositoryInterface $repository
* @param Bill $bill
* @param Request $request
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(Request $request, BillRepositoryInterface $repository, Bill $bill)
public function destroy(Request $request, Bill $bill)
{
$name = $bill->name;
$repository->destroy($bill);
$this->billRepository->destroy($bill);
$request->session()->flash('success', (string)trans('firefly.deleted_bill', ['name' => $name]));
Preferences::mark();
@ -133,18 +134,21 @@ class BillController extends Controller
}
/**
* @param Request $request
* @param CurrencyRepositoryInterface $repository
* @param Bill $bill
* @param Request $request
* @param Bill $bill
*
* @return View
*/
public function edit(Request $request, CurrencyRepositoryInterface $repository, Bill $bill)
public function edit(Request $request, Bill $bill)
{
$periods = [];
foreach (config('firefly.bill_periods') as $current) {
/** @var array $billPeriods */
$billPeriods = config('firefly.bill_periods');
foreach ($billPeriods as $current) {
$periods[$current] = trans('firefly.' . $current);
}
$subTitle = trans('firefly.edit_bill', ['name' => $bill->name]);
// put previous url in session if not redirect from store (not "return_to_edit").
@ -156,36 +160,28 @@ class BillController extends Controller
$bill->amount_min = round($bill->amount_min, $currency->decimal_places);
$bill->amount_max = round($bill->amount_max, $currency->decimal_places);
$defaultCurrency = app('amount')->getDefaultCurrency();
$currencies = ExpandedForm::makeSelectList($repository->get());
$preFilled = [
'notes' => '',
'notes' => $this->billRepository->getNoteText($bill),
'transaction_currency_id' => $bill->transaction_currency_id,
'active' => $bill->active,
];
/** @var Note $note */
$note = $bill->notes()->first();
if (null !== $note) {
$preFilled['notes'] = $note->text;
}
$request->session()->flash('preFilled', $preFilled);
$request->session()->forget('bills.edit.fromUpdate');
return view('bills.edit', compact('subTitle', 'periods', 'bill', 'defaultCurrency', 'currencies'));
return view('bills.edit', compact('subTitle', 'periods', 'bill', 'defaultCurrency', 'preFilled'));
}
/**
* @param BillRepositoryInterface $repository
*
* @return View
*/
public function index(BillRepositoryInterface $repository)
public function index()
{
$start = session('start');
$end = session('end');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$paginator = $repository->getPaginator($pageSize);
$paginator = $this->billRepository->getPaginator($pageSize);
$parameters = new ParameterBag();
$parameters->set('start', $start);
$parameters->set('end', $end);
@ -203,7 +199,7 @@ class BillController extends Controller
);
// add info about rules:
$rules = $repository->getRulesForBills($paginator->getCollection());
$rules = $this->billRepository->getRulesForBills($paginator->getCollection());
$bills = $bills->map(
function (array $bill) use ($rules) {
$bill['rules'] = $rules[$bill['id']] ?? [];
@ -218,21 +214,20 @@ class BillController extends Controller
}
/**
* @param Request $request
* @param BillRepositoryInterface $repository
* @param Bill $bill
* @param Request $request
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function rescan(Request $request, BillRepositoryInterface $repository, Bill $bill)
public function rescan(Request $request, Bill $bill)
{
if (0 === (int)$bill->active) {
$request->session()->flash('warning', (string)trans('firefly.cannot_scan_inactive_bill'));
return redirect(URL::previous());
}
$set = $repository->getRulesForBill($bill);
$set = $this->billRepository->getRulesForBill($bill);
$total = 0;
foreach ($set as $rule) {
// simply fire off all rules?
@ -243,7 +238,7 @@ class BillController extends Controller
$matcher->setRule($rule);
$matchingTransactions = $matcher->findTransactionsByRule();
$total += $matchingTransactions->count();
$repository->linkCollectionToBill($bill, $matchingTransactions);
$this->billRepository->linkCollectionToBill($bill, $matchingTransactions);
}
@ -254,24 +249,23 @@ class BillController extends Controller
}
/**
* @param Request $request
* @param BillRepositoryInterface $repository
* @param Bill $bill
* @param Request $request
* @param Bill $bill
*
* @return View
*/
public function show(Request $request, BillRepositoryInterface $repository, Bill $bill)
public function show(Request $request, Bill $bill)
{
// add info about rules:
$rules = $repository->getRulesForBill($bill);
$rules = $this->billRepository->getRulesForBill($bill);
$subTitle = $bill->name;
$start = session('start');
$end = session('end');
$year = $start->year;
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$yearAverage = $repository->getYearAverage($bill, $start);
$overallAverage = $repository->getOverallAverage($bill);
$yearAverage = $this->billRepository->getYearAverage($bill, $start);
$overallAverage = $this->billRepository->getOverallAverage($bill);
$manager = new Manager();
$manager->setSerializer(new DataArraySerializer());
$manager->parseIncludes(['attachments', 'notes']);
@ -296,16 +290,14 @@ class BillController extends Controller
}
/**
* @param BillFormRequest $request
* @param BillRepositoryInterface $repository
* @param BillFormRequest $request
*
* @param RuleGroupRepositoryInterface $ruleGroupRepository
* @return \Illuminate\Http\RedirectResponse
*/
public function store(BillFormRequest $request, BillRepositoryInterface $repository, RuleGroupRepositoryInterface $ruleGroupRepository)
public function store(BillFormRequest $request)
{
$billData = $request->getBillData();
$bill = $repository->store($billData);
$bill = $this->billRepository->store($billData);
if (null === $bill) {
$request->session()->flash('error', (string)trans('firefly.bill_store_error'));
@ -330,16 +322,16 @@ class BillController extends Controller
}
// find first rule group, or create one:
$count = $ruleGroupRepository->count();
$count = $this->ruleGroupRepos->count();
if ($count === 0) {
$data = [
'title' => (string)trans('firefly.rulegroup_for_bills_title'),
'description' => (string)trans('firefly.rulegroup_for_bills_description'),
];
$group = $ruleGroupRepository->store($data);
$group = $this->ruleGroupRepos->store($data);
}
if ($count > 0) {
$group = $ruleGroupRepository->getActiveGroups(auth()->user())->first();
$group = $this->ruleGroupRepos->getActiveGroups(auth()->user())->first();
}
// redirect to page that will create a new rule.
@ -349,16 +341,15 @@ class BillController extends Controller
}
/**
* @param BillFormRequest $request
* @param BillRepositoryInterface $repository
* @param Bill $bill
* @param BillFormRequest $request
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(BillFormRequest $request, BillRepositoryInterface $repository, Bill $bill)
public function update(BillFormRequest $request, Bill $bill)
{
$billData = $request->getBillData();
$bill = $repository->update($bill, $billData);
$bill = $this->billRepository->update($bill, $billData);
$request->session()->flash('success', (string)trans('firefly.updated_bill', ['name' => $bill->name]));
Preferences::mark();

View File

@ -87,6 +87,8 @@ class BudgetController extends Controller
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
$largeDiff = false;
$warnText = '';
$days = 0;
$daysInMonth = 0;
if (0 === bccomp($amount, '0')) {
$budgetLimit = null;
}
@ -94,13 +96,17 @@ class BudgetController extends Controller
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
Log::debug(sprintf('Start is %s, end is %s, today is %s', $start->format('Y-m-d'), $end->format('Y-m-d'),$today->format('Y-m-d')));
if ($today->gte($start) && $today->lte($end)) {
$days = $end->diffInDays($today);
$days = $end->diffInDays($today);
$daysInMonth = $start->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$days = $start->diffInDays($end);
$days = $start->diffInDays($end);
$daysInMonth = $start->diffInDays($end);
}
$days = $days === 0 ? 1 : $days;
$days = $days === 0 ? 1 : $days;
$daysInMonth = $daysInMonth === 0 ? 1 : $daysInMonth;
// calculate left in budget:
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
@ -146,6 +152,7 @@ class BudgetController extends Controller
'large_diff' => $largeDiff,
'left_per_day' => $leftPerDay,
'warn_text' => $warnText,
'daysInMonth' => $daysInMonth,
]
);
@ -229,22 +236,13 @@ class BudgetController extends Controller
*/
public function index(Request $request, string $moment = null)
{
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
if ($today->gte($start) && $today->lte($end)) {
$days = $end->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$days = $start->diffInDays($end);
}
$days = $days === 0 ? 1 : $days;
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$days = 0;
$daysInMonth = 0;
// make date if present:
if (null !== $moment || '' !== (string)$moment) {
@ -256,6 +254,22 @@ class BudgetController extends Controller
Log::debug('start and end are already defined.');
}
}
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
if ($today->gte($start) && $today->lte($end)) {
$days = $end->diffInDays($today);
$daysInMonth = $start->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$days = $start->diffInDays($end);
$daysInMonth = $start->diffInDays($end);
}
$days = $days === 0 ? 1 : $days;
$daysInMonth = $daysInMonth === 0 ? 1 : $daysInMonth;
$next = clone $end;
$next->addDay();
$prev = clone $start;
@ -312,7 +326,7 @@ class BudgetController extends Controller
return view(
'budgets.index', compact(
'available', 'currentMonth', 'next', 'nextText', 'prev', 'allBudgets', 'prevText', 'periodStart', 'periodEnd', 'days', 'page',
'budgetInformation',
'budgetInformation', 'daysInMonth',
'inactive', 'budgets', 'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start', 'end'
)
);

View File

@ -66,6 +66,7 @@ class DebugController extends Controller
$userAgent = $request->header('user-agent');
$isSandstorm = var_export(env('IS_SANDSTORM', 'unknown'), true);
$isDocker = var_export(env('IS_DOCKER', 'unknown'), true);
$toSandbox = var_export(env('BUNQ_USE_SANDBOX', 'unknown'), true);
$trustedProxies = env('TRUSTED_PROXIES', '(none)');
$displayErrors = ini_get('display_errors');
$errorReporting = $this->errorReporting((int)ini_get('error_reporting'));
@ -96,40 +97,24 @@ class DebugController extends Controller
if (null !== $logFile) {
try {
$logContent = file_get_contents($logFile);
// @codeCoverageIgnoreStart
} catch (Exception $e) {
// don't care
Log::debug(sprintf('Could not read log file. %s', $e->getMessage()));
}
// @codeCoverageIgnoreEnd
}
}
}
// last few lines
$logContent = 'Truncated from this point <----|' . substr($logContent, -4096);
$logContent = 'Truncated from this point <----|' . substr($logContent, -8192);
return view(
'debug',
compact(
'phpVersion',
'extensions', 'localeAttempts',
'appEnv',
'appDebug',
'appLog',
'appLogLevel',
'now',
'packages',
'drivers',
'currentDriver',
'userAgent',
'displayErrors',
'errorReporting',
'phpOs',
'interface',
'logContent',
'cacheDriver',
'isDocker',
'isSandstorm',
'trustedProxies'
)
'debug', compact(
'phpVersion', 'extensions', 'localeAttempts', 'appEnv', 'appDebug', 'appLog', 'appLogLevel', 'now', 'packages', 'drivers', 'currentDriver',
'userAgent', 'displayErrors', 'errorReporting', 'phpOs', 'interface', 'logContent', 'cacheDriver', 'isDocker', 'isSandstorm', 'trustedProxies',
'toSandbox'
)
);
}

View File

@ -23,12 +23,10 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Export\ProcessorInterface;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Requests\ExportFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ExportJob;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface;
@ -107,12 +105,11 @@ class ExportController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param ExportJobRepositoryInterface $jobs
*
* @return View
*/
public function index(AccountRepositoryInterface $repository, ExportJobRepositoryInterface $jobs)
public function index(ExportJobRepositoryInterface $jobs)
{
// create new export job.
$job = $jobs->create();
@ -120,15 +117,12 @@ class ExportController extends Controller
$jobs->cleanup();
// does the user have shared accounts?
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$accountList = ExpandedForm::makeSelectList($accounts);
$checked = array_keys($accountList);
$formats = array_keys(config('firefly.export_formats'));
$defaultFormat = Preferences::get('export_format', config('firefly.default_export_format'))->data;
$first = session('first')->format('Y-m-d');
$today = Carbon::create()->format('Y-m-d');
return view('export.index', compact('job', 'checked', 'accountList', 'formats', 'defaultFormat', 'first', 'today'));
return view('export.index', compact('job', 'formats', 'defaultFormat', 'first', 'today'));
}
/**

View File

@ -133,10 +133,12 @@ class HomeController extends Controller
Log::debug('Call twig:clean...');
try {
Artisan::call('twig:clean');
// @codeCoverageIgnoreStart
} catch (Exception $e) {
// dont care
// don't care
Log::debug('Called twig:clean.');
}
// @codeCoverageIgnoreEnd
Log::debug('Call view:clear...');
Artisan::call('view:clear');
Log::debug('Done! Redirecting...');

View File

@ -1,147 +0,0 @@
<?php
/**
* ConfigurationController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Configuration\ConfiguratorInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\Request;
use Log;
/**
* Class ConfigurationController
*/
class ConfigurationController extends Controller
{
/** @var ImportJobRepositoryInterface */
public $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
$this->middleware(IsDemoUser::class);
}
/**
* Configure the job. This method is returned to until job is deemed "configured".
*
* @param ImportJob $job
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*
* @throws FireflyException
*/
public function index(ImportJob $job)
{
// create configuration class:
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
$this->repository->updateStatus($job, 'configured');
return redirect(route('import.status', [$job->key]));
}
$this->repository->updateStatus($job, 'configuring');
$view = $configurator->getNextView();
$data = $configurator->getNextData();
$subTitle = trans('firefly.import_config_bread_crumb');
$subTitleIcon = 'fa-wrench';
return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon'));
}
/**
* Store the configuration. Returns to "configure" method until job is configured.
*
* @param Request $request
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws FireflyException
*/
public function post(Request $request, ImportJob $job)
{
Log::debug('Now in postConfigure()', ['job' => $job->key]);
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
return redirect(route('import.status', [$job->key]));
}
$data = $request->all();
$configurator->configureJob($data);
// get possible warning from configurator:
$warning = $configurator->getWarningMessage();
if (\strlen($warning) > 0) {
$request->session()->flash('warning', $warning);
}
// return to configure
return redirect(route('import.configure', [$job->key]));
}
/**
* @param ImportJob $job
*
* @return ConfiguratorInterface
*
* @throws FireflyException
*/
private function makeConfigurator(ImportJob $job): ConfiguratorInterface
{
$type = $job->file_type;
$key = sprintf('import.configuration.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find configurator class for job of type "%s".', $type)); // @codeCoverageIgnore
}
Log::debug(sprintf('Going to create class "%s"', $className));
/** @var ConfiguratorInterface $configurator */
$configurator = app($className);
$configurator->setJob($job);
return $configurator;
}
}

View File

@ -24,17 +24,14 @@ namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\Request;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Http\Response as LaravelResponse;
use Log;
use Preferences;
use View;
/**
* Class FileController.
*/
@ -43,6 +40,9 @@ class IndexController extends Controller
/** @var ImportJobRepositoryInterface */
public $repository;
/** @var UserRepositoryInterface */
public $userRepository;
/**
*
*/
@ -54,33 +54,96 @@ class IndexController extends Controller
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
$this->repository = app(ImportJobRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
return $next($request);
}
);
$this->middleware(IsDemoUser::class)->except(['index']);
}
/**
* Creates a new import job for $bank with the default (global) job configuration.
* Creates a new import job for $importProvider.
*
* @param string $bank
* @param string $importProvider
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws FireflyException
*/
public function create(string $bank)
public function create(string $importProvider)
{
if (true === !config(sprintf('import.enabled.%s', $bank))) {
throw new FireflyException(sprintf('Cannot import from "%s" at this time.', $bank)); // @codeCoverageIgnore
Log::debug(sprintf('Will create job for provider "%s"', $importProvider));
// can only create "fake" for demo user.
$providers = array_keys($this->getProviders());
if (!\in_array($importProvider, $providers, true)) {
Log::error(sprintf('%s-provider is disabled. Cannot create job.', $importProvider));
session()->flash('warning', trans('import.cannot_create_for_provider', ['provider' => $importProvider]));
return redirect(route('import.index'));
}
$importJob = $this->repository->create($bank);
$importJob = $this->repository->create($importProvider);
Log::debug(sprintf('Created job #%d for provider %s', $importJob->id, $importProvider));
$hasPreReq = (bool)config(sprintf('import.has_prereq.%s', $importProvider));
$hasConfig = (bool)config(sprintf('import.has_job_config.%s', $importProvider));
// if job provider has no prerequisites:
if ($hasPreReq === false) {
Log::debug('Provider has no prerequisites. Continue.');
// if job provider also has no configuration:
if ($hasConfig === false) {
// @codeCoverageIgnoreStart
Log::debug('Provider needs no configuration for job. Job is ready to start.');
$this->repository->updateStatus($importJob, 'ready_to_run');
Log::debug('Redirect to status-page.');
return redirect(route('import.job.status.index', [$importJob->key]));
// @codeCoverageIgnoreEnd
}
// update job to say "has_prereq".
$this->repository->setStatus($importJob, 'has_prereq');
// redirect to job configuration.
Log::debug('Redirect to configuration.');
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
Log::debug('Job provider has prerequisites.');
// if need to set prerequisites, do that first.
$class = (string)config(sprintf('import.prerequisites.%s', $importProvider));
if (!class_exists($class)) {
throw new FireflyException(sprintf('No class to handle prerequisites for "%s".', $importProvider)); // @codeCoverageIgnore
}
/** @var PrerequisitesInterface $providerPre */
$providerPre = app($class);
$providerPre->setUser(auth()->user());
if (!$providerPre->isComplete()) {
Log::debug('Job provider prerequisites are not yet filled in. Redirect to prerequisites-page.');
// redirect to global prerequisites
return redirect(route('import.prerequisites.index', [$importProvider, $importJob->key]));
}
Log::debug('Prerequisites are complete.');
// update job to say "has_prereq".
$this->repository->setStatus($importJob, 'has_prereq');
if ($hasConfig === false) {
// @codeCoverageIgnoreStart
Log::debug('Provider has no configuration. Job is ready to start.');
$this->repository->updateStatus($importJob, 'ready_to_run');
Log::debug('Redirect to status-page.');
return redirect(route('import.job.status.index', [$importJob->key]));
// @codeCoverageIgnoreEnd
}
Log::debug('Job has configuration. Redirect to job-config.');
// Otherwise just redirect to job configuration.
return redirect(route('import.job.configuration.index', [$importJob->key]));
// from here, always go to configure step.
return redirect(route('import.configure', [$importJob->key]));
}
/**
@ -90,22 +153,18 @@ class IndexController extends Controller
*
* @return LaravelResponse
*/
public function download(ImportJob $job)
public function download(ImportJob $job): LaravelResponse
{
Log::debug('Now in download()', ['job' => $job->key]);
$config = $job->configuration;
$config = $this->repository->getConfiguration($job);
// This is CSV import specific:
$config['column-roles-complete'] = false;
$config['column-mapping-complete'] = false;
$config['initial-config-complete'] = false;
$config['has-file-upload'] = false;
$config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter'];
unset($config['stage']);
$result = json_encode($config, JSON_PRETTY_PRINT);
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
$config['delimiter'] = $config['delimiter'] ?? ',';
$config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter'];
// this prevents private information from escaping
$config['column-mapping-config'] = [];
$result = json_encode($config, JSON_PRETTY_PRINT);
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
/** @var LaravelResponse $response */
$response = response($result, 200);
$response->header('Content-disposition', 'attachment; filename=' . $name)
@ -127,78 +186,60 @@ class IndexController extends Controller
*/
public function index()
{
$subTitle = trans('firefly.import_index_sub_title');
$providers = $this->getProviders();
$subTitle = trans('import.index_breadcrumb');
$subTitleIcon = 'fa-home';
$routines = config('import.enabled');
return view('import.index', compact('subTitle', 'subTitleIcon', 'routines'));
return view('import.index', compact('subTitle', 'subTitleIcon', 'providers'));
}
/**
* @param Request $request
* @param string $bank
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @return array
*/
public function reset(Request $request, string $bank)
private function getProviders(): array
{
if ($bank === 'bunq') {
// remove bunq related preferences.
Preferences::delete('bunq_api_key');
Preferences::delete('bunq_server_public_key');
Preferences::delete('bunq_private_key');
Preferences::delete('bunq_public_key');
Preferences::delete('bunq_installation_token');
Preferences::delete('bunq_installation_id');
Preferences::delete('bunq_device_server_id');
Preferences::delete('external_ip');
// get and filter all import routines:
/** @var array $config */
$providerNames = array_keys(config('import.enabled'));
$providers = [];
$isDemoUser = $this->userRepository->hasRole(auth()->user(), 'demo');
$isDebug = (bool)config('app.debug');
foreach ($providerNames as $providerName) {
//Log::debug(sprintf('Now with provider %s', $providerName));
// only consider enabled providers
$enabled = (bool)config(sprintf('import.enabled.%s', $providerName));
$allowedForDemo = (bool)config(sprintf('import.allowed_for_demo.%s', $providerName));
$allowedForUser = (bool)config(sprintf('import.allowed_for_user.%s', $providerName));
if ($enabled === false) {
//Log::debug('Provider is not enabled. NEXT!');
continue;
}
if ($isDemoUser === true && $allowedForDemo === false) {
//Log::debug('User is demo and this provider is not allowed for demo user. NEXT!');
continue;
}
if ($isDemoUser === false && $allowedForUser === false && $isDebug === false) {
//Log::debug('User is not demo and this provider is not allowed for such users. NEXT!');
continue; // @codeCoverageIgnore
}
$providers[$providerName] = [
'has_prereq' => (bool)config('import.has_prereq.' . $providerName),
];
$class = (string)config(sprintf('import.prerequisites.%s', $providerName));
$result = false;
if ($class !== '' && class_exists($class)) {
//Log::debug('Will not check prerequisites.');
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
$result = $object->isComplete();
}
$providers[$providerName]['prereq_complete'] = $result;
}
Log::debug(sprintf('Enabled providers: %s', json_encode(array_keys($providers))));
if ($bank === 'spectre') {
// remove spectre related preferences:
Preferences::delete('spectre_client_id');
Preferences::delete('spectre_app_secret');
Preferences::delete('spectre_service_secret');
Preferences::delete('spectre_app_id');
Preferences::delete('spectre_secret');
Preferences::delete('spectre_private_key');
Preferences::delete('spectre_public_key');
Preferences::delete('spectre_customer');
}
Preferences::mark();
$request->session()->flash('info', (string)trans('firefly.settings_reset_for_' . $bank));
return redirect(route('import.index'));
}
/**
* @param ImportJob $job
*
* @return \Illuminate\Http\JsonResponse
*
* @throws FireflyException
*/
public function start(ImportJob $job)
{
$type = $job->file_type;
$key = sprintf('import.routine.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setJob($job);
$result = $routine->run();
if ($result) {
return response()->json(['run' => 'ok']);
}
throw new FireflyException('Job did not complete successfully. Please review the log files.');
return $providers;
}
}

View File

@ -0,0 +1,184 @@
<?php
/**
* JobConfigurationController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Import\JobConfiguration\JobConfigurationInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\MessageBag;
use Log;
/**
* Class JobConfigurationController
*/
class JobConfigurationController extends Controller
{
/** @var ImportJobRepositoryInterface */
public $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Configure the job. This method is returned to until job is deemed "configured".
*
* @param ImportJob $importJob
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*
* @throws FireflyException
*/
public function index(ImportJob $importJob)
{
Log::debug('Now in JobConfigurationController::index()');
// catch impossible status:
$allowed = ['has_prereq', 'need_job_config'];
if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::debug(sprintf('Job has state "%s", but we only accept %s', $importJob->status, json_encode($allowed)));
session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status]));
return redirect(route('import.index'));
}
Log::debug(sprintf('Now in JobConfigurationController::index() with job "%s" and status "%s"', $importJob->key, $importJob->status));
// if provider has no config, just push it through:
$importProvider = $importJob->provider;
if (!(bool)config(sprintf('import.has_job_config.%s', $importProvider))) {
// @codeCoverageIgnoreStart
Log::debug('Job needs no config, is ready to run!');
$this->repository->updateStatus($importJob, 'ready_to_run');
return redirect(route('import.job.status.index', [$importJob->key]));
// @codeCoverageIgnoreEnd
}
// create configuration class:
$configurator = $this->makeConfigurator($importJob);
// is the job already configured?
if ($configurator->configurationComplete()) {
Log::debug('Config is complete, set status to ready_to_run.');
$this->repository->updateStatus($importJob, 'ready_to_run');
return redirect(route('import.job.status.index', [$importJob->key]));
}
$view = $configurator->getNextView();
$data = $configurator->getNextData();
$subTitle = trans('import.job_configuration_breadcrumb', ['key' => $importJob->key]);
$subTitleIcon = 'fa-wrench';
return view($view, compact('data', 'importJob', 'subTitle', 'subTitleIcon'));
}
/**
* Store the configuration. Returns to "configure" method until job is configured.
*
* @param Request $request
* @param ImportJob $importJob
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws FireflyException
*/
public function post(Request $request, ImportJob $importJob)
{
// catch impossible status:
$allowed = ['has_prereq', 'need_job_config'];
if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status]));
return redirect(route('import.index'));
}
Log::debug('Now in postConfigure()', ['job' => $importJob->key]);
$configurator = $this->makeConfigurator($importJob);
// is the job already configured?
if ($configurator->configurationComplete()) {
$this->repository->updateStatus($importJob, 'ready_to_run');
return redirect(route('import.job.status.index', [$importJob->key]));
}
// uploaded files are attached to the job.
// the configurator can then handle them.
$result = new MessageBag;
/** @var UploadedFile $upload */
foreach ($request->allFiles() as $name => $upload) {
$result = $this->repository->storeFileUpload($importJob, $name, $upload);
}
$data = $request->all();
$messages = $configurator->configureJob($data);
$result->merge($messages);
if ($messages->count() > 0) {
$request->session()->flash('warning', $messages->first());
}
// return to configure
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
/**
* @param ImportJob $importJob
*
* @return JobConfigurationInterface
*
* @throws FireflyException
*/
private function makeConfigurator(ImportJob $importJob): JobConfigurationInterface
{
$key = sprintf('import.configuration.%s', $importJob->provider);
$className = (string)config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find configurator class for job with provider "%s".', $importJob->provider)); // @codeCoverageIgnore
}
Log::debug(sprintf('Going to create class "%s"', $className));
/** @var JobConfigurationInterface $configurator */
$configurator = app($className);
$configurator->setImportJob($importJob);
return $configurator;
}
}

View File

@ -0,0 +1,232 @@
<?php
/**
* JobStatusController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Import\Storage\ImportArrayStorage;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Log;
/**
* Class JobStatusController
*/
class JobStatusController extends Controller
{
/** @var ImportJobRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param ImportJob $importJob
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(ImportJob $importJob)
{
$subTitleIcon = 'fa-gear';
$subTitle = trans('import.job_status_breadcrumb', ['key' => $importJob->key]);
return view('import.status', compact('importJob', 'subTitle', 'subTitleIcon'));
}
/**
* @param ImportJob $importJob
*
* @return JsonResponse
*/
public function json(ImportJob $importJob): JsonResponse
{
$count = \count($importJob->transactions);
$json = [
'status' => $importJob->status,
'errors' => $importJob->errors,
'count' => $count,
'tag_id' => $importJob->tag_id,
'tag_name' => null === $importJob->tag_id ? null : $importJob->tag->tag,
'report_txt' => trans('import.unknown_import_result'),
'download_config' => false,
'download_config_text' => '',
];
if ($importJob->provider === 'file') {
$json['download_config'] = true;
$json['download_config_text']
= trans('import.should_download_config', ['route' => route('import.job.download', [$importJob->key])]) . ' '
. trans('import.share_config_file');
}
// if count is zero:
if (null !== $importJob->tag_id) {
$count = $importJob->tag->transactionJournals->count();
}
if ($count === 0) {
$json['report_txt'] = trans('import.result_no_transactions');
}
if ($count === 1 && null !== $importJob->tag_id) {
$json['report_txt'] = trans(
'import.result_one_transaction', ['route' => route('tags.show', [$importJob->tag_id, 'all']), 'tag' => $importJob->tag->tag]
);
}
if ($count > 1 && null !== $importJob->tag_id) {
$json['report_txt'] = trans(
'import.result_many_transactions',
['count' => $count, 'route' => route('tags.show', [$importJob->tag_id, 'all']), 'tag' => $importJob->tag->tag]
);
}
return response()->json($json);
}
/**
* @param ImportJob $importJob
*
* @return JsonResponse
*/
public function start(ImportJob $importJob): JsonResponse
{
// catch impossible status:
$allowed = ['ready_to_run', 'need_job_config', 'error']; // todo remove error
if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::error('Job is not ready.');
$this->repository->setStatus($importJob, 'error');
return response()->json(
['status' => 'NOK', 'message' => sprintf('JobStatusController::start expects status "ready_to_run" instead of "%s".', $importJob->status)]
);
}
$importProvider = $importJob->provider;
$key = sprintf('import.routine.%s', $importProvider);
$className = config($key);
if (null === $className || !class_exists($className)) {
// @codeCoverageIgnoreStart
return response()->json(
['status' => 'NOK', 'message' => sprintf('Cannot find import routine class for job of type "%s".', $importProvider)]
);
// @codeCoverageIgnoreEnd
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setImportJob($importJob);
try {
$routine->run();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$this->repository->setStatus($importJob, 'error');
return response()->json(['status' => 'NOK', 'message' => $message]);
}
// expect nothing from routine, just return OK to user.
return response()->json(['status' => 'OK', 'message' => 'stage_finished']);
}
/**
* Store does three things:
*
* - Store the transactions.
* - Add them to a tag.
*
* @param ImportJob $importJob
*
* @return JsonResponse
*/
public function store(ImportJob $importJob): JsonResponse
{
// catch impossible status:
$allowed = ['provider_finished', 'storing_data'];
if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::error('Job is not ready.');
return response()->json(
['status' => 'NOK', 'message' => sprintf('JobStatusController::start expects status "provider_finished" instead of "%s".', $importJob->status)]
);
}
// set job to be storing data:
$this->repository->setStatus($importJob, 'storing_data');
try {
$this->storeTransactions($importJob);
} catch (FireflyException $e) {
$message = 'The import storage routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$this->repository->setStatus($importJob, 'error');
return response()->json(['status' => 'NOK', 'message' => $message]);
}
// set storage to be finished:
$this->repository->setStatus($importJob, 'storage_finished');
// expect nothing from routine, just return OK to user.
return response()->json(['status' => 'OK', 'message' => 'storage_finished']);
}
/**
* @param ImportJob $importJob
*
* @throws FireflyException
*/
private function storeTransactions(ImportJob $importJob): void
{
/** @var ImportArrayStorage $storage */
$storage = app(ImportArrayStorage::class);
$storage->setImportJob($importJob);
try {
$storage->store();
} catch (FireflyException|Exception $e) {
throw new FireflyException($e->getMessage());
}
}
}

View File

@ -24,8 +24,9 @@ namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\Request;
use Log;
@ -35,6 +36,9 @@ use Log;
class PrerequisitesController extends Controller
{
/** @var ImportJobRepositoryInterface */
private $repository;
/**
*
*/
@ -46,48 +50,59 @@ class PrerequisitesController extends Controller
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
app('view')->share('subTitleIcon', 'fa-check');
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
$this->middleware(IsDemoUser::class);
}
/**
* Once there are no prerequisites, this method will create an importjob object and
* redirect the user to a view where this object can be used by a bank specific
* class to process.
* This method will process and store import provider global prerequisites
* such as API keys.
*
* @param string $bank
*
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
* @param string $importProvider
* @param ImportJob $importJob
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws FireflyException
*/
public function index(string $bank)
public function index(string $importProvider, ImportJob $importJob = null)
{
if (true === !config(sprintf('import.enabled.%s', $bank))) {
throw new FireflyException(sprintf('Cannot import from "%s" at this time.', $bank)); // @codeCoverageIgnore
}
$class = (string)config(sprintf('import.prerequisites.%s', $bank));
if (!class_exists($class)) {
throw new FireflyException(sprintf('No class to handle "%s".', $bank)); // @codeCoverageIgnore
// catch impossible status:
$allowed = ['new'];
if (null !== $importJob && !in_array($importJob->status, $allowed)) {
Log::error(sprintf('Job has state "%s" but this Prerequisites::index() only accepts %s', $importJob->status, json_encode($allowed)));
session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status]));
return redirect(route('import.index'));
}
app('view')->share('subTitle', trans('import.prerequisites_breadcrumb_' . $importProvider));
$class = (string)config(sprintf('import.prerequisites.%s', $importProvider));
if (!class_exists($class)) {
throw new FireflyException(sprintf('No class to handle prerequisites for "%s".', $importProvider)); // @codeCoverageIgnore
}
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if ($object->hasPrerequisites()) {
$view = $object->getView();
$parameters = ['title' => (string)trans('firefly.import_index_title'), 'mainTitleIcon' => 'fa-archive'];
$parameters = array_merge($object->getViewParameters(), $parameters);
if (null !== $importJob && $object->isComplete()) {
// update job:
$this->repository->setStatus($importJob, 'has_prereq');
return view($view, $parameters);
// redirect to job config:
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
// if no (more) prerequisites, return to create a job:
return redirect(route('import.create-job', [$bank]));
$view = $object->getView();
$parameters = ['title' => (string)trans('firefly.import_index_title'), 'mainTitleIcon' => 'fa-archive', 'importJob' => $importJob];
$parameters = array_merge($object->getViewParameters(), $parameters);
return view($view, $parameters);
}
/**
@ -98,42 +113,63 @@ class PrerequisitesController extends Controller
*
* @see PrerequisitesInterface::storePrerequisites
*
* @param Request $request
* @param string $bank
* @param Request $request
* @param string $importProvider
* @param ImportJob $importJob
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws FireflyException
*/
public function post(Request $request, string $bank)
public function post(Request $request, string $importProvider, ImportJob $importJob = null)
{
Log::debug(sprintf('Now in postPrerequisites for %s', $bank));
Log::debug(sprintf('Now in postPrerequisites for %s', $importProvider));
if (true === !config(sprintf('import.enabled.%s', $bank))) {
throw new FireflyException(sprintf('Cannot import from "%s" at this time.', $bank)); // @codeCoverageIgnore
// catch impossible status:
$allowed = ['new'];
if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::error(sprintf('Job has state "%s" but this Prerequisites::post() only accepts %s', $importJob->status, json_encode($allowed)));
session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status]));
return redirect(route('import.index'));
}
$class = (string)config(sprintf('import.prerequisites.%s', $bank));
$class = (string)config(sprintf('import.prerequisites.%s', $importProvider));
if (!class_exists($class)) {
throw new FireflyException(sprintf('Cannot find class %s', $class)); // @codeCoverageIgnore
}
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if (!$object->hasPrerequisites()) {
Log::debug(sprintf('No more prerequisites for %s, move to form.', $bank));
return redirect(route('import.create-job', [$bank]));
}
Log::debug('Going to store entered prerequisites.');
// store post data
$result = $object->storePrerequisites($request);
$data = $request->all();
$result = $object->storePrerequisites($data);
Log::debug(sprintf('Result of storePrerequisites has message count: %d', $result->count()));
if ($result->count() > 0) {
$request->session()->flash('error', $result->first());
// redirect back to job, if has job:
return redirect(route('import.prerequisites.index', [$importProvider, $importJob->key ?? '']))->withInput();
}
return redirect(route('import.prerequisites', [$bank]));
// session flash!
$request->session()->flash('success', (string)trans('import.prerequisites_saved_for_' . $importProvider));
// if has job, redirect to global config for provider
// if no job, back to index!
if (null === $importJob) {
return redirect(route('import.index'));
}
// update job:
$this->repository->setStatus($importJob, 'has_prereq');
// redirect to job config:
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
}

View File

@ -1,125 +0,0 @@
<?php
/**
* StatusController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Log;
/**
* Class StatusController
*/
class StatusController extends Controller
{
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
return $next($request);
}
);
$this->middleware(IsDemoUser::class);
}
/**
* @param ImportJob $job
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*/
public function index(ImportJob $job)
{
$statuses = ['configured', 'running', 'finished', 'error'];
if (!\in_array($job->status, $statuses)) {
return redirect(route('import.configure', [$job->key]));
}
$subTitle = trans('import.status_sub_title');
$subTitleIcon = 'fa-star';
return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
}
/**
* Show status of import job in JSON.
*
* @param ImportJob $job
*
* @return \Illuminate\Http\JsonResponse
*/
public function json(ImportJob $job)
{
$result = [
'started' => false,
'finished' => false,
'running' => false,
'errors' => array_values($job->extended_status['errors']),
'percentage' => 0,
'show_percentage' => false,
'steps' => $job->extended_status['steps'],
'done' => $job->extended_status['done'],
'statusText' => trans('import.status_job_' . $job->status),
'status' => $job->status,
'finishedText' => '',
];
if (0 !== $job->extended_status['steps']) {
$result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
$result['show_percentage'] = true;
}
if ('finished' === $job->status) {
$result['finished'] = true;
$tagId = (int)$job->extended_status['tag'];
if ($tagId !== 0) {
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$tag = $repository->find($tagId);
$count = $tag->transactionJournals()->count();
$result['finishedText'] = trans(
'import.status_finished_job', ['count' => $count, 'link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]
);
}
if ($tagId === 0) {
$result['finishedText'] = trans('import.status_finished_no_tag'); // @codeCoverageIgnore
}
}
if ('running' === $job->status) {
$result['started'] = true;
$result['running'] = true;
}
$result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage'];
Log::debug(sprintf('JOB STATUS: %d/%d', $result['done'], $result['steps']));
return response()->json($result);
}
}

View File

@ -29,6 +29,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Log;
use Preferences;
@ -68,9 +69,9 @@ class JavascriptController extends Controller
/**
* @param CurrencyRepositoryInterface $repository
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function currencies(CurrencyRepositoryInterface $repository)
public function currencies(CurrencyRepositoryInterface $repository): Response
{
$currencies = $repository->get();
$data = ['currencies' => []];
@ -102,7 +103,8 @@ class JavascriptController extends Controller
}
/** @var TransactionCurrency $currency */
$currency = $currencyRepository->findNull($currencyId);
if (0 === $currencyId) {
if (null === $currency) {
/** @var TransactionCurrency $currency */
$currency = app('amount')->getDefaultCurrency();
}

View File

@ -35,6 +35,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\JsonResponse;
/**
* Class AutoCompleteController.
@ -47,7 +48,7 @@ class AutoCompleteController extends Controller
*
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function allAccounts(AccountRepositoryInterface $repository)
{
@ -64,9 +65,9 @@ class AutoCompleteController extends Controller
/**
* @param JournalCollectorInterface $collector
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function allTransactionJournals(JournalCollectorInterface $collector)
public function allTransactionJournals(JournalCollectorInterface $collector): JsonResponse
{
$collector->setLimit(250)->setPage(1);
$return = array_unique($collector->getJournals()->pluck('description')->toArray());
@ -80,9 +81,9 @@ class AutoCompleteController extends Controller
*
* @param BillRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function bills(BillRepositoryInterface $repository)
public function bills(BillRepositoryInterface $repository): JsonResponse
{
$return = array_unique(
$repository->getActiveBills()->pluck('name')->toArray()
@ -95,7 +96,7 @@ class AutoCompleteController extends Controller
/**
* @param BudgetRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function budgets(BudgetRepositoryInterface $repository)
{
@ -110,7 +111,7 @@ class AutoCompleteController extends Controller
*
* @param CategoryRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function categories(CategoryRepositoryInterface $repository)
{
@ -123,7 +124,7 @@ class AutoCompleteController extends Controller
/**
* @param CurrencyRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function currencyNames(CurrencyRepositoryInterface $repository)
{
@ -138,7 +139,7 @@ class AutoCompleteController extends Controller
*
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function expenseAccounts(AccountRepositoryInterface $repository)
{
@ -163,7 +164,7 @@ class AutoCompleteController extends Controller
* @param JournalCollectorInterface $collector
* @param TransactionJournal $except
*
* @return \Illuminate\Http\JsonResponse|mixed
* @return JsonResponse|mixed
*/
public function journalsWithId(JournalCollectorInterface $collector, TransactionJournal $except)
{
@ -195,7 +196,7 @@ class AutoCompleteController extends Controller
/**
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function revenueAccounts(AccountRepositoryInterface $repository)
{
@ -220,7 +221,7 @@ class AutoCompleteController extends Controller
*
* @param TagRepositoryInterface $tagRepository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function tags(TagRepositoryInterface $tagRepository)
{
@ -234,7 +235,7 @@ class AutoCompleteController extends Controller
* @param JournalCollectorInterface $collector
* @param string $what
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function transactionJournals(JournalCollectorInterface $collector, string $what)
{
@ -251,7 +252,7 @@ class AutoCompleteController extends Controller
/**
* @param JournalRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function transactionTypes(JournalRepositoryInterface $repository)
{

View File

@ -30,6 +30,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\PiggyBankTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
@ -215,6 +216,7 @@ class PiggyBankController extends Controller
*/
public function index(Request $request)
{
$this->piggyRepos->correctOrder();
$collection = $this->piggyRepos->getPiggyBanks();
$total = $collection->count();
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
@ -261,27 +263,6 @@ class PiggyBankController extends Controller
return view('piggy-banks.index', compact('piggyBanks', 'accounts'));
}
/**
* @param Request $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function order(Request $request)
{
$data = $request->get('order');
// set all users piggy banks to zero:
$this->piggyRepos->reset();
if (\is_array($data)) {
foreach ($data as $order => $id) {
$this->piggyRepos->setOrder((int)$id, $order + 1);
}
}
return response()->json(['result' => 'ok']);
}
/**
* @param Request $request
* @param PiggyBank $piggyBank
@ -402,6 +383,20 @@ class PiggyBankController extends Controller
return view('piggy-banks.remove-mobile', compact('piggyBank', 'repetition', 'currency'));
}
/**
* @param Request $request
* @param PiggyBank $piggyBank
*
* @return JsonResponse
*/
public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
{
$newOrder = (int)$request->get('order');
$this->piggyRepos->setOrder($piggyBank, $newOrder);
return response()->json(['data' => 'OK']);
}
/**
* @param PiggyBank $piggyBank
*
@ -409,11 +404,17 @@ class PiggyBankController extends Controller
*/
public function show(PiggyBank $piggyBank)
{
$note = $piggyBank->notes()->first();
$events = $this->piggyRepos->getEvents($piggyBank);
$subTitle = $piggyBank->name;
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
// transform piggies using the transformer:
$parameters = new ParameterBag;
$parameters->set('end', $end);
$transformer = new PiggyBankTransformer(new ParameterBag);
$piggy = $transformer->transform($piggyBank);
$events = $this->piggyRepos->getEvents($piggyBank);
$subTitle = $piggyBank->name;
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'note'));
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'piggy'));
}
/**

View File

@ -424,7 +424,7 @@ class ProfileController extends Controller
/**
*
*/
private function createOAuthKeys()
private function createOAuthKeys(): void
{
$rsa = new RSA();
$keys = $rsa->createKey(4096);
@ -437,11 +437,13 @@ class ProfileController extends Controller
if (file_exists($publicKey) || file_exists($privateKey)) {
return;
}
// @codeCoverageIgnoreStart
Log::alert('NO OAuth keys were found. They have been created.');
file_put_contents($publicKey, array_get($keys, 'publickey'));
file_put_contents($privateKey, array_get($keys, 'privatekey'));
}
// @codeCoverageIgnoreEnd
/**
* @return string

View File

@ -102,7 +102,6 @@ class RuleController extends Controller
$preFilled = [
'strict' => true,
];
$groups = ExpandedForm::makeSelectList($this->ruleGroupRepos->get());
$oldTriggers = [];
$oldActions = [];
$returnToBill = false;
@ -150,7 +149,7 @@ class RuleController extends Controller
return view(
'rules.rule.create',
compact(
'subTitleIcon', 'oldTriggers', 'returnToBill', 'groups', 'preFilled', 'bill', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup',
'subTitleIcon', 'oldTriggers', 'returnToBill', 'preFilled', 'bill', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup',
'subTitle'
)
);
@ -212,7 +211,6 @@ class RuleController extends Controller
*/
public function edit(Request $request, Rule $rule)
{
$ruleGroups = ExpandedForm::makeSelectList($this->ruleGroupRepos->get());
$triggerCount = 0;
$actionCount = 0;
$oldActions = [];
@ -243,7 +241,7 @@ class RuleController extends Controller
}
session()->forget('rules.edit.fromUpdate');
return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroups'));
return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount'));
}
/**
@ -333,14 +331,11 @@ class RuleController extends Controller
public function selectTransactions(Rule $rule)
{
// does the user have shared accounts?
$accounts = $this->accountRepos->getAccountsByType([AccountType::ASSET]);
$accountList = ExpandedForm::makeSelectList($accounts);
$checkedAccounts = array_keys($accountList);
$first = session('first')->format('Y-m-d');
$today = Carbon::create()->format('Y-m-d');
$subTitle = (string)trans('firefly.apply_rule_selection', ['title' => $rule->title]);
return view('rules.rule.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'rule', 'subTitle'));
return view('rules.rule.select-transactions', compact( 'first', 'today', 'rule', 'subTitle'));
}
/**
@ -357,12 +352,12 @@ class RuleController extends Controller
// redirect to show bill.
if ($request->get('return_to_bill') === 'true' && (int)$request->get('bill_id') > 0) {
return redirect(route('bills.show', [(int)$request->get('bill_id')]));
return redirect(route('bills.show', [(int)$request->get('bill_id')])); // @codeCoverageIgnore
}
// redirect to new bill creation.
if ((int)$request->get('bill_id') > 0) {
return redirect($this->getPreviousUri('bills.create.uri'));
return redirect($this->getPreviousUri('bills.create.uri')); // @codeCoverageIgnore
}
@ -408,10 +403,12 @@ class RuleController extends Controller
$matcher->setTriggers($triggers);
try {
$matchingTransactions = $matcher->findTransactionsByTriggers();
// @codeCoverageIgnoreStart
} catch (FireflyException $exception) {
Log::error(sprintf('Could not grab transactions in testTriggers(): %s', $exception->getMessage()));
Log::error($exception->getTraceAsString());
}
// @codeCoverageIgnoreStart
// Warn the user if only a subset of transactions is returned
@ -427,10 +424,12 @@ class RuleController extends Controller
$view = 'ERROR, see logs.';
try {
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
// @codeCoverageIgnoreStart
} catch (Throwable $exception) {
Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage()));
Log::error($exception->getTraceAsString());
}
// @codeCoverageIgnoreEnd
return response()->json(['html' => $view, 'warning' => $warning]);
}
@ -466,10 +465,12 @@ class RuleController extends Controller
$matcher->setRule($rule);
try {
$matchingTransactions = $matcher->findTransactionsByRule();
// @codeCoverageIgnoreStart
} catch (FireflyException $exception) {
Log::error(sprintf('Could not grab transactions in testTriggersByRule(): %s', $exception->getMessage()));
Log::error($exception->getTraceAsString());
}
// @codeCoverageIgnoreEnd
// Warn the user if only a subset of transactions is returned
$warning = '';
@ -484,10 +485,12 @@ class RuleController extends Controller
$view = 'ERROR, see logs.';
try {
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
// @codeCoverageIgnoreStart
} catch (Throwable $exception) {
Log::error(sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage()));
Log::error($exception->getTraceAsString());
}
// @codeCoverageIgnoreEnd
return response()->json(['html' => $view, 'warning' => $warning]);
}
@ -592,10 +595,12 @@ class RuleController extends Controller
'count' => 1,
]
)->render();
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
Log::error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
}
// @codeCoverageIgnoreEnd
return $actions;
}
@ -625,10 +630,12 @@ class RuleController extends Controller
'count' => $count,
]
)->render();
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
}
// @codeCoverageIgnoreEnd
++$index;
}
@ -660,10 +667,12 @@ class RuleController extends Controller
'count' => $count,
]
)->render();
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
}
// @codeCoverageIgnoreEnd
++$index;
}
}
@ -696,10 +705,12 @@ class RuleController extends Controller
'count' => $count,
]
)->render();
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
}
// @codeCoverageIgnoreEnd
++$newIndex;
}
@ -732,10 +743,12 @@ class RuleController extends Controller
'count' => $count,
]
)->render();
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
}
// @codeCoverageIgnoreEnd
++$newIndex;
}
@ -791,10 +804,12 @@ class RuleController extends Controller
'count' => 4,
]
)->render();
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage()));
Log::debug($e->getTraceAsString());
}
// @codeCoverageIgnoreEnd
return $triggers;
}

View File

@ -23,11 +23,9 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Http\Requests\RuleGroupFormRequest;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
use FireflyIII\Jobs\ExecuteRuleGroupOnExistingTransactions;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
@ -75,22 +73,18 @@ class RuleGroupController extends Controller
}
/**
* @param RuleGroupRepositoryInterface $repository
* @param RuleGroup $ruleGroup
* @param RuleGroup $ruleGroup
*
* @return View
*/
public function delete(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup)
public function delete(RuleGroup $ruleGroup)
{
$subTitle = trans('firefly.delete_rule_group', ['title' => $ruleGroup->title]);
$ruleGroupList = ExpandedForm::makeSelectListWithEmpty($repository->get());
unset($ruleGroupList[$ruleGroup->id]);
// put previous url in session
$this->rememberPreviousUri('rule-groups.delete.uri');
return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle', 'ruleGroupList'));
return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle'));
}
/**
@ -135,11 +129,17 @@ class RuleGroupController extends Controller
{
$subTitle = trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]);
$preFilled = [
'active' => $ruleGroup->active,
];
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('rule-groups.edit.fromUpdate')) {
$this->rememberPreviousUri('rule-groups.edit.uri');
}
session()->forget('rule-groups.edit.fromUpdate');
session()->flash('preFilled', $preFilled);
return view('rules.rule-group.edit', compact('ruleGroup', 'subTitle'));
}
@ -179,22 +179,17 @@ class RuleGroupController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param RuleGroup $ruleGroup
* @param RuleGroup $ruleGroup
*
* @return View
*/
public function selectTransactions(AccountRepositoryInterface $repository, RuleGroup $ruleGroup)
public function selectTransactions(RuleGroup $ruleGroup)
{
// does the user have shared accounts?
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$accountList = ExpandedForm::makeSelectList($accounts);
$checkedAccounts = array_keys($accountList);
$first = session('first')->format('Y-m-d');
$today = Carbon::create()->format('Y-m-d');
$subTitle = (string)trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]);
$first = session('first')->format('Y-m-d');
$today = Carbon::create()->format('Y-m-d');
$subTitle = (string)trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]);
return view('rules.rule-group.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'ruleGroup', 'subTitle'));
return view('rules.rule-group.select-transactions', compact('first', 'today', 'ruleGroup', 'subTitle'));
}
/**

View File

@ -33,6 +33,7 @@ use Log;
use phpseclib\Crypt\RSA;
/**
* @codeCoverageIgnore
* Class InstallController
*/
class InstallController extends Controller

View File

@ -31,19 +31,10 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Preferences;
use View;
/**
* Class TagController.
*
* Remember: a balancingAct takes at most one expense and one transfer.
* an advancePayment takes at most one expense, infinite deposits and NO transfers.
*
* transaction can only have one advancePayment OR balancingAct.
* Other attempts to put in such a tag are blocked.
* also show an error when editing a tag and it becomes either
* of these two types. Or rather, block editing of the tag.
*/
class TagController extends Controller
{
@ -79,7 +70,6 @@ class TagController extends Controller
{
$subTitle = trans('firefly.new_tag');
$subTitleIcon = 'fa-tag';
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
// put previous url in session if not redirect from store (not "create another").
if (true !== session('tags.create.fromStore')) {
@ -87,7 +77,7 @@ class TagController extends Controller
}
session()->forget('tags.create.fromStore');
return view('tags.create', compact('subTitle', 'subTitleIcon', 'apiKey'));
return view('tags.create', compact('subTitle', 'subTitleIcon'));
}
/**
@ -118,7 +108,7 @@ class TagController extends Controller
$this->repository->destroy($tag);
session()->flash('success', (string)trans('firefly.deleted_tag', ['tag' => $tagName]));
Preferences::mark();
app('preferences')->mark();
return redirect($this->getPreviousUri('tags.delete.uri'));
}
@ -134,7 +124,6 @@ class TagController extends Controller
{
$subTitle = trans('firefly.edit_tag', ['tag' => $tag->tag]);
$subTitleIcon = 'fa-tag';
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('tags.edit.fromUpdate')) {
@ -142,7 +131,7 @@ class TagController extends Controller
}
session()->forget('tags.edit.fromUpdate');
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'apiKey'));
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon'));
}
/**
@ -155,27 +144,18 @@ class TagController extends Controller
public function index(TagRepositoryInterface $repository)
{
// start with oldest tag
$oldestTag = $repository->oldestTag();
/** @var Carbon $start */
$start = new Carbon;
if (null !== $oldestTag) {
/** @var Carbon $start */
$start = $oldestTag->date; // @codeCoverageIgnore
}
if (null === $oldestTag) {
/** @var Carbon $start */
$start = clone session('first');
}
$now = new Carbon;
$oldestTagDate = null === $repository->oldestTag() ? clone session('first') : $repository->oldestTag()->date;
$newestTagDate = null === $repository->newestTag() ? new Carbon : $repository->newestTag()->date;
$oldestTagDate->startOfYear();
$newestTagDate->endOfYear();
$clouds = [];
$clouds['no-date'] = $repository->tagCloud(null);
while ($now > $start) {
$year = $now->year;
while ($newestTagDate > $oldestTagDate) {
$year = $newestTagDate->year;
$clouds[$year] = $repository->tagCloud($year);
$now->subYear();
$newestTagDate->subYear();
}
$count = $repository->count();
@ -196,12 +176,11 @@ class TagController extends Controller
$subTitle = $tag->tag;
$subTitleIcon = 'fa-tag';
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$range = Preferences::get('viewRange', '1M')->data;
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$range = app('preferences')->get('viewRange', '1M')->data;
$start = null;
$end = null;
$periods = new Collection;
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
$path = route('tags.show', [$tag->id]);
// prep for "all" view.
@ -213,7 +192,7 @@ class TagController extends Controller
}
// prep for "specific date" view.
if (\strlen($moment) > 0 && 'all' !== $moment) {
if ('all' !== $moment && \strlen($moment) > 0) {
$start = new Carbon($moment);
$end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans(
@ -226,7 +205,7 @@ class TagController extends Controller
}
// prep for current period
if (0 === \strlen($moment)) {
if ('' === $moment) {
/** @var Carbon $start */
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
/** @var Carbon $end */
@ -261,7 +240,7 @@ class TagController extends Controller
$this->repository->store($data);
session()->flash('success', (string)trans('firefly.created_tag', ['tag' => $data['tag']]));
Preferences::mark();
app('preferences')->mark();
if (1 === (int)$request->get('create_another')) {
// @codeCoverageIgnoreStart
@ -286,7 +265,7 @@ class TagController extends Controller
$this->repository->update($tag, $data);
session()->flash('success', (string)trans('firefly.updated_tag', ['tag' => $data['tag']]));
Preferences::mark();
app('preferences')->mark();
if (1 === (int)$request->get('return_to_edit')) {
// @codeCoverageIgnoreStart
@ -308,9 +287,11 @@ class TagController extends Controller
private function getPeriodOverview(Tag $tag): Collection
{
// get first and last tag date from tag:
$range = Preferences::get('viewRange', '1M')->data;
$start = app('navigation')->startOfPeriod($this->repository->firstUseDate($tag), $range);
$end = app('navigation')->startOfPeriod($this->repository->lastUseDate($tag), $range);
$range = app('preferences')->get('viewRange', '1M')->data;
$end = app('navigation')->endOfX($this->repository->lastUseDate($tag), $range, null);
$start = $this->repository->firstUseDate($tag);
// properties for entries with their amounts.
$cache = new CacheProperties;
$cache->addProperty($start);
@ -323,22 +304,23 @@ class TagController extends Controller
}
$collection = new Collection;
$currentEnd = clone $end;
// while end larger or equal to start
while ($end >= $start) {
$currentEnd = app('navigation')->endOfPeriod($end, $range);
while ($currentEnd >= $start) {
$currentStart = app('navigation')->startOfPeriod($currentEnd, $range);
// get expenses and what-not in this period and this tag.
$arr = [
'string' => $end->format('Y-m-d'),
'name' => app('navigation')->periodShow($end, $range),
'name' => app('navigation')->periodShow($currentEnd, $range),
'date' => clone $end,
'spent' => $this->repository->spentInPeriod($tag, $end, $currentEnd),
'earned' => $this->repository->earnedInPeriod($tag, $end, $currentEnd),
'spent' => $this->repository->spentInPeriod($tag, $currentStart, $currentEnd),
'earned' => $this->repository->earnedInPeriod($tag, $currentStart, $currentEnd),
];
$collection->push($arr);
$end = app('navigation')->subtractPeriod($end, $range, 1);
$currentEnd = clone $currentStart;
$currentEnd->subDay();
}
$cache->store($collection);

View File

@ -107,7 +107,7 @@ class LinkController extends Controller
return redirect(route('transactions.show', [$journal->id]));
}
$other = $this->journalRepository->find($linkInfo['transaction_journal_id']);
$other = $this->journalRepository->findNull($linkInfo['transaction_journal_id']);
$alreadyLinked = $this->repository->findLink($journal, $other);
if ($other->id === $journal->id) {

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Http\Controllers\Transaction;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\TransactionViewFilter;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\MassDeleteJournalRequest;
use FireflyIII\Http\Requests\MassEditJournalRequest;
@ -145,22 +146,24 @@ class MassController extends Controller
$collector->setUser(auth()->user());
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setJournals($journals);
$collector->addFilter(NegativeAmountFilter::class);
$transactions = $collector->getJournals();
$collector->addFilter(TransactionViewFilter::class);
$collection = $collector->getJournals();
// add some filters:
// transform to array
$journals = $transactions->map(
$transactions = $collection->map(
function (Transaction $transaction) use ($transformer) {
$result = $transformer->transform($transaction);
return $result;
$transaction= $transformer->transform($transaction);
// make sure amount is positive:
$transaction['amount'] = app('steam')->positive((string)$transaction['amount']);
$transaction['foreign_amount'] = app('steam')->positive((string)$transaction['foreign_amount']);
return $transaction;
}
);
return view('transactions.mass.edit', compact('journals', 'subTitle', 'accounts', 'budgets'));
return view('transactions.mass.edit', compact('transactions', 'subTitle', 'accounts', 'budgets'));
}
/**

View File

@ -29,8 +29,6 @@ use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\SplitJournalFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
@ -100,22 +98,18 @@ class SplitController extends Controller
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal); // @codeCoverageIgnore
}
// basic fields:
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$subTitleIcon = 'fa-pencil';
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$currencies = $this->currencies->get();
// lists and collections
$currencies = $this->currencies->get();
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
// other fields
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
$preFilled = $this->arrayFromJournal($request, $journal);
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$subTitleIcon = 'fa-pencil';
$accountList = $this->accounts->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
$accountArray = [];
// account array to display currency info:
/** @var Account $account */
foreach ($accountList as $account) {
$accountArray[$account->id] = $account;
$accountArray[$account->id]['currency_id'] = (int)$this->accounts->getMetaValue($account, 'currency_id');
}
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('transactions.edit-split.fromUpdate')) {
@ -126,7 +120,7 @@ class SplitController extends Controller
return view(
'transactions.split.edit', compact(
'subTitleIcon', 'currencies', 'optionalFields', 'preFilled', 'subTitle', 'uploadSize', 'budgets',
'journal', 'accountArray'
'journal'
)
);
}
@ -146,8 +140,7 @@ class SplitController extends Controller
// keep current bill:
$data['bill_id'] = $journal->bill_id;
$journal = $this->repository->update($journal, $data);
$journal = $this->repository->update($journal, $data);
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
@ -283,9 +276,9 @@ class SplitController extends Controller
}
// take some info from first transaction, that should at least exist.
$array[$index] = $row;
$array[$index]['currency_id'] = $array[0]['transaction_currency_id'];
$array[$index]['currency_code'] = $array[0]['transaction_currency_code'] ?? '';
$array[$index]['currency_symbol'] = $array[0]['transaction_currency_symbol'] ?? '';
$array[$index]['currency_id'] = $array[0]['currency_id'];
$array[$index]['currency_code'] = $array[0]['currency_code'] ?? '';
$array[$index]['currency_symbol'] = $array[0]['currency_symbol'] ?? '';
$array[$index]['foreign_amount'] = round($array[0]['foreign_destination_amount'] ?? '0', 12);
$array[$index]['foreign_currency_id'] = $array[0]['foreign_currency_id'];
$array[$index]['foreign_currency_code'] = $array[0]['foreign_currency_code'];

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