Merge branch 'release/4.6.3'

This commit is contained in:
James Cole 2017-07-23 10:01:48 +02:00
commit 57d6677131
202 changed files with 5300 additions and 1827 deletions

View File

@ -6,14 +6,6 @@
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
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
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
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.

11
.github/SUPPORT.md vendored Normal file
View File

@ -0,0 +1,11 @@
# Welcome to Firefly III on Github!
:+1::tada: Thank you for taking the time to contribute something to Firefly III!
## Bugs
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
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.

1
.gitignore vendored
View File

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

View File

@ -2,6 +2,36 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.6.3] - 2017-07-23
This will be the last release to support PHP 7.0.
### Added
- New guidelines and new introduction tour to aid new users.
- Rules can now be applied at will to transactions, not just rule groups.
### Changed
- Improved category overview.
- Improved budget overview.
- Improved budget report.
- Improved command line import responsiveness and speed.
- All code comparisons are now strict.
- Improve search page.
- Charts are easier to read thanks to @simonsmiley
- Fixed #708.
### Fixed
- Fixed bug where import would not respect default account. #694
- Fixed various broken paths
- Fixed several import inconsistencies.
- Various bug fixes.
### Security
- Initial release.
## [4.6.2] - 2017-07-08
### Added
- Links added to boxes, idea by @simonsmiley

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
@ -51,6 +55,7 @@ class CreateImport extends Command
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
*/
public function handle()
{
@ -73,30 +78,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 = '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

@ -36,6 +36,7 @@ use Steam;
/**
* Class UpgradeDatabase
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things.
*
* @package FireflyIII\Console\Commands
*/
@ -80,6 +81,8 @@ class UpgradeDatabase extends Command
/**
* Moves the currency id info to the transaction instead of the journal.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped.
*/
private function currencyInfoToTransactions()
{
@ -96,7 +99,6 @@ class UpgradeDatabase extends Command
}
}
// read and use the foreign amounts when present.
if ($journal->hasMeta('foreign_amount')) {
$amount = Steam::positive($journal->getMeta('foreign_amount'));
@ -123,6 +125,8 @@ class UpgradeDatabase extends Command
/**
* Migrate budget repetitions to new format.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 5.
*/
private function migrateRepetitions()
{
@ -150,6 +154,8 @@ class UpgradeDatabase extends Command
/**
* Make sure there are only transfers linked to piggy bank events.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped.
*/
private function repairPiggyBanks()
{
@ -157,6 +163,7 @@ class UpgradeDatabase extends Command
if (!Schema::hasTable('piggy_bank_events')) {
return;
}
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
/** @var PiggyBankEvent $event */
foreach ($set as $event) {
@ -208,7 +215,7 @@ class UpgradeDatabase extends Command
}
/**
*
* Make sure all accounts have proper currency info.
*/
private function updateAccountCurrencies()
{
@ -218,42 +225,38 @@ class UpgradeDatabase extends Command
/** @var Account $account */
foreach ($accounts as $account) {
// get users preference, fall back to system pref.
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
$accountCurrency = intval($account->getMeta('currency_id'));
$openingBalance = $account->getOpeningBalance();
$openingBalanceCurrency = intval($openingBalance->transaction_currency_id);
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
$accountCurrency = intval($account->getMeta('currency_id'));
$openingBalance = $account->getOpeningBalance();
$obCurrency = intval($openingBalance->transaction_currency_id);
// both 0? set to default currency:
if ($accountCurrency === 0 && $openingBalanceCurrency === 0) {
if ($accountCurrency === 0 && $obCurrency === 0) {
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]);
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
continue;
}
// opening balance 0, account not zero? just continue:
if ($accountCurrency > 0 && $openingBalanceCurrency === 0) {
continue;
}
// account is set to 0, opening balance is not?
if ($accountCurrency === 0 && $openingBalanceCurrency > 0) {
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $openingBalanceCurrency]);
if ($accountCurrency === 0 && $obCurrency > 0) {
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
continue;
}
// both are equal, just continue:
if ($accountCurrency === $openingBalanceCurrency) {
continue;
}
// do not match:
if ($accountCurrency !== $openingBalanceCurrency) {
if ($accountCurrency !== $obCurrency) {
// update opening balance:
$openingBalance->transaction_currency_id = $accountCurrency;
$openingBalance->save();
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
continue;
}
// opening balance 0, account not zero? just continue:
// both are equal, just continue:
}
}

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

@ -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,7 +110,14 @@ 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;
foreach ($data as $key => $value) {

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

@ -89,8 +89,6 @@ class JournalCollector implements JournalCollectorInterface
'account_types.type as account_type',
];
/** @var bool */
private $filterInternalTransfers;
/** @var bool */
private $filterTransfers = false;
/** @var array */
private $filters = [InternalTransferFilter::class];
@ -508,7 +506,8 @@ class JournalCollector implements JournalCollectorInterface
->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

@ -27,6 +27,7 @@ use Route;
*/
class Help implements HelpInterface
{
const CACHEKEY = 'help_%s_%s';
/** @var string */
protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36';
@ -38,7 +39,7 @@ class Help implements HelpInterface
*/
public function getFromCache(string $route, string $language): string
{
$line = sprintf('help.%s.%s', $route, $language);
$line = sprintf(self::CACHEKEY, $route, $language);
return Cache::get($line);
}
@ -64,12 +65,12 @@ class Help implements HelpInterface
return '';
}
Log::debug(sprintf('Status code is %d', $result->status_code));
if ($result->status_code === 200) {
$content = trim($result->body);
}
if (strlen($content) > 0) {
Log::debug('Content is longer than zero. Expect something.');
$converter = new CommonMarkConverter();
@ -98,7 +99,7 @@ class Help implements HelpInterface
*/
public function inCache(string $route, string $language): bool
{
$line = sprintf('help.%s.%s', $route, $language);
$line = sprintf(self::CACHEKEY, $route, $language);
$result = Cache::has($line);
if ($result) {
Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language));
@ -120,7 +121,7 @@ class Help implements HelpInterface
*/
public function putInCache(string $route, string $language, string $content)
{
$key = sprintf('help.%s.%s', $route, $language);
$key = sprintf(self::CACHEKEY, $route, $language);
if (strlen($content) > 0) {
Log::debug(sprintf('Will store entry in cache: %s', $key));
Cache::put($key, $content, 10080); // a week.

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

@ -194,7 +194,7 @@ class AccountController extends Controller
return view(
'accounts.edit', compact(
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles'
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles'
)
);
}
@ -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

@ -153,7 +153,7 @@ 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

@ -81,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();
@ -293,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;
@ -318,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)]
@ -357,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;
@ -384,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;
@ -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;
@ -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

@ -124,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.');
}

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;
}

View File

@ -247,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

@ -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,10 +18,13 @@ 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;
use Illuminate\Routing\Controller as BaseController;
use Log;
use Route;
use Session;
use URL;
use View;
@ -47,22 +50,50 @@ class Controller extends BaseController
*/
public function __construct()
{
// for transaction lists:
View::share('hideBudgets', false);
View::share('hideCategories', false);
View::share('hideBills', false);
View::share('hideTags', false);
// is site a demo site?
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
View::share('IS_DEMO_SITE', $isDemoSite);
View::share('DEMO_USERNAME', env('DEMO_USERNAME', ''));
View::share('DEMO_PASSWORD', env('DEMO_PASSWORD', ''));
// translations:
$this->middleware(
function ($request, $next) {
// translations for specific strings:
$this->monthFormat = (string)trans('config.month');
$this->monthAndDayFormat = (string)trans('config.month_and_day');
$this->dateTimeFormat = (string)trans('config.date_time');
// get shown-intro-preference:
if (auth()->check()) {
// some routes have a "what" parameter, which indicates a special page:
$specificPage = is_null(Route::current()->parameter('what')) ? '' : '_' . Route::current()->parameter('what');
$page = str_replace('.', '_', Route::currentRouteName());
// indicator if user has seen the help for this page ( + special page):
$key = 'shown_demo_' . $page . $specificPage;
// is there an intro for this route?
$intro = config('intro.' . $page);
$specialIntro = config('intro.' . $page . $specificPage);
$shownDemo = true;
// either must be array and either must be > 0
if ((is_array($intro) || is_array($specialIntro)) && (count($intro) > 0 || count($specialIntro) > 0)) {
$shownDemo = Preferences::get($key, false)->data;
Log::debug(sprintf('Check if user has already seen intro with key "%s". Result is %d', $key, $shownDemo));
}
View::share('shownDemo', $shownDemo);
View::share('current_route_name', $page);
View::share('original_route_name', Route::currentRouteName());
}
return $next($request);
}
);

View File

@ -25,62 +25,95 @@ use Response;
*/
class HelpController extends Controller
{
/** @var HelpInterface */
private $help;
/**
* HelpController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->help = app(HelpInterface::class);
return $next($request);
}
);
}
/**
* @param HelpInterface $help
* @param $route
*
* @return \Illuminate\Http\JsonResponse
*/
public function show(HelpInterface $help, string $route)
public function show(string $route)
{
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
$html = $this->getHelpText($route, $language);
if (!$help->hasRoute($route)) {
return Response::json(['html' => $html]);
}
/**
* @param string $route
* @param string $language
*
* @return string
*/
private function getHelpText(string $route, string $language): string
{
// get language and default variables.
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
// if no such route, log error and return default text.
if (!$this->help->hasRoute($route)) {
Log::error('No such route: ' . $route);
return Response::json($content);
return $content;
}
if ($help->inCache($route, $language)) {
$content = $help->getFromCache($route, $language);
// help content may be cached:
if ($this->help->inCache($route, $language)) {
$content = $this->help->getFromCache($route, $language);
Log::debug(sprintf('Help text %s was in cache.', $language));
return Response::json($content);
return $content;
}
$content = $help->getFromGithub($route, $language);
$notYourLanguage = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>';
// get help content from Github:
$content = $this->help->getFromGithub($route, $language);
// get backup language content (try English):
// content will have 0 length when Github failed. Try en_US when it does:
if (strlen($content) === 0) {
$language = 'en_US';
if ($help->inCache($route, $language)) {
// also check cache first:
if ($this->help->inCache($route, $language)) {
Log::debug(sprintf('Help text %s was in cache.', $language));
$content = $notYourLanguage . $help->getFromCache($route, $language);
}
if (!$help->inCache($route, $language)) {
$content = trim($notYourLanguage . $help->getFromGithub($route, $language));
$content = $this->help->getFromCache($route, $language);
return $content;
}
$content = $this->help->getFromGithub($route, $language);
}
if ($content === $notYourLanguage) {
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
// help still empty?
if (strlen($content) !== 0) {
$this->help->putInCache($route, $language, $content);
return $content;
}
$help->putInCache($route, $language, $content);
return Response::json($content);
return '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
}

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

@ -92,7 +92,7 @@ class ImportController extends Controller
Log::debug('Now in download()', ['job' => $job->key]);
$config = $job->configuration;
// TODO this is CSV import specific:
// This is CSV import specific:
$config['column-roles-complete'] = false;
$config['column-mapping-complete'] = false;
$config['initial-config-complete'] = false;

View File

@ -0,0 +1,166 @@
<?php
/**
* IntroController.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\Http\Controllers\Json;
use FireflyIII\Support\Facades\Preferences;
use Log;
use Response;
/**
* Class IntroController
*
* @package FireflyIII\Http\Controllers\Json
*/
class IntroController
{
/**
* @param string $route
* @param string $specificPage
*
* @return \Illuminate\Http\JsonResponse
*/
public function getIntroSteps(string $route, string $specificPage = '')
{
$steps = $this->getBasicSteps($route);
$specificSteps = $this->getSpecificSteps($route, $specificPage);
if (count($specificSteps) === 0) {
return Response::json($steps);
}
if ($this->hasOutroStep($route)) {
// save last step:
$lastStep = $steps[count($steps) - 1];
// remove last step:
array_pop($steps);
// merge arrays and add last step again
$steps = array_merge($steps, $specificSteps);
$steps[] = $lastStep;
}
if (!$this->hasOutroStep($route)) {
$steps = array_merge($steps, $specificSteps);
}
return Response::json($steps);
}
/**
* @param string $route
*
* @return bool
*/
public function hasOutroStep(string $route): bool
{
$routeKey = str_replace('.', '_', $route);
$elements = config(sprintf('intro.%s', $routeKey));
if (!is_array($elements)) {
return false;
}
$keys = array_keys($elements);
return in_array('outro', $keys);
}
/**
* @param string $route
* @param string $specialPage
*
* @return \Illuminate\Http\JsonResponse
*/
public function postEnable(string $route, string $specialPage = '')
{
$route = str_replace('.', '_', $route);
$key = 'shown_demo_' . $route;
if ($specialPage !== '') {
$key .= '_' . $specialPage;
}
Log::debug(sprintf('Going to mark the following route as NOT done: %s with special "%s" (%s)', $route, $specialPage, $key));
Preferences::set($key, false);
return Response::json(['message' => trans('firefly.intro_boxes_after_refresh')]);
}
/**
* @param string $route
* @param string $specialPage
*
* @return \Illuminate\Http\JsonResponse
*/
public function postFinished(string $route, string $specialPage = '')
{
$key = 'shown_demo_' . $route;
if ($specialPage !== '') {
$key .= '_' . $specialPage;
}
Log::debug(sprintf('Going to mark the following route as done: %s with special "%s" (%s)', $route, $specialPage, $key));
Preferences::set($key, true);
return Response::json(['result' => sprintf('Reported demo watched for route "%s".', $route)]);
}
/**
* @param string $route
*
* @return array
*/
private function getBasicSteps(string $route): array
{
$routeKey = str_replace('.', '_', $route);
$elements = config(sprintf('intro.%s', $routeKey));
$steps = [];
if (is_array($elements) && count($elements) > 0) {
foreach ($elements as $key => $options) {
$currentStep = $options;
// get the text:
$currentStep['intro'] = trans('intro.' . $route . '_' . $key);
// save in array:
$steps[] = $currentStep;
}
}
return $steps;
}
/**
* @param string $route
* @param string $specificPage
*
* @return array
*/
private function getSpecificSteps(string $route, string $specificPage): array
{
$steps = [];
// user is on page with specific instructions:
if (strlen($specificPage) > 0) {
$routeKey = str_replace('.', '_', $route);
$elements = config(sprintf('intro.%s', $routeKey . '_' . $specificPage));
if (is_array($elements) && count($elements) > 0) {
foreach ($elements as $key => $options) {
$currentStep = $options;
// get the text:
$currentStep['intro'] = trans('intro.' . $route . '_' . $specificPage . '_' . $key);
// save in array:
$steps[] = $currentStep;
}
}
}
return $steps;
}
}

View File

@ -15,7 +15,6 @@ namespace FireflyIII\Http\Controllers;
use Amount;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
@ -27,7 +26,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request;
use Preferences;
use Response;
/**
@ -237,16 +235,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.
*
@ -293,34 +281,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
@ -365,7 +325,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

@ -89,7 +89,7 @@ class PiggyBankController extends Controller
/** @var Carbon $date */
$date = session('end', Carbon::now()->endOfMonth());
$leftOnAccount = $piggyBank->leftOnAccount($date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount?? '0';
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = min($leftOnAccount, $leftToSave);

View File

@ -13,12 +13,18 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Http\Requests\RuleFormRequest;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
use FireflyIII\Http\Requests\TestRuleFormRequest;
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Rules\TransactionMatcher;
@ -188,6 +194,41 @@ class RuleController extends Controller
return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount'));
}
/**
* Execute the given rule on a set of existing transactions
*
* @param SelectTransactionsRequest $request
* @param AccountRepositoryInterface $repository
* @param Rule $rule
*
* @return \Illuminate\Http\RedirectResponse
* @internal param RuleGroup $ruleGroup
*/
public function execute(SelectTransactionsRequest $request, AccountRepositoryInterface $repository, Rule $rule)
{
// Get parameters specified by the user
$accounts = $repository->getAccountsById($request->get('accounts'));
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
// Create a job to do the work asynchronously
$job = new ExecuteRuleOnExistingTransactions($rule);
// Apply parameters to the job
$job->setUser(auth()->user());
$job->setAccounts($accounts);
$job->setStartDate($startDate);
$job->setEndDate($endDate);
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
// Tell the user that the job is queued
Session::flash('success', strval(trans('firefly.applied_rule_selection', ['title' => $rule->title])));
return redirect()->route('rules.index');
}
/**
* @param RuleGroupRepositoryInterface $repository
*
@ -238,6 +279,25 @@ class RuleController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param Rule $rule
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function selectTransactions(AccountRepositoryInterface $repository, Rule $rule)
{
// does the user have shared accounts?
$accounts = $repository->getAccountsByType([AccountType::ASSET]);
$accountList = ExpandedForm::makeSelectList($accounts);
$checkedAccounts = array_keys($accountList);
$first = session('first')->format('Y-m-d');
$today = Carbon::create()->format('Y-m-d');
$subTitle = (string)trans('firefly.apply_rule_selection', ['title' => $rule->title]);
return view('rules.rule.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'rule', 'subTitle'));
}
/**
* @param RuleFormRequest $request
* @param RuleRepositoryInterface $repository
@ -282,7 +342,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')]);
}
@ -294,14 +354,61 @@ class RuleController extends Controller
$matcher->setLimit($limit);
$matcher->setRange($range);
$matcher->setTriggers($triggers);
$matchingTransactions = $matcher->findMatchingTransactions();
$matchingTransactions = $matcher->findTransactionsByTriggers();
// 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', ['transactions' => $matchingTransactions])->render();
return Response::json(['html' => $view, 'warning' => $warning]);
}
/**
* This method allows the user to test a certain set of rule triggers. The rule triggers are grabbed from
* the rule itself.
*
* This method will parse and validate those rules and create a "TransactionMatcher" which will attempt
* to find transaction journals matching the users input. A maximum range of transactions to try (range) and
* a maximum number of transactions to return (limit) are set as well.
*
*
* @param Rule $rule
*
* @return \Illuminate\Http\JsonResponse
*/
public function testTriggersByRule(Rule $rule)
{
$triggers = $rule->ruleTriggers;
if (count($triggers) === 0) {
return Response::json(['html' => '', 'warning' => trans('firefly.warning_no_valid_triggers')]);
}
$limit = config('firefly.test-triggers.limit');
$range = config('firefly.test-triggers.range');
/** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class);
$matcher->setLimit($limit);
$matcher->setRange($range);
$matcher->setRule($rule);
$matchingTransactions = $matcher->findTransactionsByRule();
// Warn the user if only a subset of transactions is returned
$warning = '';
if (count($matchingTransactions) === $limit) {
$warning = trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]);
}
if (count($matchingTransactions) === 0) {
$warning = trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]);
}
@ -440,7 +547,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

@ -178,7 +178,7 @@ class RuleGroupController extends Controller
$this->dispatch($job);
// Tell the user that the job is queued
Session::flash('success', strval(trans('firefly.executed_group_on_existing_transactions', ['title' => $ruleGroup->title])));
Session::flash('success', strval(trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title])));
return redirect()->route('rules.index');
}
@ -197,7 +197,7 @@ class RuleGroupController extends Controller
$checkedAccounts = array_keys($accountList);
$first = session('first')->format('Y-m-d');
$today = Carbon::create()->format('Y-m-d');
$subTitle = (string)trans('firefly.execute_on_existing_transactions');
$subTitle = (string)trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]);
return view('rules.rule-group.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'ruleGroup', 'subTitle'));
}
@ -246,14 +246,14 @@ class RuleGroupController extends Controller
* @param RuleGroupRepositoryInterface $repository
* @param RuleGroup $ruleGroup
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup)
{
$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

@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers;
use FireflyIII\Support\Search\SearchInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Response;
use View;
/**
@ -51,47 +51,29 @@ class SearchController extends Controller
*/
public function index(Request $request, SearchInterface $searcher)
{
// yes, hard coded values:
$minSearchLen = 1;
$limit = 20;
$fullQuery = $request->get('q');
// ui stuff:
$subTitle = '';
// parse search terms:
$searcher->parseQuery($fullQuery);
$query = $searcher->getWordsAsString();
$subTitle = trans('breadcrumbs.search_result', ['query' => $query]);
// query stuff
$query = null;
$result = [];
$rawQuery = $request->get('q');
$hasModifiers = true;
$modifiers = [];
return view('search.index', compact('query', 'fullQuery', 'subTitle'));
}
if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) {
// parse query, find modifiers:
// set limit for search
$searcher->setLimit($limit);
$searcher->parseQuery($request->get('q'));
public function search(Request $request, SearchInterface $searcher)
{
$fullQuery = $request->get('query');
$transactions = $searcher->searchTransactions();
$accounts = new Collection;
$categories = new Collection;
$tags = new Collection;
$budgets = new Collection;
// parse search terms:
$searcher->parseQuery($fullQuery);
$searcher->setLimit(20);
$transactions = $searcher->searchTransactions();
$html = view('search.search', compact('transactions'))->render();
// no special search thing?
if (!$searcher->hasModifiers()) {
$hasModifiers = false;
$accounts = $searcher->searchAccounts();
$categories = $searcher->searchCategories();
$budgets = $searcher->searchBudgets();
$tags = $searcher->searchTags();
}
$query = $searcher->getWordsAsString();
$subTitle = trans('firefly.search_results_for', ['query' => $query]);
$result = ['transactions' => $transactions, 'accounts' => $accounts, 'categories' => $categories, 'budgets' => $budgets, 'tags' => $tags];
return Response::json(['count' => $transactions->count(), 'html' => $html]);
}
return view('search.index', compact('rawQuery', 'hasModifiers', 'modifiers', 'subTitle', 'limit', 'query', 'result'));
}
}

View File

@ -236,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;
@ -301,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

@ -86,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);
}
}
@ -135,8 +135,8 @@ class MassController extends Controller
* @var TransactionJournal $journal
*/
foreach ($journals as $index => $journal) {
$sources = $journal->sourceAccountList($journal);
$destinations = $journal->destinationAccountList($journal);
$sources = $journal->sourceAccountList();
$destinations = $journal->destinationAccountList();
if ($sources->count() > 1) {
$messages[] = trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]);
continue;
@ -213,11 +213,11 @@ class MassController extends Controller
if ($journal) {
// get optional fields:
$what = strtolower($journal->transactionTypeStr());
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0;
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0;
$sourceAccountName = $request->get('source_account_name')[$journal->id] ?? '';
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0;
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0;
$destAccountName = $request->get('destination_account_name')[$journal->id] ?? '';
$budgetId = $request->get('budget_id')[$journal->id] ?? 0;
$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);

View File

@ -174,7 +174,7 @@ class SingleController extends Controller
*
* @param TransactionJournal $journal
*
* @return View
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function delete(TransactionJournal $journal)
{

View File

@ -70,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;
@ -93,6 +93,7 @@ class TransactionController extends Controller
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)]
@ -130,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)]
@ -172,7 +173,7 @@ class TransactionController extends Controller
* @param TransactionJournal $journal
* @param JournalTaskerInterface $tasker
*
* @return View
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
{

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

@ -10,6 +10,7 @@
*/
declare(strict_types=1);
use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator as BreadCrumbGenerator;
use FireflyIII\Exceptions\FireflyException;
@ -680,7 +681,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'));
}
);

View File

@ -36,10 +36,10 @@ class Amount implements ConverterInterface
$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 = ',';
}

View File

@ -36,7 +36,8 @@ class CommandHandler extends AbstractProcessingHandler
{
parent::__construct();
$this->command = $command;
$this->changeLevel(env('LOG_LEVEL', 'debug'));
$this->changeLevel(env('APP_LOG_LEVEL', 'info'));
}
/**
@ -56,9 +57,10 @@ class CommandHandler extends AbstractProcessingHandler
*/
private function changeLevel(string $level)
{
$level = strtoupper($level);
if (defined(sprintf('Logger::%s', $level))) {
$this->setLevel(constant(sprintf('Logger::%s', $level)));
$level = strtoupper($level);
$reference = sprintf('\Monolog\Logger::%s', $level);
if (defined($reference)) {
$this->setLevel(constant($reference));
}
}
}

View File

@ -42,7 +42,7 @@ class AssetAccountIbans implements MapperInterface
if (strlen($iban) > 0) {
$topList[$account->id] = $account->iban . ' (' . $account->name . ')';
}
if (strlen($iban) == 0) {
if (strlen($iban) === 0) {
$list[$account->id] = $account->name;
}
}

View File

@ -48,7 +48,7 @@ class OpposingAccountIbans implements MapperInterface
if (strlen($iban) > 0) {
$topList[$account->id] = $account->iban . ' (' . $account->name . ')';
}
if (strlen($iban) == 0) {
if (strlen($iban) === 0) {
$list[$account->id] = $account->name;
}
}

View File

@ -37,6 +37,8 @@ class ImportAccount
private $accountName = [];
/** @var array */
private $accountNumber = [];
/** @var int */
private $defaultAccountId = 0;
/** @var string */
private $expectedType = '';
/** @var AccountRepositoryInterface */
@ -115,6 +117,14 @@ class ImportAccount
$this->accountNumber = $accountNumber;
}
/**
* @param int $defaultAccountId
*/
public function setDefaultAccountId(int $defaultAccountId)
{
$this->defaultAccountId = $defaultAccountId;
}
/**
* @param User $user
*/
@ -249,13 +259,22 @@ class ImportAccount
$search = intval($array['mapped']);
$account = $this->repository->find($search);
if ($account->accountType->type !== $this->expectedType) {
if (is_null($account->id)) {
Log::error(sprintf('There is no account with id #%d. Invalid mapping will be ignored!', $search));
return new Account;
}
// must be of the same type
// except when mapped is an asset, then it's fair game.
// which only shows that user must map very carefully.
if ($account->accountType->type !== $this->expectedType && $account->accountType->type !== AccountType::ASSET) {
Log::error(
sprintf(
'Mapped account #%d is of type "%s" but we expect a "%s"-account. Mapping will be ignored.', $account->id, $account->accountType->type,
$this->expectedType
)
);
return new Account;
}
@ -297,6 +316,14 @@ class ImportAccount
}
$this->expectedType = $oldExpectedType;
// if search for an asset account, fall back to given "default account" (mandatory)
if ($this->expectedType === AccountType::ASSET) {
$this->account = $this->repository->find($this->defaultAccountId);
Log::debug(sprintf('Fall back to default account #%d "%s"', $this->account->id, $this->account->name));
return true;
}
Log::debug(sprintf('Found no account of type %s so must create one ourselves.', $this->expectedType));
$data = [

View File

@ -180,12 +180,19 @@ class ImportBill
Log::debug('Finding a mapped bill based on', $array);
$search = intval($array['mapped']);
$account = $this->repository->find($search);
$search = intval($array['mapped']);
$bill = $this->repository->find($search);
Log::debug(sprintf('Found bill! #%d ("%s"). Return it', $account->id, $account->name));
if (is_null($bill->id)) {
Log::error(sprintf('There is no bill with id #%d. Invalid mapping will be ignored!', $search));
return $account;
return new Bill;
}
Log::debug(sprintf('Found bill! #%d ("%s"). Return it', $bill->id, $bill->name));
return $bill;
}
/**

View File

@ -180,12 +180,18 @@ class ImportBudget
Log::debug('Finding a mapped budget based on', $array);
$search = intval($array['mapped']);
$account = $this->repository->find($search);
$search = intval($array['mapped']);
$budget = $this->repository->find($search);
Log::debug(sprintf('Found budget! #%d ("%s"). Return it', $account->id, $account->name));
if (is_null($budget->id)) {
Log::error(sprintf('There is no budget with id #%d. Invalid mapping will be ignored!', $search));
return $account;
return new Budget;
}
Log::debug(sprintf('Found budget! #%d ("%s"). Return it', $budget->id, $budget->name));
return $budget;
}
/**

View File

@ -174,12 +174,18 @@ class ImportCategory
Log::debug('Finding a mapped category based on', $array);
$search = intval($array['mapped']);
$account = $this->repository->find($search);
$search = intval($array['mapped']);
$category = $this->repository->find($search);
Log::debug(sprintf('Found category! #%d ("%s"). Return it', $account->id, $account->name));
if (is_null($category->id)) {
Log::error(sprintf('There is no category with id #%d. Invalid mapping will be ignored!', $search));
return $account;
return new Category;
}
Log::debug(sprintf('Found category! #%d ("%s"). Return it', $category->id, $category->name));
return $category;
}
/**

View File

@ -209,6 +209,13 @@ class ImportCurrency
$search = intval($array['mapped']);
$currency = $this->repository->find($search);
if (is_null($currency->id)) {
Log::error(sprintf('There is no currency with id #%d. Invalid mapping will be ignored!', $search));
return new TransactionCurrency;
}
Log::debug(sprintf('Found currency! #%d ("%s"). Return it', $currency->id, $currency->name));
return $currency;

View File

@ -144,6 +144,18 @@ class ImportJournal
return $date;
}
/**
* @return string
*/
public function getDescription(): string
{
if ($this->description === '') {
return '(no description)';
}
return $this->description;
}
/**
* @param string $hash
*/
@ -226,7 +238,7 @@ class ImportJournal
$this->date = $array['value'];
break;
case 'description':
$this->description = $array['value'];
$this->description .= $array['value'];
break;
case 'sepa-ct-op':
case 'sepa-ct-id':

View File

@ -13,11 +13,11 @@ namespace FireflyIII\Import\Routine;
use Carbon\Carbon;
use DB;
use FireflyIII\Import\FileProcessor\FileProcessorInterface;
use FireflyIII\Import\Storage\ImportStorage;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
@ -64,14 +64,19 @@ class ImportRoutine
Log::info(sprintf('Returned %d valid objects from file processor', $this->lines));
$storage = $this->storeObjects($importObjects);
Log::debug('Back in run()');
// update job:
$this->job->status = 'finished';
$this->job->save();
Log::debug('Updated job...');
$this->journals = $storage->journals;
$this->errors = $storage->errors;
Log::debug('Going to call createImportTag()');
// create tag, link tag to all journals:
$this->createImportTag();
@ -101,7 +106,7 @@ class ImportRoutine
$processor = app($class);
$processor->setJob($this->job);
if ($this->job->status == 'configured') {
if ($this->job->status === 'configured') {
// set job as "running"...
$this->job->status = 'running';
@ -120,6 +125,7 @@ class ImportRoutine
*/
private function createImportTag(): Tag
{
Log::debug('Now in createImportTag()');
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser($this->job->user);
@ -138,14 +144,17 @@ class ImportRoutine
$this->job->extended_status = $extended;
$this->job->save();
$this->journals->each(
function (TransactionJournal $journal) use ($tag) {
$journal->tags()->save($tag);
}
);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
$journalIds = $this->journals->pluck('id')->toArray();
$tagId = $tag->id;
foreach ($journalIds as $journalId) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
}
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $this->journals->count(), $tag->id, $tag->tag));
return $tag;
}
/**
@ -160,6 +169,7 @@ class ImportRoutine
$storage->setDateFormat($this->job->configuration['date-format']);
$storage->setObjects($objects);
$storage->store();
Log::info('Back in storeObjects()');
return $storage;
}

View File

@ -103,7 +103,7 @@ class AbnAmroDescription implements SpecificInterface
$this->row[8] = $matches[4]; // 'opposing-account-name'
$this->row[7] = $matches[4]; // 'description'
if ($matches[1] == 'GEA') {
if ($matches[1] === 'GEA') {
$this->row[7] = 'GEA ' . $matches[4]; // 'description'
}

View File

@ -29,6 +29,7 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Rules\Processor;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Log;
use Steam;
@ -43,6 +44,7 @@ class ImportStorage
{
/** @var Collection */
public $errors;
/** @var Collection */
public $journals;
/** @var CurrencyRepositoryInterface */
private $currencyRepository;
@ -96,13 +98,12 @@ class ImportStorage
}
/**
* Do storage of import objects
* Do storage of import objects.
*/
public function store()
{
$this->defaultCurrency = Amount::getDefaultCurrencyByUser($this->job->user);
// routine below consists of 3 steps.
/**
* @var int $index
* @var ImportJournal $object
@ -115,8 +116,9 @@ class ImportStorage
Log::error(sprintf('Cannot import row #%d because: %s', $index, $e->getMessage()));
}
}
Log::info('ImportStorage has finished.');
return true;
}
/**
@ -127,7 +129,6 @@ class ImportStorage
protected function applyRules(TransactionJournal $journal): bool
{
if ($this->rules->count() > 0) {
/** @var Rule $rule */
foreach ($this->rules as $rule) {
Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id));
@ -164,14 +165,60 @@ class ImportStorage
$errorText = join(', ', $transaction->getErrors()->all());
throw new FireflyException($errorText);
}
Log::debug(sprintf('Created transaction with ID #%d and account #%d', $transaction->id, $accountId));
Log::debug(sprintf('Created transaction with ID #%d, account #%d, amount %s', $transaction->id, $accountId, $amount));
return true;
}
/**
* @param Collection $set
* @param ImportJournal $importJournal
*
* @return bool
*/
private function filterTransferSet(Collection $set, ImportJournal $importJournal): bool
{
$amount = Steam::positive($importJournal->getAmount());
$asset = $importJournal->asset->getAccount();
$opposing = $this->getOpposingAccount($importJournal->opposing, $amount);
$description = $importJournal->getDescription();
$filtered = $set->filter(
function (TransactionJournal $journal) use ($asset, $opposing, $description) {
$match = true;
$original = [app('steam')->tryDecrypt($journal->source_name), app('steam')->tryDecrypt($journal->destination_name)];
$compare = [$asset->name, $opposing->name];
sort($original);
sort($compare);
// description does not match? Then cannot be duplicate.
if ($journal->description !== $description) {
$match = false;
}
// not both accounts in journal? Then cannot be duplicate.
if ($original !== $compare) {
$match = false;
}
if ($match) {
return $journal;
}
return null;
}
);
if (count($filtered) > 0) {
return true;
}
return false;
}
/**
* @param ImportJournal $importJournal
*
* @param Account $account
*
* @return TransactionCurrency
*/
private function getCurrency(ImportJournal $importJournal, Account $account): TransactionCurrency
@ -300,8 +347,8 @@ class ImportStorage
private function storeImportJournal(int $index, ImportJournal $importJournal): bool
{
Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->description));
Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->getDescription()));
$importJournal->asset->setDefaultAccountId($this->job->configuration['import-account']);
$asset = $importJournal->asset->getAccount();
$amount = $importJournal->getAmount();
$currency = $this->getCurrency($importJournal, $asset);
@ -317,18 +364,30 @@ class ImportStorage
// verify that opposing account is of the correct type:
if ($opposing->accountType->type === AccountType::EXPENSE && $transactionType->type !== TransactionType::WITHDRAWAL) {
Log::error(sprintf('Row #%d is imported as a %s but opposing is an expense account. This cannot be!', $index, $transactionType->type));
$message = sprintf('Row #%d is imported as a %s but opposing is an expense account. This cannot be!', $index, $transactionType->type);
Log::error($message);
throw new FireflyException($message);
}
/*** First step done! */
$this->job->addStepsDone(1);
// could be that transfer is double: verify this.
if ($this->verifyDoubleTransfer($transactionType, $importJournal)) {
// add three steps:
$this->job->addStepsDone(3);
// throw error
throw new FireflyException('Detected a possible duplicate, skip this one.');
}
// create a journal:
$journal = new TransactionJournal;
$journal->user_id = $this->job->user_id;
$journal->transaction_type_id = $transactionType->id;
$journal->transaction_currency_id = $currency->id;
$journal->description = $importJournal->description;
$journal->description = $importJournal->getDescription();
$journal->date = $date->format('Y-m-d');
$journal->order = 0;
$journal->tag_count = 0;
@ -336,7 +395,9 @@ class ImportStorage
if (!$journal->save()) {
$errorText = join(', ', $journal->getErrors()->all());
// add three steps:
$this->job->addStepsDone(3);
// throw error
throw new FireflyException($errorText);
}
@ -371,7 +432,6 @@ class ImportStorage
// run rules:
$this->applyRules($journal);
$this->job->addStepsDone(1);
$this->journals->push($journal);
Log::info(
@ -402,4 +462,49 @@ class ImportStorage
}
}
/**
* This method checks if the given transaction is a transfer and if so, if it might be a duplicate of an already imported transfer.
* This is important for import files that cover multiple accounts (and include both A<>B and B<>A transactions).
*
* @param TransactionType $transactionType
* @param ImportJournal $importJournal
*
* @return bool
*/
private function verifyDoubleTransfer(TransactionType $transactionType, ImportJournal $importJournal): bool
{
if ($transactionType->type === TransactionType::TRANSFER) {
$amount = Steam::positive($importJournal->getAmount());
$date = $importJournal->getDate($this->dateFormat);
$set = TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions AS source', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'source.transaction_journal_id')->where('source.amount', '<', 0);
}
)
->leftJoin(
'transactions AS destination', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'destination.transaction_journal_id')->where(
'destination.amount', '>', 0
);
}
)
->leftJoin('accounts as source_accounts', 'source.account_id', '=', 'source_accounts.id')
->leftJoin('accounts as destination_accounts', 'destination.account_id', '=', 'destination_accounts.id')
->where('transaction_journals.user_id', $this->job->user_id)
->where('transaction_types.type', TransactionType::TRANSFER)
->where('transaction_journals.date', $date->format('Y-m-d'))
->where('destination.amount', $amount)
->get(
['transaction_journals.id', 'transaction_journals.encrypted', 'transaction_journals.description',
'source_accounts.name as source_name', 'destination_accounts.name as destination_name']
);
return $this->filterTransferSet($set, $importJournal);
}
return false;
}
}

View File

@ -0,0 +1,161 @@
<?php
/**
* ExecuteRuleOnExistingTransactions.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\Jobs;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Rule;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
/**
* Class ExecuteRuleOnExistingTransactions
*
* @package FireflyIII\Jobs
*/
class ExecuteRuleOnExistingTransactions extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/** @var Collection */
private $accounts;
/** @var Carbon */
private $endDate;
/** @var Rule */
private $rule;
/** @var Carbon */
private $startDate;
/** @var User */
private $user;
/**
* Create a new job instance.
*
* @param Rule $rule
*/
public function __construct(Rule $rule)
{
$this->rule = $rule;
}
/**
* @return Collection
*/
public function getAccounts(): Collection
{
return $this->accounts;
}
/**
*
* @param Collection $accounts
*/
public function setAccounts(Collection $accounts)
{
$this->accounts = $accounts;
}
/**
* @return \Carbon\Carbon
*/
public function getEndDate(): Carbon
{
return $this->endDate;
}
/**
*
* @param Carbon $date
*/
public function setEndDate(Carbon $date)
{
$this->endDate = $date;
}
/**
* @return \Carbon\Carbon
*/
public function getStartDate(): Carbon
{
return $this->startDate;
}
/**
*
* @param Carbon $date
*/
public function setStartDate(Carbon $date)
{
$this->startDate = $date;
}
/**
* @return User
*/
public function getUser(): User
{
return $this->user;
}
/**
*
* @param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// Lookup all journals that match the parameters specified
$transactions = $this->collectJournals();
$processor = Processor::make($this->rule);
// Execute the rules for each transaction
foreach ($transactions as $transaction) {
$processor->handleTransaction($transaction);
// Stop processing this group if the rule specifies 'stop_processing'
if ($processor->getRule()->stop_processing) {
break;
}
}
}
/**
* Collect all journals that should be processed
*
* @return Collection
*/
protected function collectJournals()
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setUser($this->user);
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
return $collector->getJournals();
}
}

View File

@ -80,7 +80,7 @@ class MailError extends Job implements ShouldQueue
Mail::send(
['emails.error-html', 'emails.error-text'], $args,
function (Message $message) use ($email) {
if ($email != 'mail@example.com') {
if ($email !== 'mail@example.com') {
$message->to($email, $email)->subject('Caught an error in Firely III');
}
}

View File

@ -95,7 +95,7 @@ class Account extends Model
/** @var Account $account */
foreach ($set as $account) {
if ($account->name == $fields['name']) {
if ($account->name === $fields['name']) {
return $account;
}
}
@ -116,7 +116,7 @@ class Account extends Model
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}
@ -187,7 +187,7 @@ class Account extends Model
public function getMeta(string $fieldName): string
{
foreach ($this->accountMeta as $meta) {
if ($meta->name == $fieldName) {
if ($meta->name === $fieldName) {
return strval($meta->data);
}
}

View File

@ -55,7 +55,7 @@ class Attachment extends Model
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}

View File

@ -62,7 +62,7 @@ class Bill extends Model
public static function routeBinder(Bill $value)
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}
@ -77,7 +77,7 @@ class Bill extends Model
public function getMatchAttribute($value)
{
if (intval($this->match_encrypted) == 1) {
if (intval($this->match_encrypted) === 1) {
return Crypt::decrypt($value);
}
@ -92,7 +92,7 @@ class Bill extends Model
public function getNameAttribute($value)
{
if (intval($this->name_encrypted) == 1) {
if (intval($this->name_encrypted) === 1) {
return Crypt::decrypt($value);
}

View File

@ -66,7 +66,7 @@ class Budget extends Model
$set = $query->get(['budgets.*']);
/** @var Budget $budget */
foreach ($set as $budget) {
if ($budget->name == $fields['name']) {
if ($budget->name === $fields['name']) {
return $budget;
}
}
@ -85,7 +85,7 @@ class Budget extends Model
public static function routeBinder(Budget $value)
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}

View File

@ -67,7 +67,7 @@ class Category extends Model
$set = $query->get(['categories.*']);
/** @var Category $category */
foreach ($set as $category) {
if ($category->name == $fields['name']) {
if ($category->name === $fields['name']) {
return $category;
}
}
@ -86,7 +86,7 @@ class Category extends Model
public static function routeBinder(Category $value)
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}

View File

@ -124,7 +124,7 @@ class ImportJob extends Model
if (is_null($value)) {
return [];
}
if (strlen($value) == 0) {
if (strlen($value) === 0) {
return [];
}
@ -138,7 +138,7 @@ class ImportJob extends Model
*/
public function getExtendedStatusAttribute($value)
{
if (strlen($value) == 0) {
if (strlen($value) === 0) {
return [];
}

View File

@ -58,7 +58,7 @@ class PiggyBank extends Model
public static function routeBinder(PiggyBank $value)
{
if (auth()->check()) {
if ($value->account->user_id == auth()->user()->id) {
if (intval($value->account->user_id) === auth()->user()->id) {
return $value;
}
}

View File

@ -51,7 +51,7 @@ class Rule extends Model
public static function routeBinder(Rule $value)
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}

View File

@ -52,7 +52,7 @@ class RuleGroup extends Model
public static function routeBinder(RuleGroup $value)
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}

View File

@ -66,7 +66,7 @@ class Tag extends Model
$set = $query->get(['tags.*']);
/** @var Tag $tag */
foreach ($set as $tag) {
if ($tag->tag == $fields['tag']) {
if ($tag->tag === $fields['tag']) {
return $tag;
}
}
@ -87,7 +87,7 @@ class Tag extends Model
public static function routeBinder(Tag $value)
{
if (auth()->check()) {
if ($value->user_id == auth()->user()->id) {
if (intval($value->user_id) === auth()->user()->id) {
return $value;
}
}

View File

@ -207,7 +207,7 @@ class TransactionJournal extends Model
public function isDeposit(): bool
{
if (!is_null($this->transaction_type_type)) {
return $this->transaction_type_type == TransactionType::DEPOSIT;
return $this->transaction_type_type === TransactionType::DEPOSIT;
}
return $this->transactionType->isDeposit();
@ -220,7 +220,7 @@ class TransactionJournal extends Model
public function isOpeningBalance(): bool
{
if (!is_null($this->transaction_type_type)) {
return $this->transaction_type_type == TransactionType::OPENING_BALANCE;
return $this->transaction_type_type === TransactionType::OPENING_BALANCE;
}
return $this->transactionType->isOpeningBalance();
@ -233,7 +233,7 @@ class TransactionJournal extends Model
public function isTransfer(): bool
{
if (!is_null($this->transaction_type_type)) {
return $this->transaction_type_type == TransactionType::TRANSFER;
return $this->transaction_type_type === TransactionType::TRANSFER;
}
return $this->transactionType->isTransfer();
@ -246,7 +246,7 @@ class TransactionJournal extends Model
public function isWithdrawal(): bool
{
if (!is_null($this->transaction_type_type)) {
return $this->transaction_type_type == TransactionType::WITHDRAWAL;
return $this->transaction_type_type === TransactionType::WITHDRAWAL;
}
return $this->transactionType->isWithdrawal();

View File

@ -25,6 +25,7 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Log;
use Validator;
/**
@ -178,7 +179,7 @@ class AccountRepository implements AccountRepositoryInterface
{
// update the account:
$account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false;
$account->active = $data['active'] === '1' ? true : false;
$account->virtual_balance = $data['virtualBalance'];
$account->iban = $data['iban'];
$account->save();
@ -236,7 +237,7 @@ class AccountRepository implements AccountRepositoryInterface
$data['accountType'] = $data['accountType'] ?? 'invalid';
$type = config('firefly.accountTypeByIdentifier.' . $data['accountType']);
$accountType = AccountType::whereType($type)->first();
$data['iban'] = $this->filterIban($data['iban']);
// verify account type
if (is_null($accountType)) {
throw new FireflyException(sprintf('Account type "%s" is invalid. Cannot create account.', $data['accountType']));
@ -251,16 +252,17 @@ class AccountRepository implements AccountRepositoryInterface
}
// create it:
$newAccount = new Account(
[
'user_id' => $this->user->id,
'account_type_id' => $accountType->id,
'name' => $data['name'],
'virtual_balance' => $data['virtualBalance'],
'active' => $data['active'] === true ? true : false,
'iban' => $data['iban'],
]
);
$databaseData
= [
'user_id' => $this->user->id,
'account_type_id' => $accountType->id,
'name' => $data['name'],
'virtual_balance' => $data['virtualBalance'],
'active' => $data['active'] === true ? true : false,
'iban' => $data['iban'],
];
$newAccount = new Account($databaseData);
Log::debug('Final account creation dataset', $databaseData);
$newAccount->save();
// verify its creation:
if (is_null($newAccount->id)) {
@ -268,6 +270,7 @@ class AccountRepository implements AccountRepositoryInterface
sprintf('Could not create account "%s" (%d error(s))', $data['name'], $newAccount->getErrors()->count()), $newAccount->getErrors()->toArray()
);
throw new FireflyException(sprintf('Tried to create account named "%s" but failed. The logs have more details.', $data['name']));
}
Log::debug(sprintf('Created new account #%d named "%s" of type %s.', $newAccount->id, $newAccount->name, $accountType->type));
@ -457,12 +460,12 @@ class AccountRepository implements AccountRepositoryInterface
// update transactions:
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($account->id == $transaction->account_id) {
if ($account->id === $transaction->account_id) {
$transaction->amount = $amount;
$transaction->transaction_currency_id = $currencyId;
$transaction->save();
}
if ($account->id != $transaction->account_id) {
if ($account->id !== $transaction->account_id) {
$transaction->amount = bcmul($amount, '-1');
$transaction->transaction_currency_id = $currencyId;
$transaction->save();
@ -491,4 +494,26 @@ class AccountRepository implements AccountRepositoryInterface
return false;
}
/**
* @param null|string $iban
*
* @return null|string
*/
private function filterIban(string $iban = null)
{
if (is_null($iban)) {
return null;
}
$data = ['iban' => $iban];
$rules = ['iban' => 'required|iban'];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
Log::error(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban));
return null;
}
return $iban;
}
}

View File

@ -116,7 +116,7 @@ class BillRepository implements BillRepositoryInterface
$set = $set->sortBy(
function (Bill $bill) {
$int = $bill->active == 1 ? 0 : 1;
$int = $bill->active === 1 ? 0 : 1;
return $int . strtolower($bill->name);
}
@ -168,7 +168,7 @@ class BillRepository implements BillRepositoryInterface
$set = $set->sortBy(
function (Bill $bill) {
$int = $bill->active == 1 ? 0 : 1;
$int = $bill->active === 1 ? 0 : 1;
return $int . strtolower($bill->name);
}
@ -500,7 +500,7 @@ class BillRepository implements BillRepositoryInterface
return true;
}
if ($bill->id == $journal->bill_id) {
if ($bill->id === $journal->bill_id) {
// if no match, but bill used to match, remove it:
$journal->bill_id = null;
$journal->save();

View File

@ -32,7 +32,14 @@ use Log;
*/
trait CreateJournalsTrait
{
/**
* @param User $user
* @param TransactionType $type
* @param array $data
*
* @return array
*/
abstract public function storeAccounts(User $user, TransactionType $type, array $data): array;
/**
*

View File

@ -73,7 +73,7 @@ trait UpdateJournalsTrait
protected function updateDestinationTransaction(TransactionJournal $journal, Account $account, array $data)
{
$set = $journal->transactions()->where('amount', '>', 0)->get();
if ($set->count() != 1) {
if ($set->count() !== 1) {
throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count()));
}
/** @var Transaction $transaction */
@ -98,7 +98,7 @@ trait UpdateJournalsTrait
{
// should be one:
$set = $journal->transactions()->where('amount', '<', 0)->get();
if ($set->count() != 1) {
if ($set->count() !== 1) {
throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count()));
}
/** @var Transaction $transaction */
@ -140,7 +140,7 @@ trait UpdateJournalsTrait
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->whereNotIn('tag_id', $ids)->delete();
}
// if count is zero, delete them all:
if (count($ids) == 0) {
if (count($ids) === 0) {
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->delete();
}

View File

@ -236,7 +236,7 @@ class RuleRepository implements RuleRepositoryInterface
$rule->rule_group_id = $data['rule_group_id'];
$rule->order = ($order + 1);
$rule->active = 1;
$rule->stop_processing = intval($data['stop_processing']) == 1;
$rule->stop_processing = intval($data['stop_processing']) === 1;
$rule->title = $data['title'];
$rule->description = strlen($data['description']) > 0 ? $data['description'] : null;
@ -265,7 +265,7 @@ class RuleRepository implements RuleRepositoryInterface
$ruleAction->active = 1;
$ruleAction->stop_processing = $values['stopProcessing'];
$ruleAction->action_type = $values['action'];
$ruleAction->action_value = $values['value'];
$ruleAction->action_value = is_null($values['value']) ? '' : $values['value'];
$ruleAction->save();

View File

@ -282,7 +282,7 @@ class TagRepository implements TagRepositoryInterface
* changed to an advancePayment.
*/
if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') {
if ($tag->tagMode === 'balancingAct' || $tag->tagMode === 'nothing') {
foreach ($tag->transactionjournals as $journal) {
if ($journal->isTransfer()) {
return false;
@ -394,7 +394,7 @@ class TagRepository implements TagRepositoryInterface
}
// if already has transaction journals, must match ALL asset account id's:
if ($deposits > 0 || $withdrawals == 1) {
if ($deposits > 0 || $withdrawals === 1) {
Log::debug('Need to match all asset accounts.');
return $this->matchAll($journal, $tag);

View File

@ -52,7 +52,7 @@ class RemoveTag implements ActionInterface
/** @var Tag $tag */
$tag = $journal->user->tags()->get()->filter(
function (Tag $tag) use ($name) {
return $tag->tag == $name;
return $tag->tag === $name;
}
)->first();

View File

@ -56,7 +56,7 @@ class SetBudget implements ActionInterface
$budgets = $repository->getActiveBudgets();
$budget = $budgets->filter(
function (Budget $current) use ($search) {
return $current->name == $search;
return $current->name === $search;
}
)->first();
if (is_null($budget)) {
@ -65,7 +65,7 @@ class SetBudget implements ActionInterface
return true;
}
if ($journal->transactionType->type == TransactionType::TRANSFER) {
if ($journal->transactionType->type === TransactionType::TRANSFER) {
Log::debug(sprintf('RuleAction SetBudget could not set budget of journal #%d to "%s" because journal is a transfer.', $journal->id, $search));
return true;

View File

@ -59,9 +59,11 @@ final class Processor
*
* @param Rule $rule
*
* @param bool $includeActions
*
* @return Processor
*/
public static function make(Rule $rule)
public static function make(Rule $rule, $includeActions = true)
{
Log::debug(sprintf('Making new rule from Rule %d', $rule->id));
$self = new self;
@ -72,7 +74,9 @@ final class Processor
Log::debug(sprintf('Push trigger %d', $trigger->id));
$self->triggers->push(TriggerFactory::getTrigger($trigger));
}
$self->actions = $rule->ruleActions()->orderBy('order', 'ASC')->get();
if ($includeActions) {
$self->actions = $rule->ruleActions()->orderBy('order', 'ASC')->get();
}
return $self;
}
@ -254,7 +258,7 @@ final class Processor
}
}
$result = ($hitTriggers == $foundTriggers && $foundTriggers > 0);
$result = ($hitTriggers === $foundTriggers && $foundTriggers > 0);
Log::debug('Result of triggered()', ['hitTriggers' => $hitTriggers, 'foundTriggers' => $foundTriggers, 'result' => $result]);
return $result;

View File

@ -14,6 +14,7 @@ declare(strict_types=1);
namespace FireflyIII\Rules;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Rule;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
@ -32,6 +33,8 @@ class TransactionMatcher
private $limit = 10;
/** @var int Maximum number of transaction to search in (for performance reasons) * */
private $range = 200;
/** @var Rule */
private $rule;
/** @var JournalTaskerInterface */
private $tasker;
/** @var array */
@ -50,6 +53,31 @@ class TransactionMatcher
}
/**
* This method will search the user's transaction journal (with an upper limit of $range) for
* transaction journals matching the given rule. This is accomplished by trying to fire these
* triggers onto each transaction journal until enough matches are found ($limit).
*
* @return Collection
*
*/
public function findTransactionsByRule()
{
if (count($this->rule->ruleTriggers) === 0) {
return new Collection;
}
// Variables used within the loop
$processor = Processor::make($this->rule, false);
$result = $this->runProcessor($processor);
// If the list of matchingTransactions is larger than the maximum number of results
// (e.g. if a large percentage of the transactions match), truncate the list
$result = $result->slice(0, $this->limit);
return $result;
}
/**
* This method will search the user's transaction journal (with an upper limit of $range) for
* transaction journals matching the given $triggers. This is accomplished by trying to fire these
@ -58,64 +86,15 @@ class TransactionMatcher
* @return Collection
*
*/
public function findMatchingTransactions(): Collection
public function findTransactionsByTriggers(): Collection
{
if (count($this->triggers) === 0) {
return new Collection;
}
$pageSize = min($this->range / 2, $this->limit * 2);
// Variables used within the loop
$processed = 0;
$page = 1;
$result = new Collection();
$processor = Processor::makeFromStringArray($this->triggers);
// Start a loop to fetch batches of transactions. The loop will finish if:
// - all transactions have been fetched from the database
// - the maximum number of transactions to return has been found
// - the maximum number of transactions to search in have been searched
do {
// Fetch a batch of transactions from the database
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setUser(auth()->user());
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes);
$set = $collector->getPaginatedJournals();
Log::debug(sprintf('Found %d journals to check. ', $set->count()));
// Filter transactions that match the given triggers.
$filtered = $set->filter(
function (Transaction $transaction) use ($processor) {
Log::debug(sprintf('Test these triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id));
return $processor->handleTransaction($transaction);
}
);
Log::debug(sprintf('Found %d journals that match.', $filtered->count()));
// merge:
/** @var Collection $result */
$result = $result->merge($filtered);
Log::debug(sprintf('Total count is now %d', $result->count()));
// Update counters
$page++;
$processed += count($set);
Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed));
// Check for conditions to finish the loop
$reachedEndOfList = $set->count() < 1;
$foundEnough = $result->count() >= $this->limit;
$searchedEnough = ($processed >= $this->range);
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true)));
Log::debug(sprintf('searchedEnough: %s', var_export($searchedEnough, true)));
} while (!$reachedEndOfList && !$foundEnough && !$searchedEnough);
$result = $this->runProcessor($processor);
// If the list of matchingTransactions is larger than the maximum number of results
// (e.g. if a large percentage of the transactions match), truncate the list
@ -185,5 +164,73 @@ class TransactionMatcher
return $this;
}
/**
* @param Rule $rule
*/
public function setRule(Rule $rule)
{
$this->rule = $rule;
}
/**
* @param Processor $processor
*
* @return Collection
*/
private function runProcessor(Processor $processor): Collection
{
// Start a loop to fetch batches of transactions. The loop will finish if:
// - all transactions have been fetched from the database
// - the maximum number of transactions to return has been found
// - the maximum number of transactions to search in have been searched
$pageSize = min($this->range / 2, $this->limit * 2);
$processed = 0;
$page = 1;
$result = new Collection();
do {
// Fetch a batch of transactions from the database
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setUser(auth()->user());
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes);
$set = $collector->getPaginatedJournals();
Log::debug(sprintf('Found %d journals to check. ', $set->count()));
// Filter transactions that match the given triggers.
$filtered = $set->filter(
function (Transaction $transaction) use ($processor) {
Log::debug(sprintf('Test these triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id));
return $processor->handleTransaction($transaction);
}
);
Log::debug(sprintf('Found %d journals that match.', $filtered->count()));
// merge:
/** @var Collection $result */
$result = $result->merge($filtered);
Log::debug(sprintf('Total count is now %d', $result->count()));
// Update counters
$page++;
$processed += count($set);
Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed));
// Check for conditions to finish the loop
$reachedEndOfList = $set->count() < 1;
$foundEnough = $result->count() >= $this->limit;
$searchedEnough = ($processed >= $this->range);
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true)));
Log::debug(sprintf('searchedEnough: %s', var_export($searchedEnough, true)));
} while (!$reachedEndOfList && !$foundEnough && !$searchedEnough);
return $result;
}
}

View File

@ -311,6 +311,9 @@ class Amount
$coloured = false;
$format = '<span class="text-info">%s</span>';
}
if ($transaction->transaction_type_type === TransactionType::OPENING_BALANCE) {
$amount = strval($transaction->transaction_amount);
}
$currency = new TransactionCurrency;
$currency->symbol = $transaction->transaction_currency_symbol;

View File

@ -18,7 +18,6 @@ use Cache;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Collection;
use Log;
use Preferences as Prefs;
/**
@ -75,7 +74,7 @@ class CacheProperties
*/
public function has(): bool
{
if (getenv('APP_ENV') == 'testing') {
if (getenv('APP_ENV') === 'testing') {
return false;
}
$this->md5();
@ -119,8 +118,6 @@ class CacheProperties
$this->md5 .= json_encode($property);
}
Log::debug(sprintf('Cache string is %s', $this->md5));
$this->md5 = md5($this->md5);
Log::debug(sprintf('Cache MD5 is %s', $this->md5));
}
}

View File

@ -92,7 +92,9 @@ class Map implements ConfigurationInterface
}
foreach ($this->data as $index => $entry) {
$this->data[$index]['values'] = array_unique($this->data[$index]['values']);
asort($this->data[$index]['values']);
}
// save number of rows, thus number of steps, in job:
$steps = $rowIndex * 5;
$extended = $this->job->extended_status;

View File

@ -135,7 +135,7 @@ class Roles implements ConfigurationInterface
$count = $config['column-count'];
for ($i = 0; $i < $count; $i++) {
$role = $config['column-roles'][$i] ?? '_ignore';
$mapping = $config['column-do-mapping'][$i] ?? false;
$mapping = $config['column-do-mapping'][$i] ?? false;
if ($role === '_ignore' && $mapping === true) {
$mapping = false;
@ -160,7 +160,7 @@ class Roles implements ConfigurationInterface
$count = $config['column-count'];
$toBeMapped = 0;
for ($i = 0; $i < $count; $i++) {
$mapping = $config['column-do-mapping'][$i] ?? false;
$mapping = $config['column-do-mapping'][$i] ?? false;
if ($mapping === true) {
$toBeMapped++;
}

View File

@ -96,7 +96,7 @@ class Navigation
// if the range is custom, the end of the period
// is another X days (x is the difference between start)
// and end added to $theCurrentEnd
if ($repeatFreq == 'custom') {
if ($repeatFreq === 'custom') {
/** @var Carbon $tStart */
$tStart = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $tEnd */
@ -393,7 +393,7 @@ class Navigation
return $date;
}
if ($repeatFreq == 'half-year' || $repeatFreq == '6M') {
if ($repeatFreq === 'half-year' || $repeatFreq === '6M') {
$month = $date->month;
$date->startOfYear();
if ($month >= 7) {
@ -496,7 +496,7 @@ class Navigation
return $end;
}
if ($range == '6M') {
if ($range === '6M') {
if ($start->month >= 7) {
$end->endOfYear();
@ -532,7 +532,7 @@ class Navigation
return $start;
}
if ($range == '6M') {
if ($range === '6M') {
if ($start->month >= 7) {
$start->startOfYear()->addMonths(6);

View File

@ -86,17 +86,17 @@ class Modifier
case 'date':
case 'on':
$res = self::sameDate($transaction->date, $modifier['value']);
Log::debug(sprintf('Date is %s? %s', $modifier['value'], var_export($res, true)));
Log::debug(sprintf('Date same as %s? %s', $modifier['value'], var_export($res, true)));
break;
case 'date_before':
case 'before':
$res = self::dateBefore($transaction->date, $modifier['value']);
Log::debug(sprintf('Date is %s? %s', $modifier['value'], var_export($res, true)));
Log::debug(sprintf('Date before %s? %s', $modifier['value'], var_export($res, true)));
break;
case 'date_after':
case 'after':
$res = self::dateAfter($transaction->date, $modifier['value']);
Log::debug(sprintf('Date is %s? %s', $modifier['value'], var_export($res, true)));
Log::debug(sprintf('Date before %s? %s', $modifier['value'], var_export($res, true)));
break;
}

View File

@ -17,11 +17,6 @@ namespace FireflyIII\Support\Search;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\User;
use Illuminate\Support\Collection;
@ -98,112 +93,20 @@ class Search implements SearchInterface
}
}
/**
* @return Collection
*/
public function searchAccounts(): Collection
{
$words = $this->words;
$accounts = $this->user->accounts()
->accountTypeIn([AccountType::DEFAULT, AccountType::ASSET, AccountType::EXPENSE, AccountType::REVENUE, AccountType::BENEFICIARY])
->get(['accounts.*']);
/** @var Collection $result */
$result = $accounts->filter(
function (Account $account) use ($words) {
if ($this->strpos_arr(strtolower($account->name), $words)) {
return $account;
}
return false;
}
);
$result = $result->slice(0, $this->limit);
return $result;
}
/**
* @return Collection
*/
public function searchBudgets(): Collection
{
/** @var Collection $set */
$set = auth()->user()->budgets()->get();
$words = $this->words;
/** @var Collection $result */
$result = $set->filter(
function (Budget $budget) use ($words) {
if ($this->strpos_arr(strtolower($budget->name), $words)) {
return $budget;
}
return false;
}
);
$result = $result->slice(0, $this->limit);
return $result;
}
/**
* @return Collection
*/
public function searchCategories(): Collection
{
$words = $this->words;
$categories = $this->user->categories()->get();
/** @var Collection $result */
$result = $categories->filter(
function (Category $category) use ($words) {
if ($this->strpos_arr(strtolower($category->name), $words)) {
return $category;
}
return false;
}
);
$result = $result->slice(0, $this->limit);
return $result;
}
/**
* @return Collection
*/
public function searchTags(): Collection
{
$words = $this->words;
$tags = $this->user->tags()->get();
/** @var Collection $result */
$result = $tags->filter(
function (Tag $tag) use ($words) {
if ($this->strpos_arr(strtolower($tag->tag), $words)) {
return $tag;
}
return false;
}
);
$result = $result->slice(0, $this->limit);
return $result;
}
/**
* @return Collection
*/
public function searchTransactions(): Collection
{
Log::debug('Start of searchTransactions()');
$pageSize = 100;
$processed = 0;
$page = 1;
$result = new Collection();
$startTime = microtime(true);
do {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setUser($this->user);
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page);
if ($this->hasModifiers()) {
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
@ -247,7 +150,11 @@ class Search implements SearchInterface
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true)));
} while (!$reachedEndOfList && !$foundEnough);
// break at some point so the script does not crash:
$currentTime = microtime(true) - $startTime;
Log::debug(sprintf('Have been running for %f seconds.', $currentTime));
} while (!$reachedEndOfList && !$foundEnough && $currentTime <= 30);
$result = $result->slice(0, $this->limit);

View File

@ -38,25 +38,6 @@ interface SearchInterface
*/
public function parseQuery(string $query);
/**
* @return Collection
*/
public function searchAccounts(): Collection;
/**
* @return Collection
*/
public function searchBudgets(): Collection;
/**
* @return Collection
*/
public function searchCategories(): Collection;
/**
* @return Collection
*/
public function searchTags(): Collection;
/**
* @return Collection

View File

@ -19,6 +19,7 @@ use Crypt;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Collection;
/**
@ -345,4 +346,20 @@ class Steam
return $amount;
}
/**
* @param $value
*
* @return mixed
*/
public function tryDecrypt($value)
{
try {
$value = Crypt::decrypt($value);
} catch (DecryptException $e) {
// do not care.
}
return $value;
}
}

View File

@ -110,7 +110,7 @@ class General extends Twig_Extension
$what = $args[2]; // name of the route.
$activeWhat = $context['what'] ?? false;
if ($what == $activeWhat && !(strpos(Route::getCurrentRoute()->getName(), $route) === false)) {
if ($what === $activeWhat && !(strpos(Route::getCurrentRoute()->getName(), $route) === false)) {
return 'active';
}
@ -132,7 +132,7 @@ class General extends Twig_Extension
$args = func_get_args();
$route = $args[0]; // name of the route.
if (Route::getCurrentRoute()->getName() == $route) {
if (Route::getCurrentRoute()->getName() === $route) {
return 'active';
}

View File

@ -51,7 +51,7 @@ class Journal extends Twig_Extension
$array = [];
/** @var Account $entry */
foreach ($list as $entry) {
if ($entry->accountType->type == AccountType::CASH) {
if ($entry->accountType->type === AccountType::CASH) {
$array[] = '<span class="text-success">(cash)</span>';
continue;
}
@ -123,7 +123,7 @@ class Journal extends Twig_Extension
$array = [];
/** @var Account $entry */
foreach ($list as $entry) {
if ($entry->accountType->type == 'Cash account') {
if ($entry->accountType->type === AccountType::CASH) {
$array[] = '<span class="text-success">(cash)</span>';
continue;
}

View File

@ -70,7 +70,7 @@ class Rule extends Twig_Extension
$ruleTriggers = array_keys(Config::get('firefly.rule-triggers'));
$possibleTriggers = [];
foreach ($ruleTriggers as $key) {
if ($key != 'user_action') {
if ($key !== 'user_action') {
$possibleTriggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice');
}
}

View File

@ -149,7 +149,7 @@ class User extends Authenticatable
{
foreach ($this->roles as $role) {
if ($role->name == $name) {
if ($role->name === $name) {
return true;
}
}

View File

@ -211,7 +211,7 @@ class FireflyValidator extends Validator
// count budgets, should have at least one
$count = $budgets->filter(
function (Budget $budget) use ($value) {
return $budget->name == $value;
return $budget->name === $value;
}
)->count();
@ -329,7 +329,7 @@ class FireflyValidator extends Validator
/** @var AccountMeta $entry */
foreach ($set as $entry) {
if ($entry->data == $value) {
if ($entry->data === $value) {
return false;
}
@ -398,7 +398,7 @@ class FireflyValidator extends Validator
/** @var PiggyBank $entry */
foreach ($set as $entry) {
$fieldValue = $this->tryDecrypt($entry->name);
if ($fieldValue == $value) {
if ($fieldValue === $value) {
return false;
}
}
@ -460,7 +460,7 @@ class FireflyValidator extends Validator
$set = $user->accounts()->where('account_type_id', $type->id)->get();
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $value) {
if ($entry->name === $value) {
return false;
}
}
@ -486,7 +486,7 @@ class FireflyValidator extends Validator
$set = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get();
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $value) {
if ($entry->name === $value) {
return false;
}
}
@ -510,7 +510,7 @@ class FireflyValidator extends Validator
$set = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get();
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $value) {
if ($entry->name === $value) {
return false;
}
}
@ -534,7 +534,7 @@ class FireflyValidator extends Validator
$set = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get();
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $value) {
if ($entry->name === $value) {
return false;
}
}

189
composer.lock generated
View File

@ -220,16 +220,16 @@
},
{
"name": "doctrine/cache",
"version": "v1.6.1",
"version": "v1.6.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3"
"reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3",
"reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3",
"url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b",
"reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b",
"shasum": ""
},
"require": {
@ -286,7 +286,7 @@
"cache",
"caching"
],
"time": "2016-10-29T11:16:17+00:00"
"time": "2017-07-22T12:49:21+00:00"
},
{
"name": "doctrine/collections",
@ -357,16 +357,16 @@
},
{
"name": "doctrine/common",
"version": "v2.7.2",
"version": "v2.7.3",
"source": {
"type": "git",
"url": "https://github.com/doctrine/common.git",
"reference": "930297026c8009a567ac051fd545bf6124150347"
"reference": "4acb8f89626baafede6ee5475bc5844096eba8a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/common/zipball/930297026c8009a567ac051fd545bf6124150347",
"reference": "930297026c8009a567ac051fd545bf6124150347",
"url": "https://api.github.com/repos/doctrine/common/zipball/4acb8f89626baafede6ee5475bc5844096eba8a9",
"reference": "4acb8f89626baafede6ee5475bc5844096eba8a9",
"shasum": ""
},
"require": {
@ -426,20 +426,20 @@
"persistence",
"spl"
],
"time": "2017-01-13T14:02:13+00:00"
"time": "2017-07-22T08:35:12+00:00"
},
{
"name": "doctrine/dbal",
"version": "v2.5.12",
"version": "v2.5.13",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44"
"reference": "729340d8d1eec8f01bff708e12e449a3415af873"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/7b9e911f9d8b30d43b96853dab26898c710d8f44",
"reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/729340d8d1eec8f01bff708e12e449a3415af873",
"reference": "729340d8d1eec8f01bff708e12e449a3415af873",
"shasum": ""
},
"require": {
@ -497,37 +497,37 @@
"persistence",
"queryobject"
],
"time": "2017-02-08T12:53:47+00:00"
"time": "2017-07-22T20:44:48+00:00"
},
{
"name": "doctrine/inflector",
"version": "v1.1.0",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/inflector.git",
"reference": "90b2128806bfde671b6952ab8bea493942c1fdae"
"reference": "e11d84c6e018beedd929cff5220969a3c6d1d462"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae",
"reference": "90b2128806bfde671b6952ab8bea493942c1fdae",
"url": "https://api.github.com/repos/doctrine/inflector/zipball/e11d84c6e018beedd929cff5220969a3c6d1d462",
"reference": "e11d84c6e018beedd929cff5220969a3c6d1d462",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
"php": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "4.*"
"phpunit/phpunit": "^6.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Inflector\\": "lib/"
"psr-4": {
"Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -564,7 +564,7 @@
"singularize",
"string"
],
"time": "2015-11-06T14:35:42+00:00"
"time": "2017-07-22T12:18:28+00:00"
},
{
"name": "doctrine/lexer",
@ -664,16 +664,16 @@
},
{
"name": "laravel/framework",
"version": "v5.4.28",
"version": "v5.4.30",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "442511fc62121085d184355e4f964c88942bbecb"
"reference": "b9a64955f4278f45ac348a6e000b5ecc85da167a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/442511fc62121085d184355e4f964c88942bbecb",
"reference": "442511fc62121085d184355e4f964c88942bbecb",
"url": "https://api.github.com/repos/laravel/framework/zipball/b9a64955f4278f45ac348a6e000b5ecc85da167a",
"reference": "b9a64955f4278f45ac348a6e000b5ecc85da167a",
"shasum": ""
},
"require": {
@ -789,7 +789,7 @@
"framework",
"laravel"
],
"time": "2017-06-30T13:43:07+00:00"
"time": "2017-07-19T19:26:19+00:00"
},
{
"name": "laravelcollective/html",
@ -916,16 +916,16 @@
},
{
"name": "league/csv",
"version": "8.2.1",
"version": "8.2.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "43fd8b022815a0758d85e925dd92a43fe0d41bb4"
"reference": "fa8bc05f64eb6c66b96edfaf60648f022ecb5f55"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/43fd8b022815a0758d85e925dd92a43fe0d41bb4",
"reference": "43fd8b022815a0758d85e925dd92a43fe0d41bb4",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/fa8bc05f64eb6c66b96edfaf60648f022ecb5f55",
"reference": "fa8bc05f64eb6c66b96edfaf60648f022ecb5f55",
"shasum": ""
},
"require": {
@ -969,7 +969,7 @@
"read",
"write"
],
"time": "2017-02-23T08:25:03+00:00"
"time": "2017-07-12T07:18:20+00:00"
},
{
"name": "league/flysystem",
@ -1636,7 +1636,7 @@
},
{
"name": "symfony/console",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
@ -1758,7 +1758,7 @@
},
{
"name": "symfony/debug",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
@ -1814,7 +1814,7 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v2.8.24",
"version": "v2.8.25",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
@ -1874,7 +1874,7 @@
},
{
"name": "symfony/finder",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
@ -1923,16 +1923,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "f347a5f561b03db95ed666959db42bbbf429b7e5"
"reference": "e307abe4b79ccbbfdced9b91c132fd128f456bc5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/f347a5f561b03db95ed666959db42bbbf429b7e5",
"reference": "f347a5f561b03db95ed666959db42bbbf429b7e5",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/e307abe4b79ccbbfdced9b91c132fd128f456bc5",
"reference": "e307abe4b79ccbbfdced9b91c132fd128f456bc5",
"shasum": ""
},
"require": {
@ -1972,7 +1972,7 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"time": "2017-06-24T09:29:48+00:00"
"time": "2017-07-17T14:07:10+00:00"
},
{
"name": "symfony/http-kernel",
@ -2228,16 +2228,16 @@
},
{
"name": "symfony/process",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "5ab8949b682b1bf9d4511a228b5e045c96758c30"
"reference": "07432804942b9f6dd7b7377faf9920af5f95d70a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/5ab8949b682b1bf9d4511a228b5e045c96758c30",
"reference": "5ab8949b682b1bf9d4511a228b5e045c96758c30",
"url": "https://api.github.com/repos/symfony/process/zipball/07432804942b9f6dd7b7377faf9920af5f95d70a",
"reference": "07432804942b9f6dd7b7377faf9920af5f95d70a",
"shasum": ""
},
"require": {
@ -2273,11 +2273,11 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2017-07-03T08:12:02+00:00"
"time": "2017-07-13T13:05:09+00:00"
},
{
"name": "symfony/routing",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
@ -2355,7 +2355,7 @@
},
{
"name": "symfony/translation",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
@ -2420,16 +2420,16 @@
},
{
"name": "symfony/var-dumper",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "9ee920bba1d2ce877496dcafca7cbffff4dbe08a"
"reference": "0f32b62d21991700250fed5109b092949007c5b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/9ee920bba1d2ce877496dcafca7cbffff4dbe08a",
"reference": "9ee920bba1d2ce877496dcafca7cbffff4dbe08a",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/0f32b62d21991700250fed5109b092949007c5b3",
"reference": "0f32b62d21991700250fed5109b092949007c5b3",
"shasum": ""
},
"require": {
@ -2484,7 +2484,7 @@
"debug",
"dump"
],
"time": "2017-07-05T13:02:37+00:00"
"time": "2017-07-10T14:18:27+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@ -2698,16 +2698,16 @@
"packages-dev": [
{
"name": "barryvdh/laravel-debugbar",
"version": "v2.4.1",
"version": "v2.4.3",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git",
"reference": "af98b3a4ccac9364f2145fae974ff3392ec402b1"
"reference": "d7c88f08131f6404cb714f3f6cf0642f6afa3903"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/af98b3a4ccac9364f2145fae974ff3392ec402b1",
"reference": "af98b3a4ccac9364f2145fae974ff3392ec402b1",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/d7c88f08131f6404cb714f3f6cf0642f6afa3903",
"reference": "d7c88f08131f6404cb714f3f6cf0642f6afa3903",
"shasum": ""
},
"require": {
@ -2717,19 +2717,6 @@
"symfony/finder": "~2.7|~3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
},
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},
"autoload": {
"psr-4": {
"Barryvdh\\Debugbar\\": "src/"
@ -2756,20 +2743,20 @@
"profiler",
"webprofiler"
],
"time": "2017-06-14T07:44:44+00:00"
"time": "2017-07-21T11:56:48+00:00"
},
{
"name": "barryvdh/laravel-ide-helper",
"version": "v2.4.0",
"version": "v2.4.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
"reference": "87a02ff574f722c685e011f76692ab869ab64f6c"
"reference": "2b1273c45e2f8df7a625563e2283a17c14f02ae8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/87a02ff574f722c685e011f76692ab869ab64f6c",
"reference": "87a02ff574f722c685e011f76692ab869ab64f6c",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/2b1273c45e2f8df7a625563e2283a17c14f02ae8",
"reference": "2b1273c45e2f8df7a625563e2283a17c14f02ae8",
"shasum": ""
},
"require": {
@ -2829,7 +2816,7 @@
"phpstorm",
"sublime"
],
"time": "2017-06-16T14:08:59+00:00"
"time": "2017-07-16T00:24:12+00:00"
},
{
"name": "barryvdh/reflection-docblock",
@ -3223,7 +3210,7 @@
],
"authors": [
{
"name": "Pádraic Brady",
"name": "Padraic Brady",
"email": "padraic.brady@gmail.com",
"homepage": "http://blog.astrumfutura.com"
},
@ -3347,22 +3334,22 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "3.1.1",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e"
"reference": "46f7e8bb075036c92695b15a1ddb6971c751e585"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e",
"reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/46f7e8bb075036c92695b15a1ddb6971c751e585",
"reference": "46f7e8bb075036c92695b15a1ddb6971c751e585",
"shasum": ""
},
"require": {
"php": ">=5.5",
"phpdocumentor/reflection-common": "^1.0@dev",
"phpdocumentor/type-resolver": "^0.2.0",
"phpdocumentor/type-resolver": "^0.4.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
@ -3388,24 +3375,24 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2016-09-30T07:12:33+00:00"
"time": "2017-07-15T11:38:20+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "0.2.1",
"version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb"
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
"reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
"shasum": ""
},
"require": {
"php": ">=5.5",
"php": "^5.5 || ^7.0",
"phpdocumentor/reflection-common": "^1.0"
},
"require-dev": {
@ -3435,7 +3422,7 @@
"email": "me@mikevanriel.com"
}
],
"time": "2016-11-25T06:54:22+00:00"
"time": "2017-07-14T14:27:02+00:00"
},
{
"name": "phpspec/prophecy",
@ -4463,7 +4450,7 @@
},
{
"name": "symfony/class-loader",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/class-loader.git",
@ -4519,7 +4506,7 @@
},
{
"name": "symfony/config",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
@ -4637,16 +4624,16 @@
},
{
"name": "symfony/filesystem",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "311fa718389efbd8b627c272b9324a62437018cc"
"reference": "427987eb4eed764c3b6e38d52a0f87989e010676"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/311fa718389efbd8b627c272b9324a62437018cc",
"reference": "311fa718389efbd8b627c272b9324a62437018cc",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/427987eb4eed764c3b6e38d52a0f87989e010676",
"reference": "427987eb4eed764c3b6e38d52a0f87989e010676",
"shasum": ""
},
"require": {
@ -4682,11 +4669,11 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2017-06-24T09:29:48+00:00"
"time": "2017-07-11T07:17:58+00:00"
},
{
"name": "symfony/stopwatch",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
@ -4735,7 +4722,7 @@
},
{
"name": "symfony/yaml",
"version": "v3.3.4",
"version": "v3.3.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",

View File

@ -23,7 +23,7 @@ return [
'is_demo_site' => false,
],
'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true),
'version' => '4.6.2',
'version' => '4.6.3',
'maxUploadSize' => 15242880,
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
'list_length' => 10,
@ -109,7 +109,8 @@ return [
'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true],
'pl_PL' => ['name_locale' => 'Polski', 'name_english' => 'Polish ', 'complete' => false],
'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portuguese (Brazil)', 'complete' => true],
'sl-SI' => ['name_locale' => 'Slovenščina', 'name_english' => 'Slovenian', 'complete' => false],
'ru_RU' => ['name_locale' => 'Русский', 'name_english' => 'Russian', 'complete' => false],
'sl_SI' => ['name_locale' => 'Slovenščina', 'name_english' => 'Slovenian', 'complete' => false],
'zh-TW' => ['name_locale' => '正體中文', 'name_english' => 'Chinese Traditional', 'complete' => false],
],
'transactionTypesByWhat' => [

159
config/intro.php Normal file
View File

@ -0,0 +1,159 @@
<?php
/**
* intro.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);
/*
* Always make sure intro is the first element (if any) and outro is the last one.
*/
return [
// index
'index' => [
'intro' => [],
'accounts-chart' => ['element' => '#accounts-chart'],
'box_out_holder' => ['element' => '#box_out_holder'],
'help' => ['element' => '#help', 'position' => 'bottom'],
'sidebar-toggle' => ['element' => '#sidebar-toggle', 'position' => 'bottom'],
'outro' => [],
],
// accounts: create
'accounts_create' => [
'iban' => ['element' => '#ffInput_iban'],
],
// extra text for asset account creation.
'accounts_create_asset' => [
'opening_balance' => ['element' => '#ffInput_openingBalance'],
'currency' => ['element' => '#ffInput_currency_id'],
'virtual' => ['element' => '#ffInput_virtualBalance'],
],
// budgets: index
'budgets_index' => [
'intro' => [],
'set_budget' => ['element' => '#availableBar',],
'see_expenses_bar' => ['element' => '#spentBar'],
'navigate_periods' => ['element' => '#periodNavigator'],
'new_budget' => ['element' => '#createBudgetBox'],
'list_of_budgets' => ['element' => '#budgetList'],
],
// reports: index, default report, audit, budget, cat, tag
'reports_index' => [
'intro' => [],
'inputReportType' => ['element' => '#inputReportType'],
'inputAccountsSelect' => ['element' => '#inputAccountsSelect'],
'inputDateRange' => ['element' => '#inputDateRange'],
'extra-options-box' => ['element' => '#extra-options-box', 'position' => 'top'],
],
'reports_report_default' => [
'intro' => [],
],
'reports_report_audit' => [
'intro' => [],
'optionsBox' => ['element' => '#optionsBox'],
],
'reports_report_category' => [
'intro' => [],
'pieCharts' => ['element' => '#pieCharts'],
'incomeAndExpensesChart' => ['element' => '#incomeAndExpensesChart', 'position' => 'top'],
],
'reports_report_tag' => [
'intro' => [],
'pieCharts' => ['element' => '#pieCharts'],
'incomeAndExpensesChart' => ['element' => '#incomeAndExpensesChart', 'position' => 'top'],
],
'reports_report_budget' => [
'intro' => [],
'pieCharts' => ['element' => '#pieCharts'],
'incomeAndExpensesChart' => ['element' => '#incomeAndExpensesChart', 'position' => 'top'],
],
// transactions: create (also per type!)
'transactions_create' => [
'switch_box' => ['element' => '#switch-box'],
'ffInput_category' => ['element' => '#ffInput_category'],
],
'transactions_create_withdrawal' => [
'ffInput_budget' => ['element' => '#ffInput_budget_id'],
'currency_dropdown_amount' => ['element' => '#currency_dropdown_amount'],
],
'transactions_create_deposit' => [
'currency_dropdown_amount' => ['element' => '#currency_dropdown_amount'],
],
'transactions_create_transfer' => [
'ffInput_piggy_bank_id' => ['element' => '#ffInput_piggy_bank_id'],
],
// piggies: index, create, show
'piggy-banks_index' => [
'saved' => ['element' => '.piggySaved'],
'button' => ['element' => '.piggyBar',],
'accountStatus' => ['element' => '#accountStatus', 'position' => 'top'],
],
'piggy-banks_create' => [
'name' => ['element' => '#ffInput_name'],
'date' => ['element' => '#ffInput_targetdate'],
],
'piggy-banks_show' => [
'intro' => [],
'piggyChart' => ['element' => '#piggyChart'],
'piggyDetails' => ['element' => '#piggyDetails'],
'piggyEvents' => ['element' => '#piggyEvents'],
],
// bills: index, create, show
'bills_index' => [
'paid_in_period' => ['element' => '.paid_in_period'],
'expected_in_period' => ['element' => '.expected_in_period'],
],
'bills_create' => [
'name' => ['element' => '#name_holder'],
'match' => ['element' => '#match_holder'],
'amount_min_holder' => ['element' => '#amount_min_holder'],
'repeat_freq_holder' => ['element' => '#repeat_freq_holder'],
'skip_holder' => ['element' => '#skip_holder'],
],
'bills_show' => [
'billInfo' => ['element' => '#billInfo'],
'billButtons' => ['element' => '#billButtons'],
'billChart' => ['element' => '#billChart', 'position' => 'top'],
],
// rules: index, create-rule, edit-rule
'rules_index' => [
'intro' => [],
'new_rule_group' => ['element' => '#new_rule_group'],
'new_rule' => ['element' => '.new_rule'],
'prio_buttons' => ['element' => '.prio_buttons'],
'test_buttons' => ['element' => '.test_buttons'],
'rule-triggers' => ['element' => '.rule-triggers'],
'outro' => [],
],
'rules_create' => [
'mandatory' => ['element' => '#mandatory'],
'ruletriggerholder' => ['element' => '.rule-trigger-box'],
'test_rule_triggers' => ['element' => '.test_rule_triggers'],
'actions' => ['element' => '.rule-action-box', 'position' => 'top'],
],
// preferences: index
'preferences_index' => [
'tabs' => ['element' => '.nav-tabs'],
],
// currencies: index, create
'currencies_index' => [
'intro' => [],
'default' => ['element' => '.defaultCurrency'],
],
'currencies_create' => [
'code' => ['element' => '#ffInput_code',],
],
];

View File

@ -156,6 +156,7 @@ return [
'Preferences',
'URL',
'Config',
'Request',
'ExpandedForm' => [
'is_safe' => [
'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location',

View File

@ -15,11 +15,13 @@ return [
'text' => [
'upgrade' =>
[
'4.3' => 'Make sure you run the migrations and clear your cache. If you need more help, please check Github or the Firefly III website.',
'4.3' => 'Make sure you run the migrations and clear your cache. If you need more help, please check Github or the Firefly III website.',
'4.6.3' => 'This will be the last version to require PHP7.0. Future versions will require PHP7.1 minimum.',
],
'install' =>
[
'4.3' => 'Welcome to Firefly! Make sure you follow the installation guide. If you need more help, please check Github or the Firefly III website. The installation guide has a FAQ which you should check out as well.',
'4.3' => 'Welcome to Firefly! Make sure you follow the installation guide. If you need more help, please check Github or the Firefly III website. The installation guide has a FAQ which you should check out as well.',
'4.6.3' => 'This will be the last version to require PHP7.0. Future versions will require PHP7.1 minimum.',
],
],
];

View File

@ -1,22 +0,0 @@
/* ========================================================================
* bootstrap-tour - v0.10.3
* http://bootstraptour.com
* ========================================================================
* Copyright 2012-2015 Ulrich Sossou
*
* ========================================================================
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================================
*/
.tour-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1100;background-color:#000;opacity:.8;filter:alpha(opacity=80)}.tour-step-backdrop{position:relative;z-index:1101}.tour-step-backdrop>td{position:relative;z-index:1101}.tour-step-background{position:absolute!important;z-index:1100;background:inherit;border-radius:6px}.popover[class*=tour-]{z-index:1102}.popover[class*=tour-] .popover-navigation{padding:9px 14px;overflow:hidden}.popover[class*=tour-] .popover-navigation [data-role=end]{float:right}.popover[class*=tour-] .popover-navigation [data-role=prev],.popover[class*=tour-] .popover-navigation [data-role=next],.popover[class*=tour-] .popover-navigation [data-role=end]{cursor:pointer}.popover[class*=tour-] .popover-navigation [data-role=prev].disabled,.popover[class*=tour-] .popover-navigation [data-role=next].disabled,.popover[class*=tour-] .popover-navigation [data-role=end].disabled{cursor:default}.popover[class*=tour-].orphan{position:fixed;margin-top:0}.popover[class*=tour-].orphan .arrow{display:none}

View File

@ -7,7 +7,7 @@
*
* See the LICENSE file for details.
*/
/** global: Chart, defaultChartOptions, accounting, defaultPieOptions, noDataForChart */
/** global: Chart, defaultChartOptions, accounting, defaultPieOptions, noDataForChart, noDataForChart */
var allCharts = {};
/*
@ -38,7 +38,7 @@ var strokePointHighColors = [];
for (var i = 0; i < colourSet.length; i++) {
fillColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.2)");
fillColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.5)");
strokePointHighColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.9)");
}
@ -60,7 +60,8 @@ function colorizeData(data) {
for (var i = 0; i < data.count; i++) {
newData.labels = data.labels;
var dataset = data.datasets[i];
dataset.backgroundColor = fillColors[i];
dataset.fill = false;
dataset.backgroundColor = dataset.borderColor = fillColors[i];
newData.datasets.push(dataset);
}
return newData;
@ -239,13 +240,14 @@ function pieChart(URI, container) {
* @param colorData
*/
function drawAChart(URI, container, chartType, options, colorData) {
if ($('#' + container).length === 0) {
var containerObj = $('#' + container);
if (containerObj.length === 0) {
return;
}
$.getJSON(URI).done(function (data) {
$('#' + container).removeClass('general-chart-error');
containerObj.removeClass('general-chart-error');
if (data.labels.length === 0) {
// remove the chart container + parent
var holder = $('#' + container).parent().parent();

View File

@ -15,6 +15,11 @@ $(function () {
configAccounting(currencySymbol);
// on submit of form, disable any button in form:
$('form.form-horizontal').on('submit',function() {
$('button[type="submit"]').prop('disabled',true);
});
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="_token"]').attr('content')

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