Merge branch 'develop' into l10n_develop

This commit is contained in:
James Cole 2017-07-15 21:42:27 +02:00 committed by GitHub
commit 2869b92e6b
354 changed files with 8750 additions and 8558 deletions

View File

@ -4,19 +4,19 @@
## Feature requests
If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/).
I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/).
## Bugs
If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary.
First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority!
## Installation problems
Take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them!
Please take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can.
## Pull requests
I can only accept pull requests against the `develop` branch, never the `master` branch.
When contributing to Firefly III, please first discuss the change you wish to make via issue, email, or any other method. I can only accept pull requests against the `develop` branch, never the `master` branch.
## Translations :us: :fr: :de:

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ Homestead.json
Homestead.yaml
.env
public/google*.html
report.html

View File

@ -16,7 +16,7 @@ install:
- php artisan optimize
- php artisan env
- cp .env.testing .env
- mv storage/database/databasecopy.sqlite storage/database/database.sqlite
- wget -q https://github.com/firefly-iii/test-data/raw/master/storage/database.sqlite -O storage/database/database.sqlite
- mkdir -p build/logs
script:

View File

@ -2,6 +2,44 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.6.2] - 2017-07-08
### Added
- Links added to boxes, idea by @simonsmiley
### Fixed
- Various bugs in import routine
## [4.6.1] - 2017-07-02
### Fixed
- Fixed several small issues all around.
## [4.6.0] - 2017-06-28
### Changed
- Revamped import routine. Will be buggy.
### Fixed
- Issue #667, postgresql reported by @skibbipl.
- Issue #680 by @Xeli
- Fixed #660
- Fixes #672, reported by @dzaikos
- Translation error fixed by
- Fix a bug where the balance routine forgot to account for accounts without a currency preference.
- Various other bugfixes.
## [4.5.0] - 2017-06-07
### Added
- Better support for multi-currency transactions and display of transactions, accounts and everything. This requires a database overhaul (moving the currency information to specific transactions) so be careful when upgrading.
- Translations for Spanish and Slovenian.
- New interface for budget page, ~~stolen from~~ inspired by YNAB.
- Expanded Docker to work with postgresql as well, thanks to @kressh
### Fixed
- PostgreSQL support in database upgrade routine (#644, reported by @skibbipl)
- Frontpage budget chart was off, fix by @nhaarman
- Was not possible to remove opening balance.
## [4.4.3] - 2017-05-03
### Added
- Added support for Slovenian

46
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at thegrumpydictator@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -11,13 +11,14 @@ RUN apt-get update -y && \
libtidy-dev \
libxml2-dev \
libsqlite3-dev \
libpq-dev \
libbz2-dev \
gettext-base \
locales && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2 pdo_pgsql
# Generate locales supported by firefly
RUN echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8" > /etc/locale.gen && locale-gen

View File

@ -12,11 +12,9 @@
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/firefly-iii/firefly-iii/tree/master)
Firefly III can be run on Heroku. Register for a free Heroku account and instantly run Firefly III on your very own cloud instance.
Firefly III can be run on Heroku. Register for a free Heroku account and instantly run Firefly III on your very own cloud instance. There is also a [demo site](https://firefly-iii.nder.be) with an example financial administration already present.
There is also a [demo site](https://firefly-iii.nder.be) with an example financial administration already present.
## Installation
## Getting started
To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://firefly-iii.github.io/using-installing.html).
@ -26,7 +24,7 @@ Personal financial management is pretty difficult, and everybody has their own a
Firefly works on the principle that if you know where you're money is going, you can stop it from going there.
#### Some advantages of using Firefly
### Some advantages of using Firefly
- Firefly can import any CSV file, so migrating from other systems is easy.
- Firefly runs on your own server, so you are fully in control of your data. Remember, there is no such thing as "the cloud", its just somebody elses computer!
@ -35,6 +33,25 @@ Firefly works on the principle that if you know where you're money is going, you
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/).
### Contributing
Please read [CONTRIBUTING.md](https://github.com/firefly-iii/firefly-iii/blob/master/.github/CONTRIBUTING.md) for details on contributing, and the process for submitting pull requests. Please check out the [code of conduct](https://github.com/firefly-iii/firefly-iii/blob/master/CODE_OF_CONDUCT.md) as well.
### Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see [the tags](https://github.com/firefly-iii/firefly-iii/tags) on this repository.
### Authors
* James Cole
* Over time, [many people have contributed to Firefly III](https://github.com/firefly-iii/firefly-iii/graphs/contributors).
### License
This work [is licensed](https://github.com/firefly-iii/firefly-iii/blob/master/LICENSE) under a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/).
### Other stuff
If you like Firefly and if it helps you save lots of money, why not send me [a dime for every dollar saved](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) (this is a joke, although the Paypal form works just fine, try it!)
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).

View File

@ -14,10 +14,14 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Artisan;
use FireflyIII\Import\Logging\CommandHandler;
use FireflyIII\Import\Routine\ImportRoutine;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command;
use Illuminate\Support\MessageBag;
use Log;
use Monolog\Formatter\LineFormatter;
/**
* Class CreateImport
@ -73,30 +77,55 @@ class CreateImport extends Command
return;
}
$this->info(sprintf('Going to create a job to import file: %s', $file));
$this->info(sprintf('Using configuration file: %s', $configuration));
$this->info(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
$this->info(sprintf('Type of import: %s', $type));
$this->line(sprintf('Going to create a job to import file: %s', $file));
$this->line(sprintf('Using configuration file: %s', $configuration));
$this->line(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
$this->line(sprintf('Type of import: %s', $type));
/** @var ImportJobRepositoryInterface $jobRepository */
$jobRepository = app(ImportJobRepositoryInterface::class);
$jobRepository->setUser($user);
$job = $jobRepository->create($type);
$this->line(sprintf('Created job "%s"...', $job->key));
$this->line(sprintf('Created job "%s"', $job->key));
Artisan::call('firefly:encrypt-file', ['file' => $file, 'key' => $job->key]);
$this->line('Stored import data...');
$job->configuration = $configurationData;
$job->status = 'settings_complete';
$job->status = 'configured';
$job->save();
$this->line('Stored configuration...');
if ($this->option('start') === true) {
$this->line('The import will start in a moment. This process is not visible...');
Log::debug('Go for import!');
Artisan::call('firefly:start-import', ['key' => $job->key]);
$this->line('Done!');
// normally would refer to other firefly:start-import but that doesn't seem to work all to well...
$monolog = Log::getMonolog();
$handler = new CommandHandler($this);
$formatter = new LineFormatter(null, null, false, true);
$handler->setFormatter($formatter);
$monolog->pushHandler($handler);
// start the actual routine:
/** @var ImportRoutine $routine */
$routine = app(ImportRoutine::class);
$routine->setJob($job);
$routine->run();
// give feedback.
/** @var MessageBag $error */
foreach ($routine->errors as $index => $error) {
$this->error(sprintf('Error importing line #%d: %s', $index, $error));
}
$this->line(
sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->journals->count(), $routine->lines)
);
}
return;

View File

@ -13,12 +13,11 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Import\ImportProcedure;
use FireflyIII\Import\Logging\CommandHandler;
use FireflyIII\Import\Routine\ImportRoutine;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use Log;
/**
@ -75,15 +74,18 @@ class Import extends Command
$monolog = Log::getMonolog();
$handler = new CommandHandler($this);
$monolog->pushHandler($handler);
$importProcedure = new ImportProcedure;
$result = $importProcedure->runImport($job);
// display result to user:
$this->presentResults($result);
$this->line('The import has completed.');
/** @var ImportRoutine $routine */
$routine = app(ImportRoutine::class);
$routine->setJob($job);
$routine->run();
// get any errors from the importer:
$this->presentErrors($job);
/** @var MessageBag $error */
foreach ($routine->errors as $index => $error) {
$this->error(sprintf('Error importing line #%d: %s', $index, $error));
}
$this->line(sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->journals->count(), $routine->lines));
return;
}
@ -96,12 +98,14 @@ class Import extends Command
private function isValid(ImportJob $job): bool
{
if (is_null($job)) {
Log::error('This job does not seem to exist.');
$this->error('This job does not seem to exist.');
return false;
}
if ($job->status != 'settings_complete') {
if ($job->status !== 'configured') {
Log::error(sprintf('This job is not ready to be imported (status is %s).', $job->status));
$this->error('This job is not ready to be imported.');
return false;
@ -109,36 +113,4 @@ class Import extends Command
return true;
}
/**
* @param ImportJob $job
*/
private function presentErrors(ImportJob $job)
{
$extendedStatus = $job->extended_status;
if (isset($extendedStatus['errors']) && count($extendedStatus['errors']) > 0) {
$this->line(sprintf('The following %d error(s) occured during the import:', count($extendedStatus['errors'])));
foreach ($extendedStatus['errors'] as $error) {
$this->error($error);
}
}
}
/**
* @param Collection $result
*/
private function presentResults(Collection $result)
{
/**
* @var int $index
* @var TransactionJournal $journal
*/
foreach ($result as $index => $journal) {
if (!is_null($journal->id)) {
$this->line(sprintf('Line #%d has been imported as transaction #%d.', $index, $journal->id));
continue;
}
$this->error(sprintf('Could not store line #%d', $index));
}
}
}

View File

@ -32,6 +32,7 @@ use Illuminate\Database\QueryException;
use Log;
use Preferences;
use Schema;
use Steam;
/**
* Class UpgradeDatabase
@ -72,9 +73,54 @@ class UpgradeDatabase extends Command
$this->repairPiggyBanks();
$this->updateAccountCurrencies();
$this->updateJournalCurrencies();
$this->currencyInfoToTransactions();
$this->verifyCurrencyInfo();
$this->info('Firefly III database is up to date.');
}
/**
* Moves the currency id info to the transaction instead of the journal.
*/
private function currencyInfoToTransactions()
{
$count = 0;
$set = TransactionJournal::with('transactions')->get();
/** @var TransactionJournal $journal */
foreach ($set as $journal) {
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
if (is_null($transaction->transaction_currency_id)) {
$transaction->transaction_currency_id = $journal->transaction_currency_id;
$transaction->save();
$count++;
}
}
// read and use the foreign amounts when present.
if ($journal->hasMeta('foreign_amount')) {
$amount = Steam::positive($journal->getMeta('foreign_amount'));
// update both transactions:
foreach ($journal->transactions as $transaction) {
$transaction->foreign_amount = $amount;
if (bccomp($transaction->amount, '0') === -1) {
// update with negative amount:
$transaction->foreign_amount = bcmul($amount, '-1');
}
// set foreign currency id:
$transaction->foreign_currency_id = intval($journal->getMeta('foreign_currency_id'));
$transaction->save();
}
$journal->deleteMeta('foreign_amount');
$journal->deleteMeta('foreign_currency_id');
}
}
$this->line(sprintf('Updated currency information for %d transactions', $count));
}
/**
* Migrate budget repetitions to new format.
*/
@ -269,9 +315,11 @@ class UpgradeDatabase extends Command
$repository = app(CurrencyRepositoryInterface::class);
$notification = '%s #%d uses %s but should use %s. It has been updated. Please verify this in Firefly III.';
$transfer = 'Transfer #%d has been updated to use the correct currencies. Please verify this in Firefly III.';
$driver = DB::connection()->getDriverName();
$pgsql = ['pgsql', 'postgresql'];
foreach ($types as $type => $operator) {
$set = TransactionJournal
$query = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')->leftJoin(
'transactions', function (JoinClause $join) use ($operator) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', $operator, '0');
@ -280,9 +328,15 @@ class UpgradeDatabase extends Command
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('transaction_types.type', $type)
->where('account_meta.name', 'currency_id')
->where('transaction_journals.transaction_currency_id', '!=', DB::raw('account_meta.data'))
->get(['transaction_journals.*', 'account_meta.data as expected_currency_id', 'transactions.amount as transaction_amount']);
->where('account_meta.name', 'currency_id');
if (in_array($driver, $pgsql)) {
$query->where('transaction_journals.transaction_currency_id', '!=', DB::raw('cast(account_meta.data as int)'));
}
if (!in_array($driver, $pgsql)) {
$query->where('transaction_journals.transaction_currency_id', '!=', DB::raw('account_meta.data'));
}
$set = $query->get(['transaction_journals.*', 'account_meta.data as expected_currency_id', 'transactions.amount as transaction_amount']);
/** @var TransactionJournal $journal */
foreach ($set as $journal) {
$expectedCurrency = $repository->find(intval($journal->expected_currency_id));
@ -334,4 +388,25 @@ class UpgradeDatabase extends Command
}
}
}
/**
*
*/
private function verifyCurrencyInfo()
{
$count = 0;
$transactions = Transaction::get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$currencyId = intval($transaction->transaction_currency_id);
$foreignId = intval($transaction->foreign_currency_id);
if ($currencyId === $foreignId) {
$transaction->foreign_currency_id = null;
$transaction->foreign_amount = null;
$transaction->save();
$count++;
}
}
$this->line(sprintf('Updated currency information for %d transactions', $count));
}
}

View File

@ -50,10 +50,10 @@ class UpgradeFireflyInstructions extends Command
public function handle()
{
if ($this->argument('task') == 'update') {
if ($this->argument('task') === 'update') {
$this->updateInstructions();
}
if ($this->argument('task') == 'install') {
if ($this->argument('task') === 'install') {
$this->installInstructions();
}
}

View File

@ -259,7 +259,7 @@ class VerifyDatabase extends Command
{
$plural = str_plural($name);
$class = sprintf('FireflyIII\Models\%s', ucfirst($name));
$field = $name == 'tag' ? 'tag' : 'name';
$field = $name === 'tag' ? 'tag' : 'name';
$set = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id')
->leftJoin('users', $plural . '.user_id', '=', 'users.id')
->distinct()

View File

@ -26,9 +26,9 @@ class StoredTransactionJournal extends Event
use SerializesModels;
/** @var TransactionJournal */
/** @var TransactionJournal */
public $journal;
/** @var int */
/** @var int */
public $piggyBankId;
/**

View File

@ -26,7 +26,7 @@ class UpdatedTransactionJournal extends Event
use SerializesModels;
/** @var TransactionJournal */
/** @var TransactionJournal */
public $journal;
/**

View File

@ -303,7 +303,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id')
->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id')
->leftJoin('transaction_currencies', 'transactions.transaction_currency_id', '=', 'transaction_currencies.id')
->whereIn('transactions.account_id', $accountIds)
->where('transaction_journals.user_id', $this->job->user_id)
->where('transaction_journals.date', '>=', $this->start->format('Y-m-d'))
@ -338,7 +338,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
'transaction_journals.encrypted as journal_encrypted',
'transaction_journals.transaction_type_id',
'transaction_types.type as transaction_type',
'transaction_journals.transaction_currency_id',
'transactions.transaction_currency_id',
'transaction_currencies.code AS transaction_currency_code',
]

View File

@ -88,7 +88,7 @@ final class Entry
$entry->budget_name = $object->budget_name ?? '';
// update description when transaction description is different:
if (!is_null($object->description) && $object->description != $entry->description) {
if (!is_null($object->description) && $object->description !== $entry->description) {
$entry->description = $entry->description . ' (' . $object->description . ')';
}

View File

@ -110,9 +110,16 @@ class ChartJsGenerator implements GeneratorInterface
];
// sort by value, keep keys.
// different sort when values are positive and when they're negative.
asort($data);
$next = next($data);
if (!is_bool($next) && bccomp($next, '0') === 1) {
// next is positive, sort other way around.
arsort($data);
}
unset($next);
$index = 0;
$index = 0;
foreach ($data as $key => $value) {
// make larger than 0

View File

@ -22,10 +22,15 @@ interface GeneratorInterface
{
/**
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
* Will generate a Chart JS compatible array from the given input. Expects this format
*
* Will take labels for all from first set.
*
* 0: [
* 'label' => 'label of set',
* 'type' => bar or line, optional
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
* 'fill' => if to fill a line? optional, will not be included when unused.
* 'entries' =>
* [
* 'label-of-entry' => 'value'
@ -33,12 +38,16 @@ interface GeneratorInterface
* ]
* 1: [
* 'label' => 'label of another set',
* 'type' => bar or line, optional
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
* 'fill' => if to fill a line? optional, will not be included when unused.
* 'entries' =>
* [
* 'label-of-entry' => 'value'
* ]
* ]
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
*
* @param array $data
*

View File

@ -19,6 +19,7 @@ use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection;
use Steam;
@ -147,6 +148,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
*/
private function getAuditReport(Account $account, Carbon $date): array
{
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
@ -155,15 +158,21 @@ class MonthReportGenerator implements ReportGeneratorInterface
$journals = $journals->reverse();
$dayBeforeBalance = Steam::balance($account, $date);
$startBalance = $dayBeforeBalance;
$currency = $currencyRepos->find(intval($account->getMeta('currency_id')));
/** @var Transaction $journal */
foreach ($journals as $transaction) {
$transaction->before = $startBalance;
$transactionAmount = $transaction->transaction_amount;
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
if ($currency->id === $transaction->foreign_currency_id) {
$transactionAmount = $transaction->transaction_foreign_amount;
}
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
$transaction->currency = $currency;
}
/*

View File

@ -15,7 +15,6 @@ namespace FireflyIII\Generator\Report;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;
use Log;
/**

View File

@ -14,6 +14,7 @@ namespace FireflyIII\Generator\Report\Tag;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
@ -30,7 +31,7 @@ use Log;
*
* @package FireflyIII\Generator\Report\Tag
*/
class MonthReportGenerator implements ReportGeneratorInterface
class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
/** @var Collection */

View File

@ -42,6 +42,10 @@ class StoredJournalEventHandler
/**
* StoredJournalEventHandler constructor.
*
* @param PRI $repository
* @param JRI $journalRepository
* @param RGRI $ruleGroupRepository
*/
public function __construct(PRI $repository, JRI $journalRepository, RGRI $ruleGroupRepository)
{

View File

@ -35,6 +35,8 @@ class UpdatedJournalEventHandler
/**
* StoredJournalEventHandler constructor.
*
* @param RuleGroupRepositoryInterface $ruleGroupRepository
*/
public function __construct(RuleGroupRepositoryInterface $ruleGroupRepository)
{
@ -52,7 +54,7 @@ class UpdatedJournalEventHandler
{
// get all the user's rule groups, with the rules, order by 'order'.
$journal = $updatedJournalEvent->journal;
$groups = $this->repository->getActiveGroups($journal->user);
$groups = $this->repository->getActiveGroups($journal->user);
/** @var RuleGroup $group */
foreach ($groups as $group) {

View File

@ -74,6 +74,7 @@ class UserEventHandler
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
// @codeCoverageIgnoreEnd
return true;
@ -96,16 +97,17 @@ class UserEventHandler
}
// get the email address
$email = $event->user->email;
$address = route('index');
$uri = route('index');
$ipAddress = $event->ipAddress;
// send email.
try {
Mail::to($email)->send(new RegisteredUserMail($address, $ipAddress));
Mail::to($email)->send(new RegisteredUserMail($uri, $ipAddress));
// @codeCoverageIgnoreStart
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
// @codeCoverageIgnoreEnd
return true;

View File

@ -18,9 +18,10 @@ use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use Log;
use Storage;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Log;
/**
* Class AttachmentHelper
*
@ -157,11 +158,14 @@ class AttachmentHelper implements AttachmentHelperInterface
$attachment->size = $file->getSize();
$attachment->uploaded = 0;
$attachment->save();
Log::debug('Created attachment:', $attachment->toArray());
$fileObject = $file->openFile('r');
$fileObject->rewind();
$content = $fileObject->fread($file->getSize());
$encrypted = Crypt::encrypt($content);
Log::debug(sprintf('Full file length is %d and upload size is %d.', strlen($content), $file->getSize()));
Log::debug(sprintf('Encrypted content is %d', strlen($encrypted)));
// store it:
$this->uploadDisk->put($attachment->fileName(), $encrypted);
@ -202,6 +206,7 @@ class AttachmentHelper implements AttachmentHelperInterface
/**
* @codeCoverageIgnore
*
* @param UploadedFile $file
*
* @return bool

View File

@ -147,13 +147,13 @@ class BalanceLine
if ($this->getBudget() instanceof BudgetModel && !is_null($this->getBudget()->id)) {
return $this->getBudget()->name;
}
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
if ($this->getRole() === self::ROLE_DEFAULTROLE) {
return strval(trans('firefly.no_budget'));
}
if ($this->getRole() == self::ROLE_TAGROLE) {
if ($this->getRole() === self::ROLE_TAGROLE) {
return strval(trans('firefly.coveredWithTags'));
}
if ($this->getRole() == self::ROLE_DIFFROLE) {
if ($this->getRole() === self::ROLE_DIFFROLE) {
return strval(trans('firefly.leftUnbalanced'));
}

View File

@ -96,7 +96,7 @@ class Bill
{
$set = $this->bills->sortBy(
function (BillLine $bill) {
$active = intval($bill->getBill()->active) == 0 ? 1 : 0;
$active = intval($bill->getBill()->active) === 0 ? 1 : 0;
$name = $bill->getBill()->name;
return $active . $name;

View File

@ -60,17 +60,30 @@ class JournalCollector implements JournalCollectorInterface
'transaction_journals.description',
'transaction_journals.date',
'transaction_journals.encrypted',
'transaction_currencies.code as transaction_currency_code',
'transaction_types.type as transaction_type_type',
'transaction_journals.bill_id',
'bills.name as bill_name',
'bills.name_encrypted as bill_name_encrypted',
'transactions.id as id',
'transactions.amount as transaction_amount',
'transactions.description as transaction_description',
'transactions.account_id',
'transactions.identifier',
'transactions.transaction_journal_id',
'transactions.amount as transaction_amount',
'transactions.transaction_currency_id as transaction_currency_id',
'transaction_currencies.code as transaction_currency_code',
'transaction_currencies.symbol as transaction_currency_symbol',
'transaction_currencies.decimal_places as transaction_currency_dp',
'transactions.foreign_amount as transaction_foreign_amount',
'transactions.foreign_currency_id as foreign_currency_id',
'foreign_currencies.code as foreign_currency_code',
'foreign_currencies.symbol as foreign_currency_symbol',
'foreign_currencies.decimal_places as foreign_currency_dp',
'accounts.name as account_name',
'accounts.encrypted as account_encrypted',
'account_types.type as account_type',
@ -484,17 +497,19 @@ class JournalCollector implements JournalCollectorInterface
Log::debug('journalCollector::startQuery');
/** @var EloquentBuilder $query */
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transactions.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', 'transactions.foreign_currency_id')
->whereNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at')
->where('transaction_journals.user_id', $this->user->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC');
$this->query = $query;

View File

@ -26,7 +26,7 @@ use Log;
*/
class InternalTransferFilter implements FilterInterface
{
/** @var array */
/** @var array */
private $accounts = [];
/**

View File

@ -244,7 +244,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface
foreach ($accounts as $account) {
$leftEntry = $tagsLeft->filter(
function (Tag $tag) use ($account) {
return $tag->account_id == $account->id;
return $tag->account_id === $account->id;
}
);
$left = '0';

View File

@ -56,7 +56,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface
/** @var Budget $budget */
foreach ($set as $budget) {
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget
if ($budgetLimits->count() === 0) { // no budget limit(s) for this budget
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range
if (bccomp($spent, '0') === -1) {

View File

@ -187,7 +187,7 @@ class PopupReport implements PopupReportInterface
$journals = $journals->filter(
function (Transaction $transaction) use ($report) {
// get the destinations:
$destinations = $transaction->destinationAccountList($transaction->transactionJournal)->pluck('id')->toArray();
$destinations = $transaction->transactionJournal->destinationAccountList()->pluck('id')->toArray();
// do these intersect with the current list?
return !empty(array_intersect($report, $destinations));

View File

@ -219,8 +219,8 @@ class AccountController extends Controller
$start->subDay();
$ids = $accounts->pluck('id')->toArray();
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$startBalances = Steam::balancesByAccounts($accounts, $start);
$endBalances = Steam::balancesByAccounts($accounts, $end);
$activities = Steam::getLastActivities($ids);
$accounts->each(
@ -293,8 +293,8 @@ class AccountController extends Controller
$periods = $this->getPeriodOverview($account);
}
$count = 0;
$loop = 0;
$count = 0;
$loop = 0;
// grab journals, but be prepared to jump a period back to get the right ones:
Log::info('Now at loop start.');
while ($count === 0 && $loop < 3) {
@ -308,7 +308,7 @@ class AccountController extends Controller
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id . '/' . $moment);
$count = $journals->getCollection()->count();
if ($count === 0) {
if ($count === 0 && $loop < 3) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
@ -316,7 +316,7 @@ class AccountController extends Controller
}
}
if ($moment != 'all' && $loop > 1) {
if ($moment !== 'all' && $loop > 1) {
$subTitle = trans(
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
'end' => $end->formatLocalized($this->monthAndDayFormat)]

View File

@ -98,10 +98,13 @@ class AttachmentController extends Controller
*/
public function download(AttachmentRepositoryInterface $repository, Attachment $attachment)
{
if ($repository->exists($attachment)) {
$content = $repository->getContent($attachment);
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
/** @var LaravelResponse $response */
$response = response($content, 200);
$response
@ -149,7 +152,8 @@ class AttachmentController extends Controller
{
$image = 'images/page_green.png';
if ($attachment->mime == 'application/pdf') {
if ($attachment->mime === 'application/pdf') {
$image = 'images/page_white_acrobat.png';
}
$file = public_path($image);

View File

@ -175,7 +175,7 @@ class BillController extends Controller
*/
public function rescan(Request $request, BillRepositoryInterface $repository, Bill $bill)
{
if (intval($bill->active) == 0) {
if (intval($bill->active) === 0) {
$request->session()->flash('warning', strval(trans('firefly.cannot_scan_inactive_bill')));
return redirect(URL::previous());
@ -206,7 +206,7 @@ class BillController extends Controller
/** @var Carbon $date */
$date = session('start');
$year = $date->year;
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$yearAverage = $repository->getYearAverage($bill, $date);
$overallAverage = $repository->getOverallAverage($bill);

View File

@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers;
use Amount;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\BudgetFormRequest;
@ -80,7 +81,7 @@ class BudgetController extends Controller
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
if ($amount == 0) {
if ($amount === 0) {
$budgetLimit = null;
}
Preferences::mark();
@ -166,16 +167,38 @@ class BudgetController extends Controller
}
/**
* @param string|null $moment
*
* @return View
*/
public function index()
public function index(string $moment = null)
{
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', new Carbon);
$end = session('end', new Carbon);
// make date if present:
if (!is_null($moment) || strlen(strval($moment)) !== 0) {
try {
$start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range);
} catch (Exception $e) {
// start and end are already defined.
}
}
$next = clone $end;
$next->addDay();
$prev = clone $start;
$prev->subDay();
$prev = Navigation::startOfPeriod($prev, $range);
$this->repository->cleanupBudgets();
$budgets = $this->repository->getActiveBudgets();
$inactive = $this->repository->getInactiveBudgets();
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
$budgetInformation = $this->collectBudgetInformation($budgets, $start, $end);
@ -184,9 +207,44 @@ class BudgetController extends Controller
$spent = array_sum(array_column($budgetInformation, 'spent'));
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
// select thing for last 12 periods:
$previousLoop = [];
$previousDate = clone $start;
$count = 0;
while ($count < 12) {
$previousDate->subDay();
$previousDate = Navigation::startOfPeriod($previousDate, $range);
$format = $previousDate->format('Y-m-d');
$previousLoop[$format] = Navigation::periodShow($previousDate, $range);
$count++;
}
// select thing for next 12 periods:
$nextLoop = [];
$nextDate = clone $end;
$nextDate->addDay();
$count = 0;
while ($count < 12) {
$format = $nextDate->format('Y-m-d');
$nextLoop[$format] = Navigation::periodShow($nextDate, $range);
$nextDate = Navigation::endOfPeriod($nextDate, $range);
$count++;
$nextDate->addDay();
}
// display info
$currentMonth = Navigation::periodShow($start, $range);
$nextText = Navigation::periodShow($next, $range);
$prevText = Navigation::periodShow($prev, $range);
return view(
'budgets.index',
compact('available', 'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets', 'spent', 'budgeted')
compact(
'available', 'currentMonth', 'next', 'nextText', 'prev', 'prevText',
'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets',
'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start'
)
);
}
@ -235,7 +293,7 @@ class BudgetController extends Controller
);
}
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
@ -244,7 +302,7 @@ class BudgetController extends Controller
Log::info('Now at no-budget loop start.');
while ($count === 0 && $loop < 3) {
$loop++;
Log::info('Count is zero, search for journals.');
Log::info(sprintf('Count is zero, search for journals between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d')));
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page)
@ -252,7 +310,7 @@ class BudgetController extends Controller
$journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/list/no-budget');
$count = $journals->getCollection()->count();
if ($count === 0) {
if ($count === 0 && $loop < 3) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
@ -260,7 +318,7 @@ class BudgetController extends Controller
}
}
if ($moment != 'all' && $loop > 1) {
if ($moment !== 'all' && $loop > 1) {
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@ -299,7 +357,7 @@ class BudgetController extends Controller
/** @var Carbon $start */
$start = session('first', Carbon::create()->startOfYear());
$end = new Carbon;
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$limits = $this->getLimits($budget, $start, $end);
$repetition = null;
@ -326,11 +384,11 @@ class BudgetController extends Controller
*/
public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit)
{
if ($budgetLimit->budget->id != $budget->id) {
if ($budgetLimit->budget->id !== $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = trans(
'firefly.budget_in_period', [

View File

@ -199,7 +199,7 @@ class CategoryController extends Controller
);
}
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
@ -216,7 +216,7 @@ class CategoryController extends Controller
$journals = $collector->getPaginatedJournals();
$journals->setPath('/categories/list/no-category');
$count = $journals->getCollection()->count();
if ($count === 0) {
if ($count === 0 && $loop < 3) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
@ -224,7 +224,7 @@ class CategoryController extends Controller
}
}
if ($moment != 'all' && $loop > 1) {
if ($moment !== 'all' && $loop > 1) {
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@ -247,7 +247,7 @@ class CategoryController extends Controller
// default values:
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
$loop = 0;
@ -300,7 +300,7 @@ class CategoryController extends Controller
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id);
$count = $journals->getCollection()->count();
if ($count === 0) {
if ($count === 0 && $loop < 3) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
@ -308,7 +308,7 @@ class CategoryController extends Controller
}
}
if ($moment != 'all' && $loop > 1) {
if ($moment !== 'all' && $loop > 1) {
$subTitle = trans(
'firefly.journals_in_period_for_category',
['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
@ -463,7 +463,7 @@ class CategoryController extends Controller
$accountRepository = app(AccountRepositoryInterface::class);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$first = $repository->firstUseDate($category);
if ($first->year == 1900) {
if ($first->year === 1900) {
$first = new Carbon;
}
$range = Preferences::get('viewRange', '1M')->data;

View File

@ -14,7 +14,6 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
@ -115,9 +114,8 @@ class AccountController extends Controller
$start->subDay();
$accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
$ids = $accounts->pluck('id')->toArray();
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$startBalances = Steam::balancesByAccounts($accounts, $start);
$endBalances = Steam::balancesByAccounts($accounts, $end);
$chartData = [];
foreach ($accounts as $account) {
@ -129,6 +127,7 @@ class AccountController extends Controller
$chartData[$account->name] = $diff;
}
}
arsort($chartData);
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
$cache->store($data);
@ -336,7 +335,7 @@ class AccountController extends Controller
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $start
*
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
@ -411,9 +410,8 @@ class AccountController extends Controller
$accounts = $repository->getAccountsByType([AccountType::REVENUE]);
$start->subDay();
$ids = $accounts->pluck('id')->toArray();
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$startBalances = Steam::balancesByAccounts($accounts, $start);
$endBalances = Steam::balancesByAccounts($accounts, $end);
foreach ($accounts as $account) {
$id = $account->id;
@ -427,7 +425,7 @@ class AccountController extends Controller
}
arsort($chartData);
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
$data = $this->generator->singleSet(strval(trans('firefly.earned')), $chartData);
$cache->store($data);
return Response::json($data);

View File

@ -31,6 +31,7 @@ use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Steam;
/**
* Class BudgetController
@ -123,7 +124,7 @@ class BudgetController extends Controller
*/
public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit)
{
if ($budgetLimit->budget->id != $budget->id) {
if ($budgetLimit->budget->id !== $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
@ -320,12 +321,12 @@ class BudgetController extends Controller
['label' => strval(trans('firefly.overspent')), 'entries' => [], 'type' => 'bar',],
];
/** @var Budget $budget */
foreach ($budgets as $budget) {
// get relevant repetitions:
$limits = $this->repository->getBudgetLimits($budget, $start, $end);
$expenses = $this->getExpensesForBudget($limits, $budget, $start, $end);
foreach ($expenses as $name => $row) {
$chartData[0]['entries'][$name] = $row['spent'];
$chartData[1]['entries'][$name] = $row['left'];
@ -529,9 +530,7 @@ class BudgetController extends Controller
$rows = $this->spentInPeriodMulti($budget, $limits);
foreach ($rows as $name => $row) {
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['left'], '0') !== 0) {
$return[$name]['spent'] = bcmul($row['spent'], '-1');
$return[$name]['left'] = $row['left'];
$return[$name]['overspent'] = bcmul($row['overspent'], '-1');
$return[$name] = $row;
}
}
unset($rows, $row);
@ -563,6 +562,7 @@ class BudgetController extends Controller
/** @var BudgetLimit $budgetLimit */
foreach ($limits as $budgetLimit) {
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date);
$expenses = Steam::positive($expenses);
if ($limits->count() > 1) {
$name = $budget->name . ' ' . trans(
@ -578,10 +578,14 @@ class BudgetController extends Controller
* left: amount of budget limit min spent, or 0 when < 0.
* spent: spent, or amount of budget limit when > amount
*/
$amount = $budgetLimit->amount;
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
$spent = bccomp($expenses, $amount) === 1 ? $expenses : bcmul($amount, '-1');
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
$amount = $budgetLimit->amount;
$leftInLimit = bcsub($amount, $expenses);
$hasOverspent = bccomp($leftInLimit, '0') === -1;
$left = $hasOverspent ? '0' : bcsub($amount, $expenses);
$spent = $hasOverspent ? $amount : $expenses;
$overspent = $hasOverspent ? Steam::positive($leftInLimit) : '0';
$return[$name] = [
'left' => $left,
'overspent' => $overspent,

View File

@ -67,7 +67,7 @@ class CategoryController extends Controller
$start = $repository->firstUseDate($category);
if ($start->year == 1900) {
if ($start->year === 1900) {
$start = new Carbon;
}
@ -277,10 +277,10 @@ class CategoryController extends Controller
*/
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, Carbon $date)
{
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($date, $range);
$end = Navigation::endOfPeriod($date, $range);
$data = $this->makePeriodChart($repository, $category, $start, $end);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($date, $range);
$end = Navigation::endOfPeriod($date, $range);
$data = $this->makePeriodChart($repository, $category, $start, $end);
return Response::json($data);
}
@ -336,9 +336,9 @@ class CategoryController extends Controller
$sum = bcadd($spent, $earned);
$label = trim(Navigation::periodShow($start, '1D'));
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'),12);
$chartData[1]['entries'][$label] = round($earned,12);
$chartData[2]['entries'][$label] = round($sum,12);
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
$chartData[1]['entries'][$label] = round($earned, 12);
$chartData[2]['entries'][$label] = round($sum, 12);
$start->addDay();

View File

@ -16,7 +16,6 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
@ -248,7 +247,7 @@ class CategoryReportController extends Controller
// remove all empty entries to prevent cluttering:
$newSet = [];
foreach ($chartData as $key => $entry) {
if (!array_sum($entry['entries']) == 0) {
if (!array_sum($entry['entries']) === 0) {
$newSet[$key] = $chartData[$key];
}
}

View File

@ -67,11 +67,10 @@ class ReportController extends Controller
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$ids = $accounts->pluck('id')->toArray();
$current = clone $start;
$chartData = [];
while ($current < $end) {
$balances = Steam::balancesById($ids, $current);
$balances = Steam::balancesByAccounts($accounts, $current);
$sum = $this->arraySum($balances);
$label = $current->formatLocalized(strval(trans('config.month_and_day')));
$chartData[$label] = $sum;
@ -104,7 +103,7 @@ class ReportController extends Controller
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
//return Response::json($cache->get()); // @codeCoverageIgnore
return Response::json($cache->get()); // @codeCoverageIgnore
}
Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray());
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
@ -250,7 +249,7 @@ class ReportController extends Controller
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
// return $cache->get(); // @codeCoverageIgnore
return $cache->get(); // @codeCoverageIgnore
}
$currentStart = clone $start;

View File

@ -231,7 +231,7 @@ class TagReportController extends Controller
// remove all empty entries to prevent cluttering:
$newSet = [];
foreach ($chartData as $key => $entry) {
if (!array_sum($entry['entries']) == 0) {
if (!array_sum($entry['entries']) === 0) {
$newSet[$key] = $chartData[$key];
}
}

View File

@ -18,6 +18,7 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
@ -25,6 +26,7 @@ use Illuminate\Routing\Controller as BaseController;
use Session;
use URL;
use View;
use Route;
/**
* Class Controller
@ -63,6 +65,12 @@ class Controller extends BaseController
$this->monthAndDayFormat = (string)trans('config.month_and_day');
$this->dateTimeFormat = (string)trans('config.date_time');
// get shown-intro-preference:
$key = 'shown_demo_' . Route::currentRouteName();
$shownDemo = Preferences::get($key, false)->data;
View::share('shownDemo', $shownDemo);
View::share('current_route_name', Route::currentRouteName());
return $next($request);
}
);

View File

@ -21,9 +21,11 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Illuminate\Support\Collection;
use Log;
use Preferences;
use Route as RouteFacade;
use Session;
use View;
@ -107,7 +109,7 @@ class HomeController extends Controller
$types = config('firefly.accountTypesByIdentifier.asset');
$count = $repository->count($types);
if ($count == 0) {
if ($count === 0) {
return redirect(route('new-user.index'));
}
@ -120,7 +122,6 @@ class HomeController extends Controller
$start = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
$showTour = Preferences::get('tour', true)->data;
$accounts = $repository->getAccountsById($frontPage->data);
$showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data;
@ -137,10 +138,34 @@ class HomeController extends Controller
}
return view(
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount')
'index', compact('count', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount')
);
}
public function routes()
{
$set = RouteFacade::getRoutes();
$ignore = ['chart.','javascript.','json.','report-data.','popup.','debugbar.'];
/** @var Route $route */
foreach ($set as $route) {
$name = $route->getName();
if (!is_null($name) && in_array('GET', $route->methods()) && strlen($name) > 0) {
$found = false;
foreach ($ignore as $string) {
if (strpos($name, $string) !== false) {
$found = true;
}
}
if (!$found) {
echo 'touch '.$route->getName() . '.md;';
}
}
}
return '&nbsp;';
}
/**
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/

View File

@ -12,32 +12,29 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Requests\ImportUploadRequest;
use FireflyIII\Import\ImportProcedureInterface;
use FireflyIII\Import\Setup\SetupInterface;
use FireflyIII\Import\Configurator\ConfiguratorInterface;
use FireflyIII\Import\Routine\ImportRoutine;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Http\Response as LaravelResponse;
use Log;
use Response;
use Session;
use SplFileObject;
use Storage;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use View;
/**
* Class ImportController
* Class ImportController.
*
* @package FireflyIII\Http\Controllers
*/
class ImportController extends Controller
{
/** @var ImportJobRepositoryInterface */
public $repository;
/**
*
*/
@ -48,7 +45,8 @@ class ImportController extends Controller
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-archive');
View::share('title', trans('firefly.import_data_full'));
View::share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
@ -56,28 +54,7 @@ class ImportController extends Controller
}
/**
* This is the last step before the import starts.
*
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function complete(ImportJob $job)
{
Log::debug('Now in complete()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'complete')) {
return $this->redirectToCorrectStep($job);
}
$subTitle = trans('firefly.import_complete');
$subTitleIcon = 'fa-star';
return view('import.complete', compact('job', 'subTitle', 'subTitleIcon'));
}
/**
* This is step 3.
* This is the first step in configuring the job. It can only be executed
* when the job is set to "import_status_never_started".
* This is step 3. This repeats until the job is configured.
*
* @param ImportJob $job
*
@ -86,37 +63,43 @@ class ImportController extends Controller
*/
public function configure(ImportJob $job)
{
Log::debug('Now at start of configure()');
if (!$this->jobInCorrectStep($job, 'configure')) {
return $this->redirectToCorrectStep($job);
}
// create configuration class:
$configurator = $this->makeConfigurator($job);
// actual code
$importer = $this->makeImporter($job);
$importer->configure();
$data = $importer->getConfigurationData();
$subTitle = trans('firefly.configure_import');
// is the job already configured?
if ($configurator->isJobConfigured()) {
$this->repository->updateStatus($job, 'configured');
return redirect(route('import.status', [$job->key]));
}
$view = $configurator->getNextView();
$data = $configurator->getNextData();
$subTitle = trans('firefly.import_config_bread_crumb');
$subTitleIcon = 'fa-wrench';
return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon'));
return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon'));
}
/**
* Generate a JSON file of the job's config and send it to the user.
* Generate a JSON file of the job's configuration and send it to the user.
*
* @param ImportJob $job
*
* @return mixed
* @return LaravelResponse
*/
public function download(ImportJob $job)
{
Log::debug('Now in download()', ['job' => $job->key]);
$config = $job->configuration;
$config = $job->configuration;
// TODO this is CSV import specific:
$config['column-roles-complete'] = false;
$config['column-mapping-complete'] = false;
$config['initial-config-complete'] = false;
$config['delimiter'] = $config['delimiter'] === "\t" ? 'tab' : $config['delimiter'];
$result = json_encode($config, JSON_PRETTY_PRINT);
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
$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);
@ -134,26 +117,6 @@ class ImportController extends Controller
}
/**
* @param ImportJob $job
*
* @return View
*/
public function finished(ImportJob $job)
{
if (!$this->jobInCorrectStep($job, 'finished')) {
return $this->redirectToCorrectStep($job);
}
// if there is a tag (there might not be), we can link to it:
$tagId = $job->extended_status['importTag'] ?? 0;
$subTitle = trans('firefly.import_finished');
$subTitleIcon = 'fa-star';
return view('import.finished', compact('job', 'subTitle', 'subTitleIcon', 'tagId'));
}
/**
* This is step 1. Upload a file.
*
@ -161,8 +124,7 @@ class ImportController extends Controller
*/
public function index()
{
Log::debug('Now at index');
$subTitle = trans('firefly.import_data_index');
$subTitle = trans('firefly.import_index_sub_title');
$subTitleIcon = 'fa-home';
$importFileTypes = [];
$defaultImportType = config('firefly.default_import_format');
@ -175,42 +137,75 @@ class ImportController extends Controller
}
/**
* This is step 2. It creates an Import Job. Stores the import.
*
* @param ImportUploadRequest $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function initialize(ImportUploadRequest $request)
{
Log::debug('Now in initialize()');
// create import job:
$type = $request->get('import_file_type');
$job = $this->repository->create($type);
Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]);
// process file:
$this->repository->processFile($job, $request->files->get('import_file'));
// process config, if present:
if ($request->files->has('configuration_file')) {
$this->repository->processConfiguration($job, $request->files->get('configuration_file'));
}
$this->repository->updateStatus($job, 'initialized');
return redirect(route('import.configure', [$job->key]));
}
/**
*
* Show status of import job in JSON.
*
* @param ImportJob $job
*
* @return \Illuminate\Http\JsonResponse
*/
public function json(ImportJob $job)
{
$result = [
'showPercentage' => false,
'started' => false,
'finished' => false,
'running' => false,
'errors' => $job->extended_status['errors'],
'percentage' => 0,
'steps' => $job->extended_status['total_steps'],
'stepsDone' => $job->extended_status['steps_done'],
'statusText' => trans('firefly.import_status_' . $job->status),
'finishedText' => '',
$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('firefly.import_status_job_' . $job->status),
'status' => $job->status,
'finishedText' => '',
];
$percentage = 0;
if ($job->extended_status['total_steps'] !== 0) {
$percentage = round(($job->extended_status['steps_done'] / $job->extended_status['total_steps']) * 100, 0);
if ($job->extended_status['steps'] !== 0) {
$result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
$result['show_percentage'] = true;
}
if ($job->status === 'import_complete') {
$tagId = $job->extended_status['importTag'];
if ($job->status === 'finished') {
$tagId = $job->extended_status['tag'];
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$tag = $repository->find($tagId);
$result['finished'] = true;
$result['finishedText'] = trans('firefly.import_finished_link', ['link' => route('tags.show', [$tag->id]), 'tag' => $tag->tag]);
$result['finishedText'] = trans('firefly.import_status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]);
}
if ($job->status === 'import_running') {
$result['started'] = true;
$result['running'] = true;
$result['showPercentage'] = true;
$result['percentage'] = $percentage;
if ($job->status === 'running') {
$result['started'] = true;
$result['running'] = true;
}
return Response::json($result);
@ -219,286 +214,82 @@ class ImportController extends Controller
/**
* Step 4. Save the configuration.
*
* @param Request $request
* @param ImportJobRepositoryInterface $repository
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function postConfigure(Request $request, ImportJobRepositoryInterface $repository, ImportJob $job)
{
Log::debug('Now in postConfigure()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'process')) {
return $this->redirectToCorrectStep($job);
}
Log::debug('Continue postConfigure()', ['job' => $job->key]);
// actual code
$importer = $this->makeImporter($job);
$data = $request->all();
$files = $request->files;
$importer->saveImportConfiguration($data, $files);
// update job:
$repository->updateStatus($job, 'import_configuration_saved');
// return redirect to settings.
// this could loop until the user is done.
return redirect(route('import.settings', [$job->key]));
}
/**
* This step 6. Depending on the importer, this will process the
* settings given and store them.
*
* @param Request $request
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/
public function postSettings(Request $request, ImportJob $job)
public function postConfigure(Request $request, ImportJob $job)
{
Log::debug('Now in postSettings()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'store-settings')) {
return $this->redirectToCorrectStep($job);
}
$importer = $this->makeImporter($job);
$importer->storeSettings($request);
// return redirect to settings (for more settings perhaps)
return redirect(route('import.settings', [$job->key]));
}
/**
* Step 5. Depending on the importer, this will show the user settings to
* fill in.
*
* @param ImportJobRepositoryInterface $repository
* @param ImportJob $job
*
* @return View
*/
public function settings(ImportJobRepositoryInterface $repository, ImportJob $job)
{
Log::debug('Now in settings()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'settings')) {
return $this->redirectToCorrectStep($job);
}
Log::debug('Continue in settings()');
$importer = $this->makeImporter($job);
$subTitle = trans('firefly.settings_for_import');
$subTitleIcon = 'fa-wrench';
// now show settings screen to user.
if ($importer->requireUserSettings()) {
Log::debug('Job requires user config.');
$data = $importer->getDataForSettings();
$view = $importer->getViewForSettings();
return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon'));
}
Log::debug('Job does NOT require user config.');
$repository->updateStatus($job, 'settings_complete');
// if no more settings, save job and continue to process thing.
return redirect(route('import.complete', [$job->key]));
// ask the importer for the requested action.
// for example pick columns or map data.
// depends of course on the data in the job.
}
/**
* @param ImportProcedureInterface $importProcedure
* @param ImportJob $job
*/
public function start(ImportProcedureInterface $importProcedure, ImportJob $job)
{
set_time_limit(0);
if ($job->status == 'settings_complete') {
$importProcedure->runImport($job);
}
}
/**
* This is the last step before the import starts.
*
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function status(ImportJob $job)
{ //
Log::debug('Now in status()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'status')) {
return $this->redirectToCorrectStep($job);
}
$subTitle = trans('firefly.import_status');
$subTitleIcon = 'fa-star';
return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
}
/**
* This is step 2. It creates an Import Job. Stores the import.
*
* @param ImportUploadRequest $request
* @param ImportJobRepositoryInterface $repository
* @param UserRepositoryInterface $userRepository
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository, UserRepositoryInterface $userRepository)
{
Log::debug('Now in upload()');
// create import job:
$type = $request->get('import_file_type');
$job = $repository->create($type);
Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]);
/** @var UploadedFile $upload */
$upload = $request->files->get('import_file');
$newName = $job->key . '.upload';
$uploaded = new SplFileObject($upload->getRealPath());
$content = $uploaded->fread($uploaded->getSize());
$contentEncrypted = Crypt::encrypt($content);
$disk = Storage::disk('upload');
// user is demo user, replace upload with prepared file.
if ($userRepository->hasRole(auth()->user(), 'demo')) {
$stubsDisk = Storage::disk('stubs');
$content = $stubsDisk->get('demo-import.csv');
$contentEncrypted = Crypt::encrypt($content);
$disk->put($newName, $contentEncrypted);
Log::debug('Replaced upload with demo file.');
// also set up prepared configuration.
$configuration = json_decode($stubsDisk->get('demo-configuration.json'), true);
$repository->setConfiguration($job, $configuration);
Log::debug('Set configuration for demo user', $configuration);
// also flash info
Session::flash('info', trans('demo.import-configure-security'));
}
if (!$userRepository->hasRole(auth()->user(), 'demo')) {
// user is not demo, process original upload:
$disk->put($newName, $contentEncrypted);
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
}
// store configuration file's content into the job's configuration thing. Otherwise, leave it empty.
// demo user's configuration upload is ignored completely.
if ($request->files->has('configuration_file') && !auth()->user()->hasRole('demo')) {
/** @var UploadedFile $configFile */
$configFile = $request->files->get('configuration_file');
Log::debug(
'Uploaded configuration file',
['name' => $configFile->getClientOriginalName(), 'size' => $configFile->getSize(), 'mime' => $configFile->getClientMimeType()]
);
$configFileObject = new SplFileObject($configFile->getRealPath());
$configRaw = $configFileObject->fread($configFileObject->getSize());
$configuration = json_decode($configRaw, true);
// @codeCoverageIgnoreStart
if (!is_null($configuration) && is_array($configuration)) {
Log::debug('Found configuration', $configuration);
$repository->setConfiguration($job, $configuration);
}
// @codeCoverageIgnoreEnd
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);
// return to configure
return redirect(route('import.configure', [$job->key]));
}
/**
* @param ImportJob $job
* @param string $method
*
* @return bool
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
*/
private function jobInCorrectStep(ImportJob $job, string $method): bool
public function start(ImportJob $job)
{
Log::debug('Now in jobInCorrectStep()', ['job' => $job->key, 'method' => $method]);
switch ($method) {
case 'configure':
case 'process':
return $job->status === 'import_status_never_started';
case 'settings':
case 'store-settings':
Log::debug(sprintf('Job %d with key %s has status %s', $job->id, $job->key, $job->status));
return $job->status === 'import_configuration_saved';
case 'finished':
return $job->status === 'import_complete';
case 'complete':
return $job->status === 'settings_complete';
case 'status':
return ($job->status === 'settings_complete') || ($job->status === 'import_running');
/** @var ImportRoutine $routine */
$routine = app(ImportRoutine::class);
$routine->setJob($job);
$result = $routine->run();
if ($result) {
return Response::json(['run' => 'ok']);
}
return false; // @codeCoverageIgnore
throw new FireflyException('Job did not complete succesfully.');
}
/**
* @param ImportJob $job
*
* @return SetupInterface
* @throws FireflyException
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
private function makeImporter(ImportJob $job): SetupInterface
public function status(ImportJob $job)
{
// create proper importer (depends on job)
$type = strtolower($job->file_type);
// validate type:
$validTypes = array_keys(config('firefly.import_formats'));
if (in_array($type, $validTypes)) {
/** @var SetupInterface $importer */
$importer = app('FireflyIII\Import\Setup\\' . ucfirst($type) . 'Setup');
$importer->setJob($job);
return $importer;
$statuses = ['configured', 'running', 'finished'];
if (!in_array($job->status, $statuses)) {
return redirect(route('import.configure', [$job->key]));
}
throw new FireflyException(sprintf('"%s" is not a valid file type', $type)); // @codeCoverageIgnore
$subTitle = trans('firefly.import_status_sub_title');
$subTitleIcon = 'fa-star';
return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
}
/**
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @return ConfiguratorInterface
* @throws FireflyException
*/
private function redirectToCorrectStep(ImportJob $job)
private function makeConfigurator(ImportJob $job): ConfiguratorInterface
{
Log::debug('Now in redirectToCorrectStep()', ['job' => $job->key]);
switch ($job->status) {
case 'import_status_never_started':
Log::debug('Will redirect to configure()');
return redirect(route('import.configure', [$job->key]));
case 'import_configuration_saved':
Log::debug('Will redirect to settings()');
return redirect(route('import.settings', [$job->key]));
case 'settings_complete':
Log::debug('Will redirect to complete()');
return redirect(route('import.complete', [$job->key]));
case 'import_complete':
Log::debug('Will redirect to finished()');
return redirect(route('import.finished', [$job->key]));
$type = $job->file_type;
$key = sprintf('firefly.import_configurators.%s', $type);
$className = config($key);
if (is_null($className)) {
throw new FireflyException('Cannot find configurator class for this job.'); // @codeCoverageIgnore
}
/** @var ConfiguratorInterface $configurator */
$configurator = app($className);
$configurator->setJob($job);
throw new FireflyException('Cannot redirect for job state ' . $job->status); // @codeCoverageIgnore
return $configurator;
}
}

View File

@ -34,7 +34,7 @@ class JavascriptController extends Controller
* @param AccountRepositoryInterface $repository
* @param CurrencyRepositoryInterface $currencyRepository
*
* @return $this
* @return \Illuminate\Http\Response
*/
public function accounts(AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepository)
{
@ -63,7 +63,7 @@ class JavascriptController extends Controller
/**
* @param CurrencyRepositoryInterface $repository
*
* @return $this
* @return \Illuminate\Http\Response
*/
public function currencies(CurrencyRepositoryInterface $repository)
{
@ -71,8 +71,8 @@ class JavascriptController extends Controller
$data = ['currencies' => [],];
/** @var TransactionCurrency $currency */
foreach ($currencies as $currency) {
$currencyId = $currency->id;
$entry = ['name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol];
$currencyId = $currency->id;
$entry = ['name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol];
$data['currencies'][$currencyId] = $entry;
}

View File

@ -41,7 +41,6 @@ class ExchangeController extends Controller
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$rate = $repository->getExchangeRate($fromCurrency, $toCurrency, $date);
$amount = null;
if (is_null($rate->id)) {
Log::debug(sprintf('No cached exchange rate in database for %s to %s on %s', $fromCurrency->code, $toCurrency->code, $date->format('Y-m-d')));
$preferred = env('EXCHANGE_RATE_SERVICE', config('firefly.preferred_exchange_service'));

View File

@ -20,7 +20,6 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
@ -116,10 +115,11 @@ class JsonController extends Controller
* Since both this method and the chart use the exact same data, we can suffice
* with calling the one method in the bill repository that will get this amount.
*/
$amount = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
$amount = bcmul($amount, '-1');
$amount = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
$amount = bcmul($amount, '-1');
$currency = Amount::getDefaultCurrency();
$data = ['box' => 'bills-paid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$data = ['box' => 'bills-paid', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
return Response::json($data);
}
@ -131,19 +131,19 @@ class JsonController extends Controller
*/
public function boxBillsUnpaid(BillRepositoryInterface $repository)
{
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
$amount = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
$data = ['box' => 'bills-unpaid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
$amount = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
$currency = Amount::getDefaultCurrency();
$data = ['box' => 'bills-unpaid', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
return Response::json($data);
}
/**
* @param AccountTaskerInterface $accountTasker
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @internal param AccountTaskerInterface $accountTasker
* @internal param AccountRepositoryInterface $repository
*
*/
public function boxIn()
@ -167,18 +167,19 @@ class JsonController extends Controller
->setTypes([TransactionType::DEPOSIT])
->withOpposingAccount();
$amount = strval($collector->getJournals()->sum('transaction_amount'));
$data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$amount = strval($collector->getJournals()->sum('transaction_amount'));
$currency = Amount::getDefaultCurrency();
$data = ['box' => 'in', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
$cache->store($data);
return Response::json($data);
}
/**
* @param AccountTaskerInterface $accountTasker
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
* @internal param AccountTaskerInterface $accountTasker
* @internal param AccountRepositoryInterface $repository
*
*/
public function boxOut()
{
@ -200,9 +201,9 @@ class JsonController extends Controller
$collector->setAllAssetAccounts()->setRange($start, $end)
->setTypes([TransactionType::WITHDRAWAL])
->withOpposingAccount();
$amount = strval($collector->getJournals()->sum('transaction_amount'));
$data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$amount = strval($collector->getJournals()->sum('transaction_amount'));
$currency = Amount::getDefaultCurrency();
$data = ['box' => 'out', 'amount' => Amount::formatAnything($currency, $amount, false), 'amount_raw' => $amount];
$cache->store($data);
return Response::json($data);
@ -236,16 +237,6 @@ class JsonController extends Controller
return Response::json($return);
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function endTour()
{
Preferences::set('tour', false);
return Response::json('true');
}
/**
* Returns a JSON list of all beneficiaries.
*
@ -292,34 +283,6 @@ class JsonController extends Controller
}
/**
*
*/
public function tour()
{
$pref = Preferences::get('tour', true);
if (!$pref) {
throw new FireflyException('Cannot find preference for tour. Exit.'); // @codeCoverageIgnore
}
$headers = ['main-content', 'sidebar-toggle', 'account-menu', 'budget-menu', 'report-menu', 'transaction-menu', 'option-menu', 'main-content-end'];
$steps = [];
foreach ($headers as $header) {
$steps[] = [
'element' => '#' . $header,
'title' => trans('help.' . $header . '-title'),
'content' => trans('help.' . $header . '-text'),
];
}
$steps[0]['orphan'] = true;// orphan and backdrop for first element.
$steps[0]['backdrop'] = true;
$steps[1]['placement'] = 'left';// sidebar position left:
$steps[7]['orphan'] = true; // final in the center again.
$steps[7]['backdrop'] = true;
$template = view('json.tour')->render();
return Response::json(['steps' => $steps, 'template' => $template]);
}
/**
* @param JournalCollectorInterface $collector
* @param string $what
@ -364,7 +327,7 @@ class JsonController extends Controller
$keys = array_keys(config('firefly.rule-triggers'));
$triggers = [];
foreach ($keys as $key) {
if ($key != 'user_action') {
if ($key !== 'user_action') {
$triggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice');
}
}

View File

@ -54,7 +54,6 @@ class NewUserController extends Controller
View::share('title', trans('firefly.welcome'));
View::share('mainTitleIcon', 'fa-fire');
$types = config('firefly.accountTypesByIdentifier.asset');
$count = $repository->count($types);
@ -74,30 +73,13 @@ class NewUserController extends Controller
*/
public function submit(NewUserFormRequest $request, AccountRepositoryInterface $repository)
{
$count = 1;
// create normal asset account:
$this->createAssetAccount($request, $repository);
// create savings account
$savingBalance = strval($request->get('savings_balance')) === '' ? '0' : strval($request->get('savings_balance'));
if (bccomp($savingBalance, '0') !== 0) {
$this->createSavingsAccount($request, $repository);
$count++;
}
$this->createSavingsAccount($request, $repository);
// create credit card.
$limit = strval($request->get('credit_card_limit')) === '' ? '0' : strval($request->get('credit_card_limit'));
if (bccomp($limit, '0') !== 0) {
$this->storeCreditCard($request, $repository);
$count++;
}
$message = strval(trans('firefly.stored_new_accounts_new_user'));
if ($count == 1) {
$message = strval(trans('firefly.stored_new_account_new_user'));
}
Session::flash('success', $message);
Session::flash('success', strval(trans('firefly.stored_new_accounts_new_user')));
Preferences::mark();
return redirect(route('index'));
@ -152,29 +134,4 @@ class NewUserController extends Controller
return true;
}
/**
* @param NewUserFormRequest $request
* @param AccountRepositoryInterface $repository
*
* @return bool
*/
private function storeCreditCard(NewUserFormRequest $request, AccountRepositoryInterface $repository): bool
{
$creditAccount = [
'name' => 'Credit card',
'iban' => null,
'accountType' => 'asset',
'virtualBalance' => round($request->get('credit_card_limit'), 12),
'active' => true,
'accountRole' => 'ccAsset',
'openingBalance' => null,
'openingBalanceDate' => null,
'openingBalanceCurrency' => intval($request->input('amount_currency_id_credit_card_limit')),
'ccType' => 'monthlyFull',
'ccMonthlyPaymentDate' => Carbon::now()->year . '-01-01',
];
$repository->store($creditAccount);
return true;
}
}

View File

@ -276,18 +276,29 @@ class PiggyBankController extends Controller
*/
public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$amount = $request->get('amount');
$amount = $request->get('amount');
$currency = Amount::getDefaultCurrency();
if ($repository->canAddAmount($piggyBank, $amount)) {
$repository->addAmount($piggyBank, $amount);
Session::flash('success', strval(trans('firefly.added_amount_to_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name])));
Session::flash(
'success', strval(
trans(
'firefly.added_amount_to_piggy',
['amount' => Amount::formatAnything($currency, $amount, false), 'name' => $piggyBank->name]
)
)
);
Preferences::mark();
return redirect(route('piggy-banks.index'));
}
Log::error('Cannot add ' . $amount . ' because canAddAmount returned false.');
Session::flash('error', strval(trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
Session::flash(
'error', strval(
trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)])
)
);
return redirect(route('piggy-banks.index'));
}
@ -301,11 +312,13 @@ class PiggyBankController extends Controller
*/
public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$amount = $request->get('amount');
$amount = $request->get('amount');
$currency = Amount::getDefaultCurrency();
if ($repository->canRemoveAmount($piggyBank, $amount)) {
$repository->removeAmount($piggyBank, $amount);
Session::flash(
'success', strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name]))
'success',
strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::formatAnything($currency, $amount, false), 'name' => $piggyBank->name]))
);
Preferences::mark();
@ -314,7 +327,11 @@ class PiggyBankController extends Controller
$amount = strval(round($request->get('amount'), 12));
Session::flash('error', strval(trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
Session::flash(
'error', strval(
trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)])
)
);
return redirect(route('piggy-banks.index'));
}
@ -380,7 +397,7 @@ class PiggyBankController extends Controller
// @codeCoverageIgnoreEnd
}
return redirect($this->getPreviousUri('piggy-banks.edit.uri'));
return redirect($this->getPreviousUri('piggy-banks.create.uri'));
}
/**

View File

@ -120,7 +120,7 @@ class CategoryController extends Controller
foreach ($categories as $category) {
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
if (bccomp($spent, '0') !== 0) {
$report[$category->id] = ['name' => $category->name, 'spent' => $spent];
$report[$category->id] = ['name' => $category->name, 'spent' => $spent, 'id' => $category->id];
}
}

View File

@ -15,10 +15,7 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;

View File

@ -282,7 +282,7 @@ class RuleController extends Controller
// build trigger array from response
$triggers = $this->getValidTriggerList($request);
if (count($triggers) == 0) {
if (count($triggers) === 0) {
return Response::json(['html' => '', 'warning' => trans('firefly.warning_no_valid_triggers')]);
}
@ -298,15 +298,15 @@ class RuleController extends Controller
// Warn the user if only a subset of transactions is returned
$warning = '';
if (count($matchingTransactions) == $limit) {
if (count($matchingTransactions) === $limit) {
$warning = trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]);
}
if (count($matchingTransactions) == 0) {
if (count($matchingTransactions) === 0) {
$warning = trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]);
}
// Return json response
$view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render();
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
return Response::json(['html' => $view, 'warning' => $warning]);
}
@ -440,7 +440,7 @@ class RuleController extends Controller
/** @var RuleTrigger $entry */
foreach ($rule->ruleTriggers as $entry) {
if ($entry->trigger_type != 'user_action') {
if ($entry->trigger_type !== 'user_action') {
$count = ($index + 1);
$triggers[] = view(
'rules.partials.trigger',

View File

@ -253,7 +253,7 @@ class RuleGroupController extends Controller
$data = [
'title' => $request->input('title'),
'description' => $request->input('description'),
'active' => intval($request->input('active')) == 1,
'active' => intval($request->input('active')) === 1,
];
$repository->update($ruleGroup, $data);

View File

@ -16,6 +16,7 @@ namespace FireflyIII\Http\Controllers;
use FireflyIII\Support\Search\SearchInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Response;
use View;
/**
@ -51,6 +52,15 @@ class SearchController extends Controller
*/
public function index(Request $request, SearchInterface $searcher)
{
$fullQuery = $request->get('q');
// parse search terms:
$searcher->parseQuery($fullQuery);
$query = $searcher->getWordsAsString();
$subTitle = trans('breadcrumbs.search_result', ['query' => $query]);
return view('search.index', compact('query', 'fullQuery', 'subTitle'));
// yes, hard coded values:
$minSearchLen = 1;
$limit = 20;
@ -94,4 +104,19 @@ class SearchController extends Controller
return view('search.index', compact('rawQuery', 'hasModifiers', 'modifiers', 'subTitle', 'limit', 'query', 'result'));
}
public function search(Request $request, SearchInterface $searcher)
{
$fullQuery = $request->get('query');
// parse search terms:
$searcher->parseQuery($fullQuery);
$searcher->setLimit(20);
$transactions = $searcher->searchTransactions();
$html = view('search.search', compact('transactions'))->render();
return Response::json(['count' => $transactions->count(), 'html' => $html]);
}
}

View File

@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Http\Requests\TagFormRequest;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
@ -235,7 +236,7 @@ class TagController extends Controller
// default values:
$subTitle = $tag->tag;
$subTitleIcon = 'fa-tag';
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
$loop = 0;
@ -245,6 +246,7 @@ class TagController extends Controller
$periods = new Collection;
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
$sum = '0';
$path = 'tags/show/' . $tag->id;
// prep for "all" view.
@ -253,6 +255,7 @@ class TagController extends Controller
$start = $repository->firstUseDate($tag);
$end = new Carbon;
$sum = $repository->sumOfTag($tag);
$path = 'tags/show/' . $tag->id . '/all';
}
// prep for "specific date" view.
@ -282,15 +285,15 @@ class TagController extends Controller
Log::info('Now at tag loop start.');
while ($count === 0 && $loop < 3) {
$loop++;
Log::info('Count is zero, search for journals.');
Log::info(sprintf('Count is zero, search for journals between %s and %s (pagesize %d, page %d).', $start, $end, $pageSize, $page));
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
->setTag($tag)->withBudgetInformation()->withCategoryInformation();
->setTag($tag)->withBudgetInformation()->withCategoryInformation()->removeFilter(InternalTransferFilter::class);
$journals = $collector->getPaginatedJournals();
$journals->setPath('tags/show/' . $tag->id);
$journals->setPath($path);
$count = $journals->getCollection()->count();
if ($count === 0) {
if ($count === 0 && $loop < 3) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
@ -298,7 +301,7 @@ class TagController extends Controller
}
}
if ($moment != 'all' && $loop > 1) {
if ($moment !== 'all' && $loop > 1) {
$subTitle = trans(
'firefly.journals_in_period_for_tag',
['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]

View File

@ -174,14 +174,17 @@ class ConvertController extends Controller
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT:
// one
$destination = $sourceAccount;
break;
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: // two
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER:
// two
$destination = $accountRepository->find(intval($data['destination_account_asset']));
break;
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: // three
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: // five
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL:
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL:
// three and five
if ($data['destination_account_expense'] === '') {
// destination is a cash account.
$destination = $accountRepository->getCashAccount();
@ -197,8 +200,9 @@ class ConvertController extends Controller
];
$destination = $accountRepository->store($data);
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: // four
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: // six
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER:
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT:
// four and six
$destination = $destinationAccount;
break;
}
@ -225,8 +229,8 @@ class ConvertController extends Controller
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: // six
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT:
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT:
if ($data['source_account_revenue'] === '') {
// destination is a cash account.
@ -244,14 +248,14 @@ class ConvertController extends Controller
];
$source = $accountRepository->store($data);
break;
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: // two
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: // five
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER:
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL:
$source = $sourceAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: // three
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL:
$source = $destinationAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: // four
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER:
$source = $accountRepository->find(intval($data['source_account_asset']));
break;
}

View File

@ -19,6 +19,7 @@ use FireflyIII\Http\Requests\MassDeleteJournalRequest;
use FireflyIII\Http\Requests\MassEditJournalRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
@ -85,7 +86,7 @@ class MassController extends Controller
foreach ($ids as $journalId) {
/** @var TransactionJournal $journal */
$journal = $repository->find(intval($journalId));
if (!is_null($journal->id) && $journalId == $journal->id) {
if (!is_null($journal->id) && $journalId === $journal->id) {
$set->push($journal);
}
}
@ -126,8 +127,7 @@ class MassController extends Controller
$budgetRepository = app(BudgetRepositoryInterface::class);
$budgets = $budgetRepository->getBudgets();
// skip transactions that have multiple destinations
// or multiple sources:
// skip transactions that have multiple destinations, multiple sources or are an opening balance.
$filtered = new Collection;
$messages = [];
/**
@ -146,6 +146,10 @@ class MassController extends Controller
$messages[] = trans('firefly.cannot_edit_multiple_dest', ['description' => $journal->description, 'id' => $journal->id]);
continue;
}
if ($journal->transactionType->type === TransactionType::OPENING_BALANCE) {
$messages[] = trans('firefly.cannot_edit_opening_balance');
continue;
}
$filtered->push($journal);
}
@ -158,13 +162,21 @@ class MassController extends Controller
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'mass-edit');
// set some values to be used in the edit routine:
// collect some useful meta data for the mass edit:
$filtered->each(
function (TransactionJournal $journal) {
$journal->amount = $journal->amountPositive();
$sources = $journal->sourceAccountList();
$destinations = $journal->destinationAccountList();
$journal->transaction_count = $journal->transactions()->count();
$transaction = $journal->positiveTransaction();
$currency = $transaction->transactionCurrency;
$journal->amount = floatval($transaction->amount);
$sources = $journal->sourceAccountList();
$destinations = $journal->destinationAccountList();
$journal->transaction_count = $journal->transactions()->count();
$journal->currency_symbol = $currency->symbol;
$journal->transaction_type_type = $journal->transactionType->type;
$journal->foreign_amount = floatval($transaction->foreign_amount);
$journal->foreign_currency = $transaction->foreignCurrency;
if (!is_null($sources->first())) {
$journal->source_account_id = $sources->first()->id;
$journal->source_account_name = $sources->first()->editname;
@ -208,6 +220,10 @@ class MassController extends Controller
$budgetId = $request->get('budget_id')[$journal->id] ?? 0;
$category = $request->get('category')[$journal->id];
$tags = $journal->tags->pluck('tag')->toArray();
$amount = round($request->get('amount')[$journal->id], 12);
$foreignAmount = isset($request->get('foreign_amount')[$journal->id]) ? round($request->get('foreign_amount')[$journal->id], 12) : null;
$foreignCurrencyId = isset($request->get('foreign_currency_id')[$journal->id]) ?
intval($request->get('foreign_currency_id')[$journal->id]) : null;
// build data array
$data = [
@ -218,16 +234,19 @@ class MassController extends Controller
'source_account_name' => $sourceAccountName,
'destination_account_id' => intval($destAccountId),
'destination_account_name' => $destAccountName,
'amount' => round($request->get('amount')[$journal->id], 12),
'currency_id' => $journal->transaction_currency_id,
'amount' => $foreignAmount,
'native_amount' => $amount,
'source_amount' => $amount,
'date' => new Carbon($request->get('date')[$journal->id]),
'interest_date' => $journal->interest_date,
'book_date' => $journal->book_date,
'process_date' => $journal->process_date,
'budget_id' => intval($budgetId),
'currency_id' => $foreignCurrencyId,
'foreign_amount' => $foreignAmount,
'destination_amount' => $foreignAmount,
'category' => $category,
'tags' => $tags,
];
// call repository update function.
$repository->update($journal, $data);
@ -235,6 +254,7 @@ class MassController extends Controller
$count++;
}
}
}
Preferences::mark();
Session::flash('success', trans('firefly.mass_edited_transactions_success', ['amount' => $count]));

View File

@ -21,6 +21,7 @@ use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@ -93,27 +94,35 @@ class SingleController extends Controller
$category = $journal->categories()->first();
$categoryName = is_null($category) ? '' : $category->name;
$tags = join(',', $journal->tags()->get()->pluck('tag')->toArray());
/** @var Transaction $transaction */
$transaction = $journal->transactions()->first();
$amount = Steam::positive($transaction->amount);
$foreignAmount = is_null($transaction->foreign_amount) ? null : Steam::positive($transaction->foreign_amount);
$preFilled = [
'description' => $journal->description,
'source_account_id' => $source->id,
'source_account_name' => $source->name,
'destination_account_id' => $destination->id,
'destination_account_name' => $destination->name,
'amount' => $journal->amountPositive(),
'date' => $journal->date->format('Y-m-d'),
'budget_id' => $budgetId,
'category' => $categoryName,
'tags' => $tags,
'interest_date' => $journal->getMeta('interest_date'),
'book_date' => $journal->getMeta('book_date'),
'process_date' => $journal->getMeta('process_date'),
'due_date' => $journal->getMeta('due_date'),
'payment_date' => $journal->getMeta('payment_date'),
'invoice_date' => $journal->getMeta('invoice_date'),
'internal_reference' => $journal->getMeta('internal_reference'),
'notes' => $journal->getMeta('notes'),
'description' => $journal->description,
'source_account_id' => $source->id,
'source_account_name' => $source->name,
'destination_account_id' => $destination->id,
'destination_account_name' => $destination->name,
'amount' => $amount,
'source_amount' => $amount,
'destination_amount' => $foreignAmount,
'foreign_amount' => $foreignAmount,
'native_amount' => $foreignAmount,
'amount_currency_id_amount' => $transaction->foreign_currency_id ?? 0,
'date' => $journal->date->format('Y-m-d'),
'budget_id' => $budgetId,
'category' => $categoryName,
'tags' => $tags,
'interest_date' => $journal->getMeta('interest_date'),
'book_date' => $journal->getMeta('book_date'),
'process_date' => $journal->getMeta('process_date'),
'due_date' => $journal->getMeta('due_date'),
'payment_date' => $journal->getMeta('payment_date'),
'invoice_date' => $journal->getMeta('invoice_date'),
'internal_reference' => $journal->getMeta('internal_reference'),
'notes' => $journal->getMeta('notes'),
];
Session::flash('preFilled', $preFilled);
@ -238,6 +247,7 @@ class SingleController extends Controller
$sourceAccounts = $journal->sourceAccountList();
$destinationAccounts = $journal->destinationAccountList();
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$pTransaction = $journal->positiveTransaction();
$preFilled = [
'date' => $journal->dateAsString(),
'interest_date' => $journal->dateAsString('interest_date'),
@ -250,8 +260,6 @@ class SingleController extends Controller
'source_account_name' => $sourceAccounts->first()->edit_name,
'destination_account_id' => $destinationAccounts->first()->id,
'destination_account_name' => $destinationAccounts->first()->edit_name,
'amount' => $journal->amountPositive(),
'currency' => $journal->transactionCurrency,
// new custom fields:
'due_date' => $journal->dateAsString('due_date'),
@ -260,26 +268,36 @@ class SingleController extends Controller
'interal_reference' => $journal->getMeta('internal_reference'),
'notes' => $journal->getMeta('notes'),
// exchange rate fields
'native_amount' => $journal->amountPositive(),
'native_currency' => $journal->transactionCurrency,
// amount fields
'amount' => $pTransaction->amount,
'source_amount' => $pTransaction->amount,
'native_amount' => $pTransaction->amount,
'destination_amount' => $pTransaction->foreign_amount,
'currency' => $pTransaction->transactionCurrency,
'source_currency' => $pTransaction->transactionCurrency,
'native_currency' => $pTransaction->transactionCurrency,
'foreign_currency' => !is_null($pTransaction->foreignCurrency) ? $pTransaction->foreignCurrency : $pTransaction->transactionCurrency,
'destination_currency' => !is_null($pTransaction->foreignCurrency) ? $pTransaction->foreignCurrency : $pTransaction->transactionCurrency,
];
// if user has entered a foreign currency, update some fields
$foreignCurrencyId = intval($journal->getMeta('foreign_currency_id'));
if ($foreignCurrencyId > 0) {
// update some fields in pre-filled.
// @codeCoverageIgnoreStart
$preFilled['amount'] = $journal->getMeta('foreign_amount');
$preFilled['currency'] = $this->currency->find(intval($journal->getMeta('foreign_currency_id')));
// @codeCoverageIgnoreEnd
// amounts for withdrawals and deposits:
// amount, native_amount, source_amount, destination_amount
if (($journal->isWithdrawal() || $journal->isDeposit()) && !is_null($pTransaction->foreign_amount)) {
$preFilled['amount'] = $pTransaction->foreign_amount;
$preFilled['currency'] = $pTransaction->foreignCurrency;
}
if ($journal->isWithdrawal() && $destinationAccounts->first()->accountType->type == AccountType::CASH) {
if ($journal->isTransfer() && !is_null($pTransaction->foreign_amount)) {
$preFilled['destination_amount'] = $pTransaction->foreign_amount;
$preFilled['destination_currency'] = $pTransaction->foreignCurrency;
}
// fixes for cash accounts:
if ($journal->isWithdrawal() && $destinationAccounts->first()->accountType->type === AccountType::CASH) {
$preFilled['destination_account_name'] = '';
}
if ($journal->isDeposit() && $sourceAccounts->first()->accountType->type == AccountType::CASH) {
if ($journal->isDeposit() && $sourceAccounts->first()->accountType->type === AccountType::CASH) {
$preFilled['source_account_name'] = '';
}
@ -319,6 +337,7 @@ class SingleController extends Controller
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
}
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
$this->attachments->saveAttachmentsForModel($journal, $files);

View File

@ -93,7 +93,7 @@ class SplitController extends Controller
}
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$currencies = ExpandedForm::makeSelectList($this->currencies->get());
$currencies = $this->currencies->get();
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
@ -130,7 +130,6 @@ class SplitController extends Controller
*/
public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
@ -179,7 +178,6 @@ class SplitController extends Controller
'journal_source_account_id' => $request->get('journal_source_account_id'),
'journal_source_account_name' => $request->get('journal_source_account_name'),
'journal_destination_account_id' => $request->get('journal_destination_account_id'),
'currency_id' => $request->get('currency_id'),
'what' => $request->get('what'),
'date' => $request->get('date'),
// all custom fields:
@ -218,10 +216,9 @@ class SplitController extends Controller
'journal_source_account_id' => $request->old('journal_source_account_id', $sourceAccounts->first()->id),
'journal_source_account_name' => $request->old('journal_source_account_name', $sourceAccounts->first()->name),
'journal_destination_account_id' => $request->old('journal_destination_account_id', $destinationAccounts->first()->id),
'currency_id' => $request->old('currency_id', $journal->transaction_currency_id),
'destinationAccounts' => $destinationAccounts,
'what' => strtolower($journal->transactionTypeStr()),
'date' => $request->old('date', $journal->date),
'date' => $request->old('date', $journal->date->format('Y-m-d')),
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
// all custom fields:
@ -253,14 +250,22 @@ class SplitController extends Controller
/** @var array $transaction */
foreach ($transactions as $index => $transaction) {
$set = [
'description' => $transaction['description'],
'source_account_id' => $transaction['source_account_id'],
'source_account_name' => $transaction['source_account_name'],
'destination_account_id' => $transaction['destination_account_id'],
'destination_account_name' => $transaction['destination_account_name'],
'amount' => round($transaction['destination_amount'], 12),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'],
'description' => $transaction['description'],
'source_account_id' => $transaction['source_account_id'],
'source_account_name' => $transaction['source_account_name'],
'destination_account_id' => $transaction['destination_account_id'],
'destination_account_name' => $transaction['destination_account_name'],
'amount' => round($transaction['destination_amount'], 12),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'],
'transaction_currency_id' => $transaction['transaction_currency_id'],
'transaction_currency_code' => $transaction['transaction_currency_code'],
'transaction_currency_symbol' => $transaction['transaction_currency_symbol'],
'foreign_amount' => round($transaction['foreign_destination_amount'], 12),
'foreign_currency_id' => $transaction['foreign_currency_id'],
'foreign_currency_code' => $transaction['foreign_currency_code'],
'foreign_currency_symbol' => $transaction['foreign_currency_symbol'],
];
// set initial category and/or budget:
@ -294,8 +299,12 @@ class SplitController extends Controller
'destination_account_id' => $transaction['destination_account_id'] ?? 0,
'destination_account_name' => $transaction['destination_account_name'] ?? '',
'amount' => round($transaction['amount'] ?? 0, 12),
'foreign_amount' => !isset($transaction['foreign_amount']) ? null : round($transaction['foreign_amount'] ?? 0, 12),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'] ?? '',
'transaction_currency_id' => intval($transaction['transaction_currency_id']),
'foreign_currency_id' => $transaction['foreign_currency_id'] ?? null,
];
}
Log::debug(sprintf('Found %d splits in request data.', count($return)));

View File

@ -18,7 +18,6 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use FireflyIII\Support\CacheProperties;
@ -71,7 +70,7 @@ class TransactionController extends Controller
// default values:
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what);
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
$loop = 0;
@ -79,6 +78,7 @@ class TransactionController extends Controller
$start = null;
$end = null;
$periods = new Collection;
$path = '/transactions/' . $what;
// prep for "all" view.
if ($moment === 'all') {
@ -86,12 +86,14 @@ class TransactionController extends Controller
$first = $repository->first();
$start = $first->date ?? new Carbon;
$end = new Carbon;
$path = '/transactions/' . $what . '/all/';
}
// prep for "specific date" view.
if (strlen($moment) > 0 && $moment !== 'all') {
$start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range);
$path = '/transactions/' . $what . '/' . $moment;
$subTitle = trans(
'firefly.title_' . $what . '_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@ -119,9 +121,9 @@ class TransactionController extends Controller
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes($types)->setLimit($pageSize)->setPage($page)->withOpposingAccount();
$collector->removeFilter(InternalTransferFilter::class);
$journals = $collector->getPaginatedJournals();
$journals->setPath('/transactions/' . $what);
$journals->setPath($path);
$count = $journals->getCollection()->count();
if ($count === 0) {
if ($count === 0 && $loop < 3) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
@ -129,7 +131,7 @@ class TransactionController extends Controller
}
}
if ($moment != 'all' && $loop > 1) {
if ($moment !== 'all' && $loop > 1) {
$subTitle = trans(
'firefly.title_' . $what . '_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@ -179,21 +181,12 @@ class TransactionController extends Controller
return $this->redirectToAccount($journal);
}
$events = $tasker->getPiggyBankEvents($journal);
$transactions = $tasker->getTransactionsOverview($journal);
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
$foreignCurrency = null;
$events = $tasker->getPiggyBankEvents($journal);
$transactions = $tasker->getTransactionsOverview($journal);
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
if ($journal->hasMeta('foreign_currency_id')) {
// @codeCoverageIgnoreStart
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$foreignCurrency = $repository->find(intval($journal->getMeta('foreign_currency_id')));
// @codeCoverageIgnoreEnd
}
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions', 'foreignCurrency'));
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions'));
}

View File

@ -38,19 +38,19 @@ class AccountFormRequest extends Request
public function getAccountData(): array
{
return [
'name' => $this->string('name'),
'active' => $this->boolean('active'),
'accountType' => $this->string('what'),
'currency_id' => $this->integer('currency_id'),
'virtualBalance' => $this->float('virtualBalance'),
'iban' => $this->string('iban'),
'BIC' => $this->string('BIC'),
'accountNumber' => $this->string('accountNumber'),
'accountRole' => $this->string('accountRole'),
'openingBalance' => $this->float('openingBalance'),
'openingBalanceDate' => $this->date('openingBalanceDate'),
'ccType' => $this->string('ccType'),
'ccMonthlyPaymentDate' => $this->string('ccMonthlyPaymentDate'),
'name' => $this->string('name'),
'active' => $this->boolean('active'),
'accountType' => $this->string('what'),
'currency_id' => $this->integer('currency_id'),
'virtualBalance' => $this->float('virtualBalance'),
'iban' => $this->string('iban'),
'BIC' => $this->string('BIC'),
'accountNumber' => $this->string('accountNumber'),
'accountRole' => $this->string('accountRole'),
'openingBalance' => $this->float('openingBalance'),
'openingBalanceDate' => $this->date('openingBalanceDate'),
'ccType' => $this->string('ccType'),
'ccMonthlyPaymentDate' => $this->string('ccMonthlyPaymentDate'),
];
}

View File

@ -38,8 +38,9 @@ class ImportUploadRequest extends Request
$types = array_keys(config('firefly.import_formats'));
return [
'import_file' => 'required|file',
'import_file_type' => 'required|in:' . join(',', $types),
'import_file' => 'required|file',
'import_file_type' => 'required|in:' . join(',', $types),
'configuration_file' => 'file',
];
}

View File

@ -37,7 +37,7 @@ class TagFormRequest extends Request
*/
public function collectTagData(): array
{
if ($this->get('setTag') == 'true') {
if ($this->get('setTag') === 'true') {
$latitude = $this->string('latitude');
$longitude = $this->string('longitude');
$zoomLevel = $this->integer('zoomLevel');

View File

@ -78,10 +78,10 @@ Breadcrumbs::register(
$breadcrumbs->push(trans('firefly.everything'), route('accounts.show', [$account->id, 'all']));
}
// when is specific period or when empty:
if ($moment !== 'all') {
if ($moment !== 'all' && $moment !== '(nothing)') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
);
$breadcrumbs->push($title, route('accounts.show', [$account->id, $moment, $start, $end]));
}
@ -91,7 +91,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'accounts.delete', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account, '', new Carbon, new Carbon);
$breadcrumbs->parent('accounts.show', $account, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.delete_account', ['name' => e($account->name)]), route('accounts.delete', [$account->id]));
}
);
@ -99,7 +99,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'accounts.edit', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account, '', new Carbon, new Carbon);
$breadcrumbs->parent('accounts.show', $account, '(nothing)', new Carbon, new Carbon);
$what = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$breadcrumbs->push(trans('firefly.edit_' . $what . '_account', ['name' => e($account->name)]), route('accounts.edit', [$account->id]));
@ -257,8 +257,8 @@ Breadcrumbs::register(
if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('budgets.no-budget', ['all']));
}
// when is specific period:
if ($moment !== 'all') {
// when is specific period or when empty:
if ($moment !== 'all' && $moment !== '(nothing)') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
@ -312,13 +312,13 @@ Breadcrumbs::register(
Breadcrumbs::register(
'categories.edit', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category, '', new Carbon, new Carbon);
$breadcrumbs->parent('categories.show', $category, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.edit_category', ['name' => e($category->name)]), route('categories.edit', [$category->id]));
}
);
Breadcrumbs::register(
'categories.delete', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category, '', new Carbon, new Carbon);
$breadcrumbs->parent('categories.show', $category, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.delete_category', ['name' => e($category->name)]), route('categories.delete', [$category->id]));
}
);
@ -333,8 +333,8 @@ Breadcrumbs::register(
if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('categories.show', [$category->id, 'all']));
}
// when is specific period:
if ($moment !== 'all') {
// when is specific period or when empty:
if ($moment !== 'all' && $moment !== '(nothing)') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
@ -354,8 +354,8 @@ Breadcrumbs::register(
if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('categories.no-category', ['all']));
}
// when is specific period:
if ($moment !== 'all') {
// when is specific period or when empty:
if ($moment !== 'all' && $moment !== '(nothing)') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
@ -469,26 +469,20 @@ Breadcrumbs::register(
$breadcrumbs->push(trans('firefly.import'), route('import.index'));
}
);
Breadcrumbs::register(
'import.complete', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
$breadcrumbs->parent('import.index');
$breadcrumbs->push(trans('firefly.bread_crumb_import_complete', ['key' => $job->key]), route('import.complete', [$job->key]));
}
);
Breadcrumbs::register(
'import.configure', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
$breadcrumbs->parent('import.index');
$breadcrumbs->push(trans('firefly.bread_crumb_configure_import', ['key' => $job->key]), route('import.configure', [$job->key]));
$breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.configure', [$job->key]));
}
);
Breadcrumbs::register(
'import.finished', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
'import.status', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
$breadcrumbs->parent('import.index');
$breadcrumbs->push(trans('firefly.bread_crumb_import_finished', ['key' => $job->key]), route('import.finished', [$job->key]));
$breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.status', [$job->key]));
}
);
/**
* PREFERENCES
*/
@ -686,7 +680,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'search.index', function (BreadCrumbGenerator $breadcrumbs, $query) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search.index'));
$breadcrumbs->push(trans('breadcrumbs.search_result', ['query' => e($query)]), route('search.index'));
}
);
@ -710,14 +704,14 @@ Breadcrumbs::register(
Breadcrumbs::register(
'tags.edit', function (BreadCrumbGenerator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag, '', new Carbon, new Carbon);
$breadcrumbs->parent('tags.show', $tag, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('breadcrumbs.edit_tag', ['tag' => e($tag->tag)]), route('tags.edit', [$tag->id]));
}
);
Breadcrumbs::register(
'tags.delete', function (BreadCrumbGenerator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag, '', new Carbon, new Carbon);
$breadcrumbs->parent('tags.show', $tag, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('breadcrumbs.delete_tag', ['tag' => e($tag->tag)]), route('tags.delete', [$tag->id]));
}
);
@ -726,16 +720,17 @@ Breadcrumbs::register(
Breadcrumbs::register(
'tags.show', function (BreadCrumbGenerator $breadcrumbs, Tag $tag, string $moment, Carbon $start, Carbon $end) {
$breadcrumbs->parent('tags.index');
$breadcrumbs->push(e($tag->tag), route('tags.show', [$tag->id], $moment));
$breadcrumbs->push(e($tag->tag), route('tags.show', [$tag->id, $moment]));
if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('tags.show', [$tag->id], $moment));
$breadcrumbs->push(trans('firefly.everything'), route('tags.show', [$tag->id, $moment]));
}
if ($moment !== 'all') {
// when is specific period or when empty:
if ($moment !== 'all' && $moment !== '(nothing)') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
);
$breadcrumbs->push($title, route('tags.show', [$tag->id], $moment));
$breadcrumbs->push($title, route('tags.show', [$tag->id, $moment]));
}
}
);
@ -753,8 +748,8 @@ Breadcrumbs::register(
$breadcrumbs->push(trans('firefly.everything'), route('transactions.index', [$what, 'all']));
}
// when is specific period:
if ($moment !== 'all') {
// when is specific period or when empty:
if ($moment !== 'all' && $moment !== '(nothing)') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
@ -767,7 +762,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'transactions.create', function (BreadCrumbGenerator $breadcrumbs, string $what) {
$breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->parent('transactions.index', $what, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('transactions.create', [$what]));
}
);
@ -789,7 +784,7 @@ Breadcrumbs::register(
'transactions.show', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
$what = strtolower($journal->transactionType->type);
$breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->parent('transactions.index', $what, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push($journal->description, route('transactions.show', [$journal->id]));
}
);
@ -814,7 +809,7 @@ Breadcrumbs::register(
if ($journals->count() > 0) {
$journalIds = $journals->pluck('id')->toArray();
$what = strtolower($journals->first()->transactionType->type);
$breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->parent('transactions.index', $what, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.edit', $journalIds));
return;
@ -829,7 +824,7 @@ Breadcrumbs::register(
$journalIds = $journals->pluck('id')->toArray();
$what = strtolower($journals->first()->transactionType->type);
$breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->parent('transactions.index', $what, '(nothing)', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.delete', $journalIds));
}
);

View File

@ -0,0 +1,65 @@
<?php
/**
* ConfiguratorInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Configurator;
use FireflyIII\Models\ImportJob;
/**
* Interface ConfiguratorInterface
*
* @package FireflyIII\Import\Configurator
*/
interface ConfiguratorInterface
{
/**
* ConfiguratorInterface constructor.
*/
public function __construct();
/**
* Store any data from the $data array into the job.
*
* @param array $data
*
* @return bool
*/
public function configureJob(array $data): bool;
/**
* Return the data required for the next step in the job configuration.
*
* @return array
*/
public function getNextData(): array;
/**
* Returns the view of the next step in the job configuration.
*
* @return string
*/
public function getNextView(): string;
/**
* Returns true when the initial configuration for this job is complete.
*
* @return bool
*/
public function isJobConfigured(): bool;
/**
* @param ImportJob $job
*
* @return void
*/
public function setJob(ImportJob $job);
}

View File

@ -0,0 +1,159 @@
<?php
/**
* CsvConfigurator.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Configurator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\ImportJob;
use FireflyIII\Support\Import\Configuration\ConfigurationInterface;
use FireflyIII\Support\Import\Configuration\Csv\Initial;
use FireflyIII\Support\Import\Configuration\Csv\Map;
use FireflyIII\Support\Import\Configuration\Csv\Roles;
use Log;
/**
* Class CsvConfigurator
*
* @package FireflyIII\Import\Configurator
*/
class CsvConfigurator implements ConfiguratorInterface
{
private $job;
/**
* ConfiguratorInterface constructor.
*/
public function __construct()
{
}
/**
* Store any data from the $data array into the job.
*
* @param array $data
*
* @return bool
* @throws FireflyException
*/
public function configureJob(array $data): bool
{
$class = $this->getConfigurationClass();
$job = $this->job;
/** @var ConfigurationInterface $object */
$object = new $class($this->job);
$object->setJob($job);
return $object->storeConfiguration($data);
}
/**
* Return the data required for the next step in the job configuration.
*
* @return array
* @throws FireflyException
*/
public function getNextData(): array
{
$class = $this->getConfigurationClass();
$job = $this->job;
/** @var ConfigurationInterface $object */
$object = app($class);
$object->setJob($job);
return $object->getData();
}
/**
* @return string
* @throws FireflyException
*/
public function getNextView(): string
{
if (!$this->job->configuration['initial-config-complete']) {
return 'import.csv.initial';
}
if (!$this->job->configuration['column-roles-complete']) {
return 'import.csv.roles';
}
if (!$this->job->configuration['column-mapping-complete']) {
return 'import.csv.map';
}
throw new FireflyException('No view for state');
}
/**
* @return bool
*/
public function isJobConfigured(): bool
{
$config = $this->job->configuration;
$config['initial-config-complete'] = $config['initial-config-complete'] ?? false;
$config['column-roles-complete'] = $config['column-roles-complete'] ?? false;
$config['column-mapping-complete'] = $config['column-mapping-complete'] ?? false;
$this->job->configuration = $config;
$this->job->save();
if ($this->job->configuration['initial-config-complete']
&& $this->job->configuration['column-roles-complete']
&& $this->job->configuration['column-mapping-complete']
) {
return true;
}
return false;
}
/**
* @param ImportJob $job
*/
public function setJob(ImportJob $job)
{
$this->job = $job;
if (is_null($this->job->configuration) || count($this->job->configuration) === 0) {
Log::debug(sprintf('Gave import job %s initial configuration.', $this->job->key));
$this->job->configuration = config('csv.default_config');
$this->job->save();
}
}
/**
* @return string
* @throws FireflyException
*/
private function getConfigurationClass(): string
{
$class = false;
switch (true) {
case (!$this->job->configuration['initial-config-complete']):
$class = Initial::class;
break;
case (!$this->job->configuration['column-roles-complete']):
$class = Roles::class;
break;
case (!$this->job->configuration['column-mapping-complete']):
$class = Map::class;
break;
default:
break;
}
if ($class === false || strlen($class) === 0) {
throw new FireflyException('Cannot handle current job state in getConfigurationClass().');
}
if (!class_exists($class)) {
throw new FireflyException(sprintf('Class %s does not exist in getConfigurationClass().', $class));
}
return $class;
}
}

View File

@ -1,69 +0,0 @@
<?php
/**
* AccountId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Log;
/**
* Class AccountId
*
* @package FireflyIII\Import\Converter
*/
class AccountId extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Account
*/
public function convert($value)
{
$value = intval(trim($value));
Log::debug('Going to convert using AssetAccountId', ['value' => $value]);
if ($value === 0) {
$this->setCertainty(0);
return new Account;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$account = $repository->find(intval($this->mapping[$value]));
if (!is_null($account->id)) {
Log::debug('Found account by ID', ['id' => $account->id]);
$this->setCertainty(100);
return $account;
}
}
$account = $repository->find($value);// not mapped? Still try to find it first:
if (!is_null($account->id)) {
$this->setCertainty(90);
Log::debug('Found account by ID ', ['id' => $account->id]);
return $account;
}
$this->setCertainty(0); // should not really happen. If the ID does not match FF, what is FF supposed to do?
return new Account;
}
}

View File

@ -18,7 +18,7 @@ namespace FireflyIII\Import\Converter;
*
* @package FireflyIII\Import\Converter
*/
class Amount extends BasicConverter implements ConverterInterface
class Amount implements ConverterInterface
{
/**
@ -28,18 +28,18 @@ class Amount extends BasicConverter implements ConverterInterface
*
* @param $value
*
* @return float
* @return string
*/
public function convert($value): float
public function convert($value): string
{
$len = strlen($value);
$decimalPosition = $len - 3;
$decimal = null;
if (($len > 2 && $value{$decimalPosition} == '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) {
if (($len > 2 && $value{$decimalPosition} === '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) {
$decimal = '.';
}
if ($len > 2 && $value{$decimalPosition} == ',') {
if ($len > 2 && $value{$decimalPosition} === ',') {
$decimal = ',';
}
@ -59,10 +59,7 @@ class Amount extends BasicConverter implements ConverterInterface
$value = str_replace($search, '', $value);
}
$this->setCertainty(90);
return round(floatval($value), 12);
return strval(round(floatval($value), 12));
}
}

View File

@ -1,87 +0,0 @@
<?php
/**
* AssetAccountIban.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Log;
/**
* Class AssetAccountIban
*
* @package FireflyIII\Import\Converter
*/
class AssetAccountIban extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Account
*/
public function convert($value): Account
{
$value = trim($value);
Log::debug('Going to convert ', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Account;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$account = $repository->find(intval($this->mapping[$value]));
if (!is_null($account->id)) {
$this->setCertainty(100);
Log::debug('Found account by ID', ['id' => $account->id]);
return $account;
}
}
// not mapped? Still try to find it first:
$account = $repository->findByIban($value, [AccountType::ASSET]);
if (!is_null($account->id)) {
Log::debug('Found account by IBAN', ['id' => $account->id]);
$this->setCertainty(50);
return $account;
}
$account = $repository->store(
['name' => 'Asset account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0,
'active' => true, 'openingBalance' => 0]
);
if (is_null($account->id)) {
$this->setCertainty(0);
Log::info('Could not store new asset account by IBAN', $account->getErrors()->toArray());
return new Account;
}
$this->setCertainty(100);
return $account;
}
}

View File

@ -1,90 +0,0 @@
<?php
/**
* AssetAccountName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Log;
/**
* Class AssetAccountName
*
* @package FireflyIII\Import\Converter
*/
class AssetAccountName extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Account
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using AssetAccountName', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Account;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$account = $repository->find(intval($this->mapping[$value]));
if (!is_null($account->id)) {
Log::debug('Found account by ID', ['id' => $account->id]);
$this->setCertainty(100);
return $account;
}
}
// not mapped? Still try to find it first:
$account = $repository->findByName($value, [AccountType::ASSET]);
if (!is_null($account->id)) {
Log::debug('Found asset account by name', ['value' => $value, 'id' => $account->id]);
return $account;
}
$account = $repository->store(
['name' => $value, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0,
'active' => true]
);
if (is_null($account->id)) {
$this->setCertainty(0);
Log::info('Could not store new asset account by name', $account->getErrors()->toArray());
return new Account;
}
$this->setCertainty(100);
Log::debug('Created new asset account ', ['name' => $account->name, 'id' => $account->id]);
return $account;
}
}

View File

@ -1,96 +0,0 @@
<?php
/**
* AssetAccountNumber.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Log;
/**
* Class AssetAccountNumber
*
* @package FireflyIII\Import\Converter
*/
class AssetAccountNumber extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Account
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using AssetAccountNumber', ['value' => $value]);
if (strlen($value) === 0) {
return new Account;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$account = $repository->find(intval($this->mapping[$value]));
if (!is_null($account->id)) {
Log::debug('Found account by ID', ['id' => $account->id]);
return $account;
}
}
// not mapped? Still try to find it first:
$account = $repository->findByAccountNumber($value, [AccountType::ASSET]);
if (!is_null($account->id)) {
Log::debug('Found account by name', ['id' => $account->id]);
$this->setCertainty(50);
return $account;
}
// try to find by the name we would give it:
$accountName = 'Asset account with number ' . e($value);
$account = $repository->findByName($accountName, [AccountType::ASSET]);
if (!is_null($account->id)) {
Log::debug('Found account by name', ['id' => $account->id]);
$this->setCertainty(50);
return $account;
}
$account = $repository->store(
['name' => $accountName, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id,
'accountType' => 'asset',
'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true]
);
if (is_null($account->id)) {
$this->setCertainty(0);
Log::info('Could not store new asset account by account number', $account->getErrors()->toArray());
return new Account;
}
$this->setCertainty(100);
return $account;
}
}

View File

@ -1,85 +0,0 @@
<?php
/**
* BasicConverter.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\User;
/**
* Class BasicConverter
*
* @package FireflyIII\Import\Converter
*/
class BasicConverter
{
/** @var int */
public $certainty = 50;
/** @var array */
public $config;
/** @var bool */
public $doMap;
/** @var array */
public $mapping = [];
/** @var User */
public $user;
/**
* @return int
*/
public function getCertainty(): int
{
return $this->certainty;
}
/**
* @param int $certainty
*/
protected function setCertainty(int $certainty)
{
$this->certainty = $certainty;
}
/**
* @param array $config
*/
public function setConfig(array $config)
{
$this->config = $config;
}
/**
* @param mixed $doMap
*/
public function setDoMap(bool $doMap)
{
$this->doMap = $doMap;
}
/**
* @param array $mapping
*
*/
public function setMapping(array $mapping)
{
$this->mapping = $mapping;
}
/**
* @param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
}

View File

@ -1,76 +0,0 @@
<?php
/**
* BillId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Log;
/**
* Class BillId
*
* @package FireflyIII\Import\Converter
*/
class BillId extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Bill
*/
public function convert($value)
{
$value = intval(trim($value));
Log::debug('Going to convert using BillId', ['value' => $value]);
if ($value === 0) {
$this->setCertainty(0);
return new Bill;
}
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$bill = $repository->find(intval($this->mapping[$value]));
if (!is_null($bill->id)) {
Log::debug('Found bill by ID', ['id' => $bill->id]);
$this->setCertainty(100);
return $bill;
}
}
// not mapped? Still try to find it first:
$bill = $repository->find($value);
if (!is_null($bill->id)) {
Log::debug('Found bill by ID ', ['id' => $bill->id]);
$this->setCertainty(100);
return $bill;
}
// should not really happen. If the ID does not match FF, what is FF supposed to do?
Log::info(sprintf('Could not find bill with ID %d. Will return NULL', $value));
$this->setCertainty(0);
return new Bill;
}
}

View File

@ -1,99 +0,0 @@
<?php
/**
* BillName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Log;
/**
* Class BillName
*
* @package FireflyIII\Import\Converter
*/
class BillName extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Bill
* @throws FireflyException
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using BillName', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Bill;
}
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$bill = $repository->find(intval($this->mapping[$value]));
if (!is_null($bill->id)) {
Log::debug('Found bill by ID', ['id' => $bill->id]);
$this->setCertainty(100);
return $bill;
}
}
// not mapped? Still try to find it first:
$bill = $repository->findByName($value);
if (!is_null($bill->id)) {
Log::debug('Found bill by name ', ['id' => $bill->id]);
$this->setCertainty(100);
return $bill;
}
// create new bill. Use a lot of made up values.
$bill = $repository->store(
[
'name' => $value,
'match' => $value,
'amount_min' => 1,
'user' => $this->user->id,
'amount_max' => 10,
'date' => date('Ymd'),
'repeat_freq' => 'monthly',
'skip' => 0,
'automatch' => 0,
'active' => 1,
]
);
if (is_null($bill->id)) {
$this->setCertainty(0);
Log::info('Could not store new bill by name', $bill->getErrors()->toArray());
return new Bill;
}
$this->setCertainty(100);
return $bill;
}
}

View File

@ -1,76 +0,0 @@
<?php
/**
* BudgetId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Log;
/**
* Class BudgetId
*
* @package FireflyIII\Import\Converter
*/
class BudgetId extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Budget
*/
public function convert($value)
{
$value = intval(trim($value));
Log::debug('Going to convert using BudgetId', ['value' => $value]);
if ($value === 0) {
$this->setCertainty(0);
return new Budget;
}
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$budget = $repository->find(intval($this->mapping[$value]));
if (!is_null($budget->id)) {
Log::debug('Found budget by ID', ['id' => $budget->id]);
$this->setCertainty(100);
return $budget;
}
}
// not mapped? Still try to find it first:
$budget = $repository->find($value);
if (!is_null($budget->id)) {
Log::debug('Found budget by ID ', ['id' => $budget->id]);
$this->setCertainty(100);
return $budget;
}
// should not really happen. If the ID does not match FF, what is FF supposed to do?
$this->setCertainty(0);
Log::info(sprintf('Could not find budget with ID %d. Will return NULL', $value));
return new Budget;
}
}

View File

@ -1,80 +0,0 @@
<?php
/**
* BudgetName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Log;
/**
* Class BudgetName
*
* @package FireflyIII\Import\Converter
*/
class BudgetName extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Budget
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using BudgetName', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Budget;
}
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$budget = $repository->find(intval($this->mapping[$value]));
if (!is_null($budget->id)) {
Log::debug('Found budget by ID', ['id' => $budget->id]);
$this->setCertainty(100);
return $budget;
}
}
// not mapped? Still try to find it first:
$budget = $repository->findByName($value);
if (!is_null($budget->id)) {
Log::debug('Found budget by name ', ['id' => $budget->id]);
$this->setCertainty(100);
return $budget;
}
// create new budget. Use a lot of made up values.
$budget = $repository->store(
[
'name' => $value,
'user' => $this->user->id,
]
);
$this->setCertainty(100);
return $budget;
}
}

View File

@ -1,76 +0,0 @@
<?php
/**
* CategoryId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Log;
/**
* Class CategoryId
*
* @package FireflyIII\Import\Converter
*/
class CategoryId extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Category
*/
public function convert($value)
{
$value = intval(trim($value));
Log::debug('Going to convert using CategoryId', ['value' => $value]);
if ($value === 0) {
$this->setCertainty(0);
return new Category;
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$category = $repository->find(intval($this->mapping[$value]));
if (!is_null($category->id)) {
Log::debug('Found category by ID', ['id' => $category->id]);
$this->setCertainty(100);
return $category;
}
}
// not mapped? Still try to find it first:
$category = $repository->find($value);
if (!is_null($category->id)) {
Log::debug('Found category by ID ', ['id' => $category->id]);
$this->setCertainty(100);
return $category;
}
// should not really happen. If the ID does not match FF, what is FF supposed to do?
$this->setCertainty(0);
Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value));
return new Category;
}
}

View File

@ -1,80 +0,0 @@
<?php
/**
* CategoryName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Log;
/**
* Class CategoryName
*
* @package FireflyIII\Import\Converter
*/
class CategoryName extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Category
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using CategoryName', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Category;
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$category = $repository->find(intval($this->mapping[$value]));
if (!is_null($category->id)) {
Log::debug('Found category by ID', ['id' => $category->id]);
$this->setCertainty(100);
return $category;
}
}
// not mapped? Still try to find it first:
$category = $repository->findByName($value);
if (!is_null($category->id)) {
Log::debug('Found category by name ', ['id' => $category->id]);
$this->setCertainty(100);
return $category;
}
// create new category. Use a lot of made up values.
$category = $repository->store(
[
'name' => $value,
'user' => $this->user->id,
]
);
$this->setCertainty(100);
return $category;
}
}

View File

@ -13,8 +13,6 @@ declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\User;
/**
* Interface ConverterInterface
*
@ -27,30 +25,4 @@ interface ConverterInterface
*
*/
public function convert($value);
/**
* @return int
*/
public function getCertainty(): int;
/**
* @param array $config
*/
public function setConfig(array $config);
/**
* @param bool $doMap
*/
public function setDoMap(bool $doMap);
/**
* @param array $mapping
*
*/
public function setMapping(array $mapping);
/**
* @param User $user
*/
public function setUser(User $user);
}

View File

@ -1,71 +0,0 @@
<?php
/**
* CurrencyCode.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Log;
/**
* Class CurrencyCode
*
* @package FireflyIII\Import\Converter
*/
class CurrencyCode extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return TransactionCurrency
*/
public function convert($value): TransactionCurrency
{
Log::debug('Going to convert currency code', ['value' => $value]);
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$currency = $repository->find(intval($this->mapping[$value]));
if (!is_null($currency->id)) {
Log::debug('Found currency by ID', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
}
// not mapped? Still try to find it first:
$currency = $repository->findByCode($value);
if (!is_null($currency->id)) {
Log::debug('Found currency by code', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
$currency = $repository->store(
[
'name' => $value,
'code' => $value,
'symbol' => $value,
]
);
$this->setCertainty(100);
return $currency;
}
}

View File

@ -1,75 +0,0 @@
<?php
/**
* CurrencyId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Log;
/**
* Class CurrencyId
*
* @package FireflyIII\Import\Converter
*/
class CurrencyId extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return TransactionCurrency
*/
public function convert($value)
{
$value = intval(trim($value));
Log::debug('Going to convert using CurrencyId', ['value' => $value]);
if ($value === 0) {
$this->setCertainty(0);
return new TransactionCurrency;
}
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$currency = $repository->find(intval($this->mapping[$value]));
if (!is_null($currency->id)) {
Log::debug('Found currency by ID', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
}
// not mapped? Still try to find it first:
$currency = $repository->find($value);
if (!is_null($currency->id)) {
Log::debug('Found currency by ID ', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
$this->setCertainty(0);
// should not really happen. If the ID does not match FF, what is FF supposed to do?
Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value));
return new TransactionCurrency;
}
}

View File

@ -1,81 +0,0 @@
<?php
/**
* CurrencyName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Log;
/**
* Class CurrencyName
*
* @package FireflyIII\Import\Converter
*/
class CurrencyName extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return TransactionCurrency
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using CurrencyName', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new TransactionCurrency;
}
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$currency = $repository->find(intval($this->mapping[$value]));
if (!is_null($currency->id)) {
Log::debug('Found currency by ID', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
}
// not mapped? Still try to find it first:
$currency = $repository->findByName($value);
if (!is_null($currency->id)) {
Log::debug('Found currency by name ', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
// create new currency
$currency = $repository->store(
[
'name' => $value,
'code' => strtoupper(substr($value, 0, 3)),
'symbol' => strtoupper(substr($value, 0, 1)),
]
);
$this->setCertainty(100);
return $currency;
}
}

View File

@ -1,81 +0,0 @@
<?php
/**
* CurrencySymbol.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Log;
/**
* Class CurrencySymbol
*
* @package FireflyIII\Import\Converter
*/
class CurrencySymbol extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return TransactionCurrency
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using CurrencySymbol', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new TransactionCurrency;
}
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$currency = $repository->find(intval($this->mapping[$value]));
if (!is_null($currency->id)) {
Log::debug('Found currency by ID', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
}
// not mapped? Still try to find it first:
$currency = $repository->findBySymbol($value);
if (!is_null($currency->id)) {
Log::debug('Found currency by symbol ', ['id' => $currency->id]);
$this->setCertainty(100);
return $currency;
}
// create new currency
$currency = $repository->store(
[
'name' => 'Currency ' . $value,
'code' => $value,
'symbol' => $value,
]
);
$this->setCertainty(100);
return $currency;
}
}

View File

@ -1,53 +0,0 @@
<?php
/**
* Date.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use InvalidArgumentException;
use Log;
/**
* Class Date
*
* @package FireflyIII\Import\Converter
*/
class Date extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Carbon
* @throws FireflyException
*/
public function convert($value): Carbon
{
Log::debug('Going to convert date', ['value' => $value]);
Log::debug('Format: ', ['format' => $this->config['date-format']]);
try {
$date = Carbon::createFromFormat($this->config['date-format'], $value);
} catch (InvalidArgumentException $e) {
Log::info($e->getMessage());
Log::info('Cannot convert this string using the given format.', ['value' => $value, 'format' => $this->config['date-format']]);
$this->setCertainty(0);
return new Carbon;
}
Log::debug('Converted date', ['converted' => $date->toAtomString()]);
$this->setCertainty(100);
return $date;
}
}

View File

@ -1,39 +0,0 @@
<?php
/**
* Description.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
/**
* Class Description
*
* @package FireflyIII\Import\Converter
*/
class Description extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return string
*/
public function convert($value): string
{
// this should replace all control characters
// but leave utf8 intact:
$value = preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', $value);
$this->setCertainty(100);
return strval($value);
}
}

View File

@ -1,39 +0,0 @@
<?php
/**
* ExternalId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
/**
* Class ExternalId
*
* @package FireflyIII\Import\Converter
*/
class ExternalId extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return string
*/
public function convert($value): string
{
// this should replace all control characters
// but leave utf8 intact:
$value = preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', $value);
$this->setCertainty(100);
return strval(trim($value));
}
}

View File

@ -20,7 +20,7 @@ use Log;
*
* @package FireflyIII\Import\Converter
*/
class INGDebetCredit extends BasicConverter implements ConverterInterface
class INGDebetCredit implements ConverterInterface
{
/**
@ -34,12 +34,10 @@ class INGDebetCredit extends BasicConverter implements ConverterInterface
if ($value === 'Af') {
Log::debug('Return -1');
$this->setCertainty(100);
return -1;
}
$this->setCertainty(100);
Log::debug('Return 1');
return 1;

View File

@ -1,34 +0,0 @@
<?php
/**
* Ignore.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
/**
* Class Ignore
*
* @package FireflyIII\Import\Converter
*/
class Ignore extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return null
*/
public function convert($value)
{
return null;
}
}

View File

@ -1,91 +0,0 @@
<?php
/**
* OpposingAccountIban.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Log;
/**
* Class OpposingAccountIban
*
* @package FireflyIII\Import\Converter
*/
class OpposingAccountIban extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Account
*/
public function convert($value): Account
{
$value = trim($value);
Log::debug('Going to convert opposing IBAN', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Account;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$account = $repository->find(intval($this->mapping[$value]));
if (!is_null($account->id)) {
Log::debug('Found account by ID', ['id' => $account->id]);
$this->setCertainty(100);
return $account;
}
}
// not mapped? Still try to find it first:
$account = $repository->findByIban($value, []);
if (!is_null($account->id)) {
Log::debug('Found account by IBAN', ['id' => $account->id]);
Log::info(
'The match between IBAN and account is uncertain because the type of transactions may not have been determined.',
['id' => $account->id, 'iban' => $value]
);
$this->setCertainty(50);
return $account;
}
// the IBAN given may not be a valid IBAN. If not, we cannot store by
// iban and we have no opposing account. There should be some kind of fall back
// routine.
try {
$account = $repository->store(
['name' => $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true,
'openingBalance' => 0]
);
$this->setCertainty(100);
} catch (FireflyException $e) {
Log::error($e);
$account = new Account;
}
return $account;
}
}

View File

@ -1,89 +0,0 @@
<?php
/**
* OpposingAccountName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Log;
/**
* Class OpposingAccountName
*
* @package FireflyIII\Import\Converter
*/
class OpposingAccountName extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Account
*/
public function convert($value): Account
{
$value = trim($value);
Log::debug('Going to convert opposing account name', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Account;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$account = $repository->find(intval($this->mapping[$value]));
if (!is_null($account->id)) {
Log::debug('Found account by ID', ['id' => $account->id]);
$this->setCertainty(100);
return $account;
}
}
// not mapped? Still try to find it first:
$account = $repository->findByName($value, []);
if (!is_null($account->id)) {
Log::debug('Found opposing account by name', ['id' => $account->id]);
Log::info(
'The match between name and account is uncertain because the type of transactions may not have been determined.',
['id' => $account->id, 'name' => $value]
);
$this->setCertainty(50);
return $account;
}
$account = $repository->store(
['name' => $value, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true,
'openingBalance' => 0,
]
);
if (is_null($account->id)) {
$this->setCertainty(0);
return new Account;
}
$this->setCertainty(100);
Log::debug('Created new opposing account ', ['name' => $account->name, 'id' => $account->id]);
return $account;
}
}

View File

@ -1,91 +0,0 @@
<?php
/**
* OpposingAccountNumber.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Log;
/**
* Class OpposingAccountNumber
*
* @package FireflyIII\Import\Converter
*/
class OpposingAccountNumber extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Account
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using OpposingAccountNumber', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Account;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
if (isset($this->mapping[$value])) {
Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]);
$account = $repository->find(intval($this->mapping[$value]));
if (!is_null($account->id)) {
Log::debug('Found account by ID', ['id' => $account->id]);
$this->setCertainty(100);
return $account;
}
}
// not mapped? Still try to find it first:
$account = $repository->findByAccountNumber($value, []);
if (!is_null($account->id)) {
Log::debug('Found account by number', ['id' => $account->id]);
$this->setCertainty(50);
return $account;
}
// try to find by the name we would give it:
$accountName = 'Import account with number ' . e($value);
$account = $repository->findByName($accountName, [AccountType::IMPORT]);
if (!is_null($account->id)) {
Log::debug('Found account by name', ['id' => $account->id]);
$this->setCertainty(50);
return $account;
}
$account = $repository->store(
['name' => $accountName, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id,
'accountType' => 'import',
'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true]
);
$this->setCertainty(100);
return $account;
}
}

View File

@ -20,7 +20,7 @@ use Log;
*
* @package FireflyIII\Import\Converter
*/
class RabobankDebetCredit extends BasicConverter implements ConverterInterface
class RabobankDebetCredit implements ConverterInterface
{
/**
@ -34,13 +34,11 @@ class RabobankDebetCredit extends BasicConverter implements ConverterInterface
if ($value === 'D') {
Log::debug('Return -1');
$this->setCertainty(100);
return -1;
}
Log::debug('Return 1');
$this->setCertainty(100);
return 1;
}

View File

@ -1,86 +0,0 @@
<?php
/**
* TagSplit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
/**
* Class TagSplit
*
* @package FireflyIII\Import\Converter
*/
class TagSplit
{
/**
* @param User $user
* @param array $mapping
* @param array $parts
*
* @return Collection
*/
public static function createSetFromSplits(User $user, array $mapping, array $parts): Collection
{
$set = new Collection;
Log::debug('Exploded parts.', $parts);
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser($user);
/** @var string $part */
foreach ($parts as $part) {
if (isset($mapping[$part])) {
Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $mapping[$part]]);
$tag = $repository->find(intval($mapping[$part]));
if (!is_null($tag->id)) {
Log::debug('Found tag by ID', ['id' => $tag->id]);
$set->push($tag);
continue;
}
}
// not mapped? Still try to find it first:
$tag = $repository->findByTag($part);
if (!is_null($tag->id)) {
Log::debug('Found tag by name ', ['id' => $tag->id]);
$set->push($tag);
}
if (is_null($tag->id)) {
// create new tag
$tag = $repository->store(
[
'tag' => $part,
'date' => null,
'description' => $part,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
]
);
Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]);
$set->push($tag);
}
}
return $set;
}
}

View File

@ -1,48 +0,0 @@
<?php
/**
* TagsComma.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use Illuminate\Support\Collection;
use Log;
/**
* Class TagsComma
*
* @package FireflyIII\Import\Converter
*/
class TagsComma extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Collection
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using TagsComma', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Collection;
}
$parts = array_unique(explode(',', $value));
$set = TagSplit::createSetFromSplits($this->user, $this->mapping, $parts);
$this->setCertainty(100);
return $set;
}
}

View File

@ -1,49 +0,0 @@
<?php
/**
* TagsSpace.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Converter;
use Illuminate\Support\Collection;
use Log;
/**
* Class TagsSpace
*
* @package FireflyIII\Import\Converter
*/
class TagsSpace extends BasicConverter implements ConverterInterface
{
/**
* @param $value
*
* @return Collection
*/
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using TagsSpace', ['value' => $value]);
if (strlen($value) === 0) {
$this->setCertainty(0);
return new Collection;
}
$parts = array_unique(explode(' ', $value));
$set = TagSplit::createSetFromSplits($this->user, $this->mapping, $parts);
$this->setCertainty(100);
return $set;
}
}

View File

@ -0,0 +1,235 @@
<?php
/**
* CsvProcessor.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\FileProcessor;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Object\ImportJournal;
use FireflyIII\Import\Specifics\SpecificInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionJournalMeta;
use Illuminate\Support\Collection;
use Iterator;
use League\Csv\Reader;
use Log;
/**
* Class CsvProcessor, as the name suggests, goes over CSV file line by line and creates
* "ImportJournal" objects, which are used in another step to create new journals and transactions
* and what-not.
*
* @package FireflyIII\Import\FileProcessor
*/
class CsvProcessor implements FileProcessorInterface
{
/** @var ImportJob */
private $job;
/** @var Collection */
private $objects;
/** @var array */
private $validConverters = [];
/** @var array */
private $validSpecifics = [];
/**
* FileProcessorInterface constructor.
*/
public function __construct()
{
$this->objects = new Collection;
$this->validSpecifics = array_keys(config('csv.import_specifics'));
$this->validConverters = array_keys(config('csv.import_roles'));
}
/**
* @return Collection
*/
public function getObjects(): Collection
{
return $this->objects;
}
/**
* Does the actual job:
*
* @return bool
*/
public function run(): bool
{
Log::debug('Now in CsvProcessor run(). Job is now running...');
$entries = $this->getImportArray();
$index = 0;
Log::notice('Building importable objects from CSV file.');
foreach ($entries as $index => $row) {
// verify if not exists already:
if ($this->rowAlreadyImported($row)) {
$message = sprintf('Row #%d has already been imported.', $index);
$this->job->addError($index, $message);
$this->job->addStepsDone(5); // all steps.
Log::info($message);
continue;
}
$this->objects->push($this->importRow($index, $row));
$this->job->addStepsDone(1);
}
// if job has no step count, set it now:
$extended = $this->job->extended_status;
if ($extended['steps'] === 0) {
$extended['steps'] = $index * 5;
$this->job->extended_status = $extended;
$this->job->save();
}
return true;
}
/**
* @param ImportJob $job
*
* @return FileProcessorInterface
*/
public function setJob(ImportJob $job): FileProcessorInterface
{
$this->job = $job;
return $this;
}
/**
* Add meta data to the individual value and verify that it can be handled in a later stage.
*
* @param int $index
* @param string $value
*
* @return array
* @throws FireflyException
*/
private function annotateValue(int $index, string $value)
{
$value = trim($value);
$config = $this->job->configuration;
$role = $config['column-roles'][$index] ?? '_ignore';
$mapped = $config['column-mapping-config'][$index][$value] ?? null;
// throw error when not a valid converter.
if (!in_array($role, $this->validConverters)) {
throw new FireflyException(sprintf('"%s" is not a valid role.', $role));
}
$entry = [
'role' => $role,
'value' => $value,
'mapped' => $mapped,
];
return $entry;
}
/**
* @return Iterator
*/
private function getImportArray(): Iterator
{
$content = $this->job->uploadFileContents();
$config = $this->job->configuration;
$reader = Reader::createFromString($content);
$reader->setDelimiter($config['delimiter']);
$start = $config['has-headers'] ? 1 : 0;
$results = $reader->setOffset($start)->fetch();
Log::debug(sprintf('Created a CSV reader starting at offset %d', $start));
return $results;
}
/**
* Take a row, build import journal by annotating each value and storing it in the import journal.
*
* @param int $index
* @param array $row
*
* @return ImportJournal
*/
private function importRow(int $index, array $row): ImportJournal
{
Log::debug(sprintf('Now at row %d', $index));
$row = $this->specifics($row);
$journal = new ImportJournal;
$journal->setUser($this->job->user);
$journal->setHash(hash('sha256', json_encode($row)));
foreach ($row as $rowIndex => $value) {
$value = trim($value);
if (strlen($value) > 0) {
$annotated = $this->annotateValue($rowIndex, $value);
Log::debug('Annotated value', $annotated);
$journal->setValue($annotated);
}
}
Log::debug('ImportJournal complete, returning.');
return $journal;
}
/**
* Checks if the row has not been imported before.
*
* @param array $array
*
* @return bool
*/
private function rowAlreadyImported(array $array): bool
{
$string = json_encode($array);
$hash = hash('sha256', json_encode($string));
$json = json_encode($hash);
$entry = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
->where('data', $json)
->where('name', 'importHash')
->first();
if (!is_null($entry)) {
return true;
}
return false;
}
/**
* And this is the point where the specifix go to work.
*
* @param array $row
*
* @return array
* @throws FireflyException
*/
private function specifics(array $row): array
{
$config = $this->job->configuration;
//
foreach ($config['specifics'] as $name => $enabled) {
if (!in_array($name, $this->validSpecifics)) {
throw new FireflyException(sprintf('"%s" is not a valid class name', $name));
}
/** @var SpecificInterface $specific */
$specific = app('FireflyIII\Import\Specifics\\' . $name);
// it returns the row, possibly modified:
$row = $specific->run($row);
}
return $row;
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* FileProcessorInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import\FileProcessor;
use FireflyIII\Models\ImportJob;
use Illuminate\Support\Collection;
/**
* Interface FileProcessorInterface
*
* @package FireflyIII\Import\FileProcessor
*/
interface FileProcessorInterface
{
/**
* @return Collection
*/
public function getObjects(): Collection;
/**
* @return bool
*/
public function run(): bool;
/**
* @param ImportJob $job
*
* @return FileProcessorInterface
*/
public function setJob(ImportJob $job): FileProcessorInterface;
}

View File

@ -1,274 +0,0 @@
<?php
/**
* ImportEntry.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Import;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
/**
* Class ImportEntry
*
* @package FireflyIII\Import
*/
class ImportEntry
{
/** @var array */
public $certain = [];
/** @var Collection */
public $errors;
/** @var string */
public $externalID;
/** @var array */
public $fields = [];
/** @var string */
public $hash;
/** @var User */
public $user;
/** @var bool */
public $valid = true;
/** @var int */
private $amountMultiplier = 0;
/** @var array */
private $validFields
= ['amount',
'date-transaction',
'date-interest',
'date-book',
'description',
'date-process',
'transaction-type',
'currency', 'asset-account', 'opposing-account', 'bill', 'budget', 'category', 'tags'];
/**
* ImportEntry constructor.
*/
public function __construct()
{
/** @var string $value */
foreach ($this->validFields as $value) {
$this->fields[$value] = null;
$this->certain[$value] = 0;
}
$this->errors = new Collection;
}
/**
* @param string $role
* @param int $certainty
* @param $convertedValue
*
* @throws FireflyException
*/
public function importValue(string $role, int $certainty, $convertedValue)
{
switch ($role) {
default:
Log::error('Import entry cannot handle object.', ['role' => $role]);
throw new FireflyException('Import entry cannot handle object of type "' . $role . '".');
case 'hash':
$this->hash = $convertedValue;
return;
case 'amount':
/*
* Easy enough.
*/
$this->setFloat('amount', $convertedValue, $certainty);
$this->applyMultiplier('amount'); // if present.
return;
case 'account-id':
case 'account-iban':
case 'account-name':
case 'account-number':
$this->setObject('asset-account', $convertedValue, $certainty);
break;
case 'opposing-number':
case 'opposing-iban':
case 'opposing-id':
case 'opposing-name':
$this->setObject('opposing-account', $convertedValue, $certainty);
break;
case 'bill-id':
case 'bill-name':
$this->setObject('bill', $convertedValue, $certainty);
break;
case 'budget-id':
case 'budget-name':
$this->setObject('budget', $convertedValue, $certainty);
break;
case 'category-id':
case 'category-name':
$this->setObject('category', $convertedValue, $certainty);
break;
case 'currency-code':
case 'currency-id':
case 'currency-name':
case 'currency-symbol':
$this->setObject('currency', $convertedValue, $certainty);
break;
case 'date-transaction':
$this->setDate('date-transaction', $convertedValue, $certainty);
break;
case 'date-interest':
$this->setDate('date-interest', $convertedValue, $certainty);
break;
case 'date-book':
$this->setDate('date-book', $convertedValue, $certainty);
break;
case 'date-process':
$this->setDate('date-process', $convertedValue, $certainty);
break;
case 'sepa-ct-id':
case 'sepa-db':
case 'sepa-ct-op':
case 'description':
$this->setAppendableString('description', $convertedValue);
break;
case '_ignore':
break;
case 'ing-debet-credit':
case 'rabo-debet-credit':
$this->manipulateFloat('amount', 'multiply', $convertedValue);
$this->applyMultiplier('amount'); // if present.
break;
case 'tags-comma':
case 'tags-space':
$this->appendCollection('tags', $convertedValue);
break;
case 'external-id':
$this->externalID = $convertedValue;
break;
}
}
/**
* @param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* @param string $field
* @param Collection $convertedValue
*/
private function appendCollection(string $field, Collection $convertedValue)
{
if (is_null($this->fields[$field])) {
$this->fields[$field] = new Collection;
}
$this->fields[$field] = $this->fields[$field]->merge($convertedValue);
}
/**
* @param string $field
*/
private function applyMultiplier(string $field)
{
if ($this->fields[$field] != 0 && $this->amountMultiplier != 0) {
$this->fields[$field] = $this->fields[$field] * $this->amountMultiplier;
}
}
/**
* @param string $field
* @param string $action
* @param $convertedValue
*
* @throws FireflyException
*/
private function manipulateFloat(string $field, string $action, $convertedValue)
{
switch ($action) {
default:
Log::error('Cannot handle manipulateFloat', ['field' => $field, 'action' => $action]);
throw new FireflyException('Cannot manipulateFloat with action ' . $action);
case 'multiply':
$this->amountMultiplier = $convertedValue;
break;
}
}
/**
* @param string $field
* @param string $value
*/
private function setAppendableString(string $field, string $value)
{
$value = trim($value);
$this->fields[$field] .= ' ' . $value;
}
/**
* @param string $field
* @param Carbon $date
* @param int $certainty
*/
private function setDate(string $field, Carbon $date, int $certainty)
{
if ($certainty > $this->certain[$field] && !is_null($date)) {
Log::debug(sprintf('ImportEntry: %s is now %s with certainty %d', $field, $date->format('Y-m-d'), $certainty));
$this->fields[$field] = $date;
$this->certain[$field] = $certainty;
return;
}
Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field]));
}
/**
* @param string $field
* @param float $value
* @param int $certainty
*/
private function setFloat(string $field, float $value, int $certainty)
{
if ($certainty > $this->certain[$field]) {
Log::debug(sprintf('ImportEntry: %s is now %f with certainty %d', $field, $value, $certainty));
$this->fields[$field] = $value;
$this->certain[$field] = $certainty;
return;
}
Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d).', $field, $certainty, $this->certain[$field]));
}
/**
* @param string $field
* @param $object
* @param int $certainty
*/
private function setObject(string $field, $object, int $certainty)
{
if ($certainty > $this->certain[$field] && !is_null($object->id)) {
Log::debug(sprintf('ImportEntry: %s ID is now %d with certainty %d', $field, $object->id, $certainty));
$this->fields[$field] = $object;
$this->certain[$field] = $certainty;
return;
}
Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field]));
}
}

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