Merge branch 'release/4.3.8'

This commit is contained in:
James Cole 2017-04-08 17:54:10 +02:00
commit 970ce917b0
179 changed files with 6583 additions and 2067 deletions

View File

@ -9,7 +9,6 @@ cache:
- $HOME/.composer/cache - $HOME/.composer/cache
install: install:
- if [[ "$(php -v | grep 'PHP 7')" ]]; then phpenv config-rm xdebug.ini; fi
- rm composer.lock - rm composer.lock
- composer update --no-scripts - composer update --no-scripts
- cp .env.testing .env - cp .env.testing .env
@ -18,9 +17,13 @@ install:
- php artisan env - php artisan env
- cp .env.testing .env - cp .env.testing .env
- mv storage/database/databasecopy.sqlite storage/database/database.sqlite - mv storage/database/databasecopy.sqlite storage/database/database.sqlite
- mkdir -p build/logs
script: script:
- phpunit - phpunit -c phpunit.coverage.xml
after_success:
- travis_retry php vendor/bin/coveralls -x storage/build/clover.xml
# safelist # safelist
branches: branches:

View File

@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [4.3.8] - 2017-04-08
### Added
- Better overview / show pages.
- #628, as reported by [xzaz](https://github.com/xzaz).
- Greatly expanded test coverage
### Fixed
- #619, as reported by [dfiel](https://github.com/dfiel).
- #620, as reported by [forcaeluz](https://github.com/forcaeluz).
- Attempt to fix #624, as reported by [TheSerenin](https://github.com/TheSerenin).
- Favicon link is relative now, fixed by [welbert](https://github.com/welbert).
- Some search bugs
## [4.3.7] - 2017-03-06 ## [4.3.7] - 2017-03-06
### Added ### Added
- Nice user friendly views for empty lists. - Nice user friendly views for empty lists.

View File

@ -35,4 +35,4 @@ If you like Firefly and if it helps you save lots of money, why not send me [a d
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com). If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).
[![Build Status](https://travis-ci.org/firefly-iii/firefly-iii.svg?branch=master)](https://travis-ci.org/firefly-iii/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [![Build Status](https://travis-ci.org/firefly-iii/firefly-iii.svg?branch=master)](https://travis-ci.org/firefly-iii/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [![Coverage Status](https://coveralls.io/repos/github/firefly-iii/firefly-iii/badge.svg?branch=master)](https://coveralls.io/github/firefly-iii/firefly-iii?branch=master)

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
@ -17,8 +17,10 @@ namespace FireflyIII\Console\Commands;
use DB; use DB;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Log; use Log;
@ -60,6 +62,7 @@ class UpgradeDatabase extends Command
{ {
$this->setTransactionIdentifier(); $this->setTransactionIdentifier();
$this->migrateRepetitions(); $this->migrateRepetitions();
$this->repairPiggyBanks();
} }
private function migrateRepetitions() private function migrateRepetitions()
@ -69,7 +72,9 @@ class UpgradeDatabase extends Command
} }
// get all budget limits with end_date NULL // get all budget limits with end_date NULL
$set = BudgetLimit::whereNull('end_date')->get(); $set = BudgetLimit::whereNull('end_date')->get();
if ($set->count() > 0) {
$this->line(sprintf('Found %d budget limit(s) to update', $set->count())); $this->line(sprintf('Found %d budget limit(s) to update', $set->count()));
}
/** @var BudgetLimit $budgetLimit */ /** @var BudgetLimit $budgetLimit */
foreach ($set as $budgetLimit) { foreach ($set as $budgetLimit) {
// get limit repetition (should be just one): // get limit repetition (should be just one):
@ -84,6 +89,35 @@ class UpgradeDatabase extends Command
} }
} }
/**
* Make sure there are only transfers linked to piggy bank events.
*/
private function repairPiggyBanks()
{
// if table does not exist, return false
if (!Schema::hasTable('piggy_bank_events')) {
return;
}
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
/** @var PiggyBankEvent $event */
foreach ($set as $event) {
if (!is_null($event->transaction_journal_id)) {
$type = $event->transactionJournal->transactionType->type;
if ($type !== TransactionType::TRANSFER) {
$event->transaction_journal_id = null;
$event->save();
$this->line(
sprintf('Piggy bank #%d ("%s") was referenced by an invalid event. This has been fixed.', $event->piggy_bank_id,
$event->piggyBank->name
));
}
}
}
}
/** /**
* This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird. * This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird.
*/ */
@ -151,7 +185,6 @@ class UpgradeDatabase extends Command
$opposing->save(); $opposing->save();
$processed[] = $transaction->id; $processed[] = $transaction->id;
$processed[] = $opposing->id; $processed[] = $opposing->id;
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
} }
$identifier++; $identifier++;
} }

View File

@ -93,6 +93,7 @@ class VerifyDatabase extends Command
// report on journals with the wrong types of accounts. // report on journals with the wrong types of accounts.
$this->reportIncorrectJournals(); $this->reportIncorrectJournals();
} }
/** /**
@ -131,7 +132,7 @@ class VerifyDatabase extends Command
/** @var Budget $entry */ /** @var Budget $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
$line = sprintf( $line = sprintf(
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.', 'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
$entry->user_id, $entry->email, $entry->id, $entry->name $entry->user_id, $entry->email, $entry->id, $entry->name
); );
$this->line($line); $this->line($line);
@ -277,7 +278,7 @@ class VerifyDatabase extends Command
} }
$line = sprintf( $line = sprintf(
'Notice: User #%d (%s) has %s #%d ("%s") which has no transactions.', 'User #%d (%s) has %s #%d ("%s") which has no transactions.',
$entry->user_id, $entry->email, $name, $entry->id, $objName $entry->user_id, $entry->email, $name, $entry->id, $objName
); );
$this->line($line); $this->line($line);

View File

@ -97,6 +97,7 @@ class Handler extends ExceptionHandler
'file' => $exception->getFile(), 'file' => $exception->getFile(),
'line' => $exception->getLine(), 'line' => $exception->getLine(),
'code' => $exception->getCode(), 'code' => $exception->getCode(),
'version' => config('firefly.version'),
]; ];
// create job that will mail. // create job that will mail.

View File

@ -20,6 +20,7 @@ use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Rule; use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Rules\Processor; use FireflyIII\Rules\Processor;
use FireflyIII\Support\Events\BillScanner; use FireflyIII\Support\Events\BillScanner;
use Log; use Log;
@ -45,6 +46,16 @@ class StoredJournalEventHandler
$piggyBankId = $event->piggyBankId; $piggyBankId = $event->piggyBankId;
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId)); Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
/*
* Will only continue when journal is a transfer.
*/
Log::debug(sprintf('Journal transaction type is %s', $journal->transactionType->type));
if ($journal->transactionType->type !== TransactionType::TRANSFER) {
Log::info(sprintf('Will not connect %s #%d to a piggy bank.', $journal->transactionType->type, $journal->id));
return true;
}
/* /*
* Verify existence of piggy bank: * Verify existence of piggy bank:
*/ */

View File

@ -9,7 +9,8 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Helpers\Attachments; namespace FireflyIII\Helpers\Attachments;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;

View File

@ -9,7 +9,8 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Helpers\Help; namespace FireflyIII\Helpers\Help;
use Cache; use Cache;
@ -43,12 +44,12 @@ class Help implements HelpInterface
} }
/** /**
* @param string $language
* @param string $route * @param string $route
* @param string $language
* *
* @return string * @return string
*/ */
public function getFromGithub(string $language, string $route): string public function getFromGithub(string $route, string $language): string
{ {
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route); $uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
@ -123,6 +124,7 @@ class Help implements HelpInterface
if (strlen($content) > 0) { if (strlen($content) > 0) {
Log::debug(sprintf('Will store entry in cache: %s', $key)); Log::debug(sprintf('Will store entry in cache: %s', $key));
Cache::put($key, $content, 10080); // a week. Cache::put($key, $content, 10080); // a week.
return; return;
} }
Log::info(sprintf('Will not cache %s because content is empty.', $key)); Log::info(sprintf('Will not cache %s because content is empty.', $key));

View File

@ -29,12 +29,12 @@ interface HelpInterface
public function getFromCache(string $route, string $language): string; public function getFromCache(string $route, string $language): string;
/** /**
* @param string $language
* @param string $route * @param string $route
* @param string $language
* *
* @return string * @return string
*/ */
public function getFromGithub(string $language, string $route): string; public function getFromGithub(string $route, string $language): string;
/** /**
* @param string $route * @param string $route

View File

@ -0,0 +1,199 @@
<?php
/**
* PopupReport.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\Helpers\Report;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
/**
* Class PopupReport
*
* @package FireflyIII\Helpers\Report
*/
class PopupReport implements PopupReportInterface
{
/**
* @param Budget $budget
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function balanceForBudget(Budget $budget, Account $account, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setBudget($budget);
$journals = $collector->getJournals();
return $journals;
}
/**
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function balanceForNoBudget(Account $account, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setTypes([TransactionType::WITHDRAWAL])
->setRange($attributes['startDate'], $attributes['endDate'])
->withoutBudget();
return $collector->getJournals();
}
/**
* @param Budget $budget
* @param array $attributes
*
* @return Collection
*/
public function byBudget(Budget $budget, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($attributes['accounts'])->setRange($attributes['startDate'], $attributes['endDate']);
if (is_null($budget->id)) {
$collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
}
if (!is_null($budget->id)) {
$collector->setBudget($budget);
}
$journals = $collector->getJournals();
return $journals;
}
/**
* @param Category $category
* @param array $attributes
*
* @return Collection
*/
public function byCategory(Category $category, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($attributes['accounts'])->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setRange($attributes['startDate'], $attributes['endDate'])
->setCategory($category);
$journals = $collector->getJournals();
return $journals;
}
/**
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function byExpenses(Account $account, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]);
$journals = $collector->getJournals();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
// filter for transfers and withdrawals TO the given $account
$journals = $journals->filter(
function (Transaction $transaction) use ($report) {
// get the destinations:
$sources = $transaction->transactionJournal->sourceAccountList()->pluck('id')->toArray();
// do these intersect with the current list?
return !empty(array_intersect($report, $sources));
}
);
return $journals;
}
/**
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function byIncome(Account $account, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]);
$journals = $collector->getJournals();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
// filter the set so the destinations outside of $attributes['accounts'] are not included.
$journals = $journals->filter(
function (Transaction $transaction) use ($report) {
// get the destinations:
$destinations = $transaction->destinationAccountList($transaction->transactionJournal)->pluck('id')->toArray();
// do these intersect with the current list?
return !empty(array_intersect($report, $destinations));
}
);
return $journals;
}
/**
* @param $account
* @param $attributes
*
* @return Collection
*/
public function balanceDifference($account, $attributes): Collection
{
// row that displays difference
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setTypes([TransactionType::WITHDRAWAL])
->setRange($attributes['startDate'], $attributes['endDate'])
->withoutBudget();
$journals = $collector->getJournals();
return $journals->filter(
function (Transaction $transaction) {
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
if ($tags === 0) {
return true;
}
return false;
}
);
}
}

View File

@ -0,0 +1,83 @@
<?php
/**
* PopupReportInterface.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\Helpers\Report;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use Illuminate\Support\Collection;
/**
* Interface PopupReportInterface
*
* @package FireflyIII\Helpers\Report
*/
interface PopupReportInterface
{
/**
* @param $account
* @param $attributes
*
* @return Collection
*/
public function balanceDifference($account, $attributes): Collection;
/**
* @param Budget $budget
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function balanceForBudget(Budget $budget, Account $account, array $attributes): Collection;
/**
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function balanceForNoBudget(Account $account, array $attributes): Collection;
/**
* @param Budget $budget
* @param array $attributes
*
* @return Collection
*/
public function byBudget(Budget $budget, array $attributes): Collection;
/**
* @param Category $category
* @param array $attributes
*
* @return Collection
*/
public function byCategory(Category $category, array $attributes): Collection;
/**
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function byExpenses(Account $account, array $attributes): Collection;
/**
* @param Account $account
* @param array $attributes
*
* @return Collection
*/
public function byIncome(Account $account, array $attributes): Collection;
}

View File

@ -25,6 +25,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -230,15 +231,17 @@ class AccountController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param JournalRepositoryInterface $repository
* @param Account $account * @param Account $account
* @param string $moment * @param string $moment
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/ */
public function show(Request $request, Account $account, string $moment = '') public function show(Request $request, JournalRepositoryInterface $repository, Account $account, string $moment = '')
{ {
if ($account->accountType->type === AccountType::INITIAL_BALANCE) { if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
return $this->redirectToOriginalAccount($account); return $this->redirectToOriginalAccount($account);
} }
$subTitle = $account->name;
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
@ -250,29 +253,34 @@ class AccountController extends Controller
// prep for "all" view. // prep for "all" view.
if ($moment === 'all') { if ($moment === 'all') {
$subTitle = $account->name . ' (' . strtolower(strval(trans('firefly.everything'))) . ')'; $subTitle = trans('firefly.all_journals_for_account', ['name' => $account->name]);
$chartUri = route('chart.account.all', [$account->id]); $chartUri = route('chart.account.all', [$account->id]);
$first = $repository->first();
$start = $first->date ?? new Carbon;
$end = new Carbon;
} }
// prep for "specific date" view. // prep for "specific date" view.
if (strlen($moment) > 0 && $moment !== 'all') { if (strlen($moment) > 0 && $moment !== 'all') {
$start = new Carbon($moment); $start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range); $end = Navigation::endOfPeriod($start, $range);
$subTitle = $account->name . ' (' . strval( $subTitle = trans(
trans( 'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
'firefly.from_to_breadcrumb', 'end' => $end->formatLocalized($this->monthAndDayFormat)]
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] );
)
) . ')';
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d')]); $chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d')]);
$periods = $this->periodEntries($account); $periods = $this->getPeriodOverview($account);
} }
// prep for current period // prep for current period
if (strlen($moment) === 0) { if (strlen($moment) === 0) {
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range)); $start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range)); $end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
$periods = $this->periodEntries($account); $subTitle = trans(
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$periods = $this->getPeriodOverview($account);
} }
$accountType = $account->accountType->type; $accountType = $account->accountType->type;
@ -289,7 +297,7 @@ class AccountController extends Controller
$collector->setRange($start, $end); $collector->setRange($start, $end);
} }
$journals = $collector->getPaginatedJournals(); $journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id); $journals->setPath('accounts/show/' . $account->id . '/' . $moment);
$count = $journals->getCollection()->count(); $count = $journals->getCollection()->count();
if ($count === 0) { if ($count === 0) {
$start->subDay(); $start->subDay();
@ -299,8 +307,17 @@ class AccountController extends Controller
} }
} }
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)]
);
}
return view('accounts.show', compact('account', 'accountType', 'periods', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
return view(
'accounts.show', compact('account', 'moment', 'accountType', 'periods', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri')
);
} }
/** /**
@ -388,7 +405,7 @@ class AccountController extends Controller
* *
* @return Collection * @return Collection
*/ */
private function periodEntries(Account $account): Collection private function getPeriodOverview(Account $account): Collection
{ {
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
@ -425,7 +442,14 @@ class AccountController extends Controller
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd); $earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
$dateStr = $end->format('Y-m-d'); $dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range); $dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned, clone $end]); $entries->push(
[
'string' => $dateStr,
'name' => $dateName,
'spent' => $spent,
'earned' => $earned,
'date' => clone $end]
);
$end = Navigation::subtractPeriod($end, $range, 1); $end = Navigation::subtractPeriod($end, $range, 1);
} }
@ -453,7 +477,7 @@ class AccountController extends Controller
$opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first(); $opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();
if (is_null($opposingTransaction)) { if (is_null($opposingTransaction)) {
throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.'); throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.'); // @codeCoverageIgnore
} }
return redirect(route('accounts.show', [$opposingTransaction->account_id])); return redirect(route('accounts.show', [$opposingTransaction->account_id]));

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin; namespace FireflyIII\Http\Controllers\Admin;
@ -18,6 +18,7 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\UserFormRequest; use FireflyIII\Http\Requests\UserFormRequest;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Log;
use Preferences; use Preferences;
use Session; use Session;
use View; use View;
@ -129,29 +130,27 @@ class UserController extends Controller
* *
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function update(UserFormRequest $request, User $user) public function update(UserFormRequest $request, User $user, UserRepositoryInterface $repository)
{ {
Log::debug('Actually here');
$data = $request->getUserData(); $data = $request->getUserData();
// update password // update password
if (strlen($data['password']) > 0) { if (strlen($data['password']) > 0) {
$user->password = bcrypt($data['password']); $repository->changePassword($user, $data['password']);
$user->save();
} }
// change blocked status and code: $repository->changeStatus($user, $data['blocked'], $data['blocked_code']);
$user->blocked = $data['blocked'];
$user->blocked_code = $data['blocked_code'];
$user->save();
Session::flash('success', strval(trans('firefly.updated_user', ['email' => $user->email]))); Session::flash('success', strval(trans('firefly.updated_user', ['email' => $user->email])));
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL: // @codeCoverageIgnoreStart
Session::put('users.edit.fromUpdate', true); Session::put('users.edit.fromUpdate', true);
return redirect(route('admin.users.edit', [$user->id]))->withInput(['return_to_edit' => 1]); return redirect(route('admin.users.edit', [$user->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
} }
// redirect to previous URL. // redirect to previous URL.

View File

@ -176,10 +176,11 @@ class AttachmentController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL: // @codeCoverageIgnoreStart
$request->session()->put('attachments.edit.fromUpdate', true); $request->session()->put('attachments.edit.fromUpdate', true);
return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]); return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
} }
// redirect to previous URL. // redirect to previous URL.

View File

@ -13,6 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Auth; namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -44,21 +45,18 @@ class ForgotPasswordController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function sendResetLinkEmail(Request $request) public function sendResetLinkEmail(Request $request, UserRepositoryInterface $repository)
{ {
$this->validate($request, ['email' => 'required|email']); $this->validate($request, ['email' => 'required|email']);
// verify if the user is not a demo user. If so, we give him back an error. // verify if the user is not a demo user. If so, we give him back an error.
$user = User::where('email', $request->get('email'))->first(); $user = User::where('email', $request->get('email'))->first();
if (!is_null($user) && $user->hasRole('demo')) {
return back()->withErrors( if (!is_null($user) && $repository->hasRole($user, 'demo')) {
['email' => trans('firefly.cannot_reset_demo_user')] return back()->withErrors(['email' => trans('firefly.cannot_reset_demo_user')]);
);
} }
$response = $this->broker()->sendResetLink( $response = $this->broker()->sendResetLink($request->only('email'));
$request->only('email')
);
if ($response === Password::RESET_LINK_SENT) { if ($response === Password::RESET_LINK_SENT) {
return back()->with('status', trans($response)); return back()->with('status', trans($response));
@ -67,8 +65,6 @@ class ForgotPasswordController extends Controller
// If an error was returned by the password broker, we will get this message // If an error was returned by the password broker, we will get this message
// translated so we can notify a user of the problem. We'll redirect back // translated so we can notify a user of the problem. We'll redirect back
// to where the users came from so they can attempt this process again. // to where the users came from so they can attempt this process again.
return back()->withErrors( return back()->withErrors(['email' => trans($response)]); // @codeCoverageIgnore
['email' => trans($response)]
);
} }
} }

View File

@ -22,6 +22,8 @@ use Illuminate\Http\Request;
use Lang; use Lang;
/** /**
* @codeCoverageIgnore
*
* Class LoginController * Class LoginController
* *
* @package FireflyIII\Http\Controllers\Auth * @package FireflyIII\Http\Controllers\Auth
@ -112,8 +114,10 @@ class LoginController extends Controller
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function showLoginForm(Request $request) public function showLoginForm(Request $request, CookieJar $cookieJar)
{ {
// forget 2fa cookie:
$cookie = $cookieJar->forever('twoFactorAuthenticated', 'false');
// is allowed to? // is allowed to?
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data; $singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
$userCount = User::count(); $userCount = User::count();
@ -125,7 +129,7 @@ class LoginController extends Controller
$email = $request->old('email'); $email = $request->old('email');
$remember = $request->old('remember'); $remember = $request->old('remember');
return view('auth.login', compact('allowRegistration', 'email', 'remember')); return view('auth.login', compact('allowRegistration', 'email', 'remember'))->withCookie($cookie);
} }
/** /**

View File

@ -22,6 +22,8 @@ use Illuminate\Support\Facades\Password;
/** /**
* @codeCoverageIgnore
*
* Class PasswordController * Class PasswordController
* *
* @package FireflyIII\Http\Controllers\Auth * @package FireflyIII\Http\Controllers\Auth

View File

@ -24,6 +24,8 @@ use Session;
use Validator; use Validator;
/** /**
* @codeCoverageIgnore
*
* Class RegisterController * Class RegisterController
* *
* @package FireflyIII\Http\Controllers\Auth * @package FireflyIII\Http\Controllers\Auth

View File

@ -16,6 +16,8 @@ use FireflyIII\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Foundation\Auth\ResetsPasswords;
/** /**
* @codeCoverageIgnore
*
* Class ResetPasswordController * Class ResetPasswordController
* *
* @package FireflyIII\Http\Controllers\Auth * @package FireflyIII\Http\Controllers\Auth

View File

@ -41,11 +41,12 @@ class TwoFactorController extends Controller
$user = auth()->user(); $user = auth()->user();
// to make sure the validator in the next step gets the secret, we push it in session // to make sure the validator in the next step gets the secret, we push it in session
$secret = Preferences::get('twoFactorAuthSecret', null)->data; $secretPreference = Preferences::get('twoFactorAuthSecret', null);
$secret = is_null($secretPreference) ? null : $secretPreference->data;
$title = strval(trans('firefly.two_factor_title')); $title = strval(trans('firefly.two_factor_title'));
// make sure the user has two factor configured: // make sure the user has two factor configured:
$has2FA = Preferences::get('twoFactorAuthEnabled', null)->data; $has2FA = Preferences::get('twoFactorAuthEnabled', false)->data;
if (is_null($has2FA) || $has2FA === false) { if (is_null($has2FA) || $has2FA === false) {
return redirect(route('index')); return redirect(route('index'));
} }

View File

@ -240,10 +240,11 @@ class BillController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL: // @codeCoverageIgnoreStart
$request->session()->put('bills.create.fromStore', true); $request->session()->put('bills.create.fromStore', true);
return redirect(route('bills.create'))->withInput(); return redirect(route('bills.create'))->withInput();
// @codeCoverageIgnoreEnd
} }
// redirect to previous URL. // redirect to previous URL.
@ -267,10 +268,11 @@ class BillController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL: // @codeCoverageIgnoreStart
$request->session()->put('bills.edit.fromUpdate', true); $request->session()->put('bills.edit.fromUpdate', true);
return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]); return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('bills.edit.uri')); return redirect($this->getPreviousUri('bills.edit.uri'));

View File

@ -22,11 +22,15 @@ use FireflyIII\Http\Requests\BudgetIncomeRequest;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Navigation;
use Preferences; use Preferences;
use Response; use Response;
use View; use View;
@ -188,30 +192,81 @@ class BudgetController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param string $moment
* *
* @return View * @return View
*/ */
public function noBudget(Request $request) public function noBudget(Request $request, JournalRepositoryInterface $repository, string $moment = '')
{ {
/** @var Carbon $start */ // default values:
$start = session('start', Carbon::now()->startOfMonth()); $range = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $end */ $start = null;
$end = session('end', Carbon::now()->endOfMonth()); $end = null;
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); $periods = new Collection;
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// prep for "all" view.
if ($moment === 'all') {
$subTitle = trans('firefly.all_journals_without_budget');
$first = $repository->first();
$start = $first->date ?? new Carbon;
$end = new Carbon;
}
// prep for "specific date" view.
if (strlen($moment) > 0 && $moment !== 'all') {
$start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range);
$subTitle = trans( $subTitle = trans(
'firefly.without_budget_between', 'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
); );
$periods = $this->getPeriodOverview();
}
// collector // prep for current period
if (strlen($moment) === 0) {
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview();
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
$loop = 0;
// grab journals, but be prepared to jump a period back to get the right ones:
Log::info('Now at no-budget loop start.');
while ($count === 0 && $loop < 3) {
$loop++;
Log::info('Count is zero, search for journals.');
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class); $collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget(); $collector->setAllAssetAccounts()->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page)
->withoutBudget()->withOpposingAccount();
$journals = $collector->getPaginatedJournals(); $journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/list/noBudget'); $journals->setPath('/budgets/list/no-budget');
$count = $journals->getCollection()->count();
if ($count === 0) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
}
}
return view('budgets.no-budget', compact('journals', 'subTitle')); if ($moment != 'all' && $loop > 1) {
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
return view('budgets.no-budget', compact('journals', 'subTitle', 'moment', 'periods', 'start', 'end'));
} }
/** /**
@ -315,10 +370,11 @@ class BudgetController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL: // @codeCoverageIgnoreStart
$request->session()->put('budgets.create.fromStore', true); $request->session()->put('budgets.create.fromStore', true);
return redirect(route('budgets.create'))->withInput(); return redirect(route('budgets.create'))->withInput();
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('budgets.create.uri')); return redirect($this->getPreviousUri('budgets.create.uri'));
@ -339,10 +395,11 @@ class BudgetController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL: // @codeCoverageIgnoreStart
$request->session()->put('budgets.edit.fromUpdate', true); $request->session()->put('budgets.edit.fromUpdate', true);
return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]); return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('budgets.edit.uri')); return redirect($this->getPreviousUri('budgets.edit.uri'));
@ -405,7 +462,6 @@ class BudgetController extends Controller
return $return; return $return;
} }
/** /**
* @param Budget $budget * @param Budget $budget
* @param Carbon $start * @param Carbon $start
@ -442,4 +498,57 @@ class BudgetController extends Controller
return $set; return $set;
} }
/**
* @return Collection
*/
private function getPeriodOverview(): Collection
{
$repository = app(JournalRepositoryInterface::class);
$first = $repository->first();
$start = $first->date ?? new Carbon;
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('no-budget-period-entries');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
Log::debug('Going to get period expenses and incomes.');
while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
// count journals without budget in this period:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutBudget()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
$set = $collector->getJournals();
$sum = $set->sum('transaction_amount');
$journals = $set->count();
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push(
[
'string' => $dateStr,
'name' => $dateName,
'count' => $journals,
'sum' => $sum,
'date' => clone $end,
]
);
$end = Navigation::subtractPeriod($end, $range, 1);
}
$cache->store($entries);
return $entries;
}
} }

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
@ -18,13 +18,17 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\CategoryFormRequest; use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Navigation; use Navigation;
use Preferences; use Preferences;
use Steam;
use View; use View;
/** /**
@ -152,116 +156,164 @@ class CategoryController extends Controller
/** /**
* @return View * @return View
*/ */
public function noCategory() public function noCategory(Request $request, JournalRepositoryInterface $repository, string $moment = '')
{ {
/** @var Carbon $start */ // default values:
$start = session('start', Carbon::now()->startOfMonth()); $range = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $end */ $start = null;
$end = session('end', Carbon::now()->startOfMonth()); $end = null;
$periods = new Collection;
// new collector: // prep for "all" view.
/** @var JournalCollectorInterface $collector */ if ($moment === 'all') {
$collector = app(JournalCollectorInterface::class); $subTitle = trans('firefly.all_journals_without_category');
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals(); $first = $repository->first();
$journals = $collector->getJournals(); $start = $first->date ?? new Carbon;
$end = new Carbon;
}
// prep for "specific date" view.
if (strlen($moment) > 0 && $moment !== 'all') {
$start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range);
$subTitle = trans( $subTitle = trans(
'firefly.without_category_between', 'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
); );
$periods = $this->getNoCategoryPeriodOverview();
return view('categories.no-category', compact('journals', 'subTitle'));
} }
/** // prep for current period
* @param Request $request if (strlen($moment) === 0) {
* @param JournalCollectorInterface $collector $start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
* @param Category $category $end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
* $periods = $this->getNoCategoryPeriodOverview();
* @return View $subTitle = trans(
*/ 'firefly.without_category_between',
public function show(Request $request, JournalCollectorInterface $collector, Category $category) ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
{ );
$range = Preferences::get('viewRange', '1M')->data; }
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = session('end', Navigation::endOfPeriod(new Carbon, $range)); $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$hideCategory = true; // used in list.
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$entries = $this->getGroupedEntries($category);
$method = 'default';
// get journals $count = 0;
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation(); $loop = 0;
// grab journals, but be prepared to jump a period back to get the right ones:
Log::info('Now at no-cat loop start.');
while ($count === 0 && $loop < 3) {
$loop++;
Log::info('Count is zero, search for journals.');
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutCategory()->withOpposingAccount();
$collector->disableInternalFilter();
$journals = $collector->getPaginatedJournals(); $journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id); $journals->setPath('/categories/list/no-category');
$count = $journals->getCollection()->count();
if ($count === 0) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
}
}
if ($moment != 'all' && $loop > 1) {
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
return view('categories.show', compact('category', 'method', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end')); return view('categories.no-category', compact('journals', 'subTitle', 'moment', 'periods', 'start', 'end'));
} }
/** /**
* @param Request $request * @param Request $request
* @param CategoryRepositoryInterface $repository * @param CategoryRepositoryInterface $repository
* @param Category $category * @param Category $category
* @param string $moment
* *
* @return View * @return View
*/ */
public function showAll(Request $request, CategoryRepositoryInterface $repository, Category $category) public function show(Request $request, CategoryRepositoryInterface $repository, Category $category, string $moment = '')
{ {
// default values:
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
$loop = 0;
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = null;
$end = null;
$periods = new Collection;
// prep for "all" view.
if ($moment === 'all') {
$subTitle = trans('firefly.all_journals_for_category', ['name' => $category->name]);
$start = $repository->firstUseDate($category); $start = $repository->firstUseDate($category);
if ($start->year == 1900) { $end = new Carbon;
$start = new Carbon;
} }
$end = Navigation::endOfPeriod(new Carbon, $range);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$hideCategory = true; // used in list.
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$method = 'all';
// prep for "specific date" view.
if (strlen($moment) > 0 && $moment !== 'all') {
$start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range);
$subTitle = trans(
'firefly.journals_in_period_for_category',
['name' => $category->name,
'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$periods = $this->getPeriodOverview($category);
}
// prep for current period
if (strlen($moment) === 0) {
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview($category);
$subTitle = trans(
'firefly.journals_in_period_for_category',
['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
// grab journals, but be prepared to jump a period back to get the right ones:
Log::info('Now at category loop start.');
while ($count === 0 && $loop < 3) {
$loop++;
Log::info('Count is zero, search for journals.');
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class); $collector = app(JournalCollectorInterface::class);
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setCategory($category)->withBudgetInformation(); $collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
->setCategory($category)->withBudgetInformation()->withCategoryInformation()->disableInternalFilter();
$journals = $collector->getPaginatedJournals(); $journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id . '/all'); $journals->setPath('categories/show/' . $category->id);
$count = $journals->getCollection()->count();
return view('categories.show', compact('category', 'method', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end')); if ($count === 0) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
}
} }
/** if ($moment != 'all' && $loop > 1) {
* @param Request $request $subTitle = trans(
* @param Category $category 'firefly.journals_in_period_for_category',
* @param string $date ['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
* 'end' => $end->formatLocalized($this->monthAndDayFormat)]
* @return View );
*/
public function showByDate(Request $request, Category $category, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$hideCategory = true; // used in list.
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$entries = $this->getGroupedEntries($category);
$method = 'date';
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show', compact('category', 'method', 'entries', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
} }
return view('categories.show', compact('category', 'moment', 'journals', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end'));
}
/** /**
* @param CategoryFormRequest $request * @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository * @param CategoryRepositoryInterface $repository
@ -277,9 +329,11 @@ class CategoryController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// @codeCoverageIgnoreStart
$request->session()->put('categories.create.fromStore', true); $request->session()->put('categories.create.fromStore', true);
return redirect(route('categories.create'))->withInput(); return redirect(route('categories.create'))->withInput();
// @codeCoverageIgnoreEnd
} }
return redirect(route('categories.index')); return redirect(route('categories.index'));
@ -302,20 +356,97 @@ class CategoryController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// @codeCoverageIgnoreStart
$request->session()->put('categories.edit.fromUpdate', true); $request->session()->put('categories.edit.fromUpdate', true);
return redirect(route('categories.edit', [$category->id])); return redirect(route('categories.edit', [$category->id]));
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('categories.edit.uri')); return redirect($this->getPreviousUri('categories.edit.uri'));
} }
/**
* @return Collection
*/
private function getNoCategoryPeriodOverview(): Collection
{
$repository = app(JournalRepositoryInterface::class);
$first = $repository->first();
$start = $first->date ?? new Carbon;
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('no-budget-period-entries');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
Log::debug(sprintf('Going to get period expenses and incomes between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d')));
while ($end >= $start) {
Log::debug('Loop!');
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
// count journals without budget in this period:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount();
$count = $collector->getJournals()->count();
// amount transferred
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()
->withOpposingAccount()->setTypes([TransactionType::TRANSFER])->disableInternalFilter();
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
// amount spent
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
$spent = $collector->getJournals()->sum('transaction_amount');
// amount earned
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::DEPOSIT]);
$earned = $collector->getJournals()->sum('transaction_amount');
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push(
[
'string' => $dateStr,
'name' => $dateName,
'count' => $count,
'spent' => $spent,
'earned' => $earned,
'transferred' => $transferred,
'date' => clone $end,
]
);
$end = Navigation::subtractPeriod($end, $range, 1);
}
Log::debug('End of loops');
$cache->store($entries);
return $entries;
}
/** /**
* @param Category $category * @param Category $category
* *
* @return Collection * @return Collection
*/ */
private function getGroupedEntries(Category $category): Collection private function getPeriodOverview(Category $category): Collection
{ {
/** @var CategoryRepositoryInterface $repository */ /** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class); $repository = app(CategoryRepositoryInterface::class);
@ -348,7 +479,25 @@ class CategoryController extends Controller
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
$dateStr = $end->format('Y-m-d'); $dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range); $dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned, clone $end]);
// amount transferred
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->setCategory($category)
->withOpposingAccount()->setTypes([TransactionType::TRANSFER])->disableInternalFilter();
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
$entries->push(
[
'string' => $dateStr,
'name' => $dateName,
'spent' => $spent,
'earned' => $earned,
'sum' => bcadd($earned, $spent),
'transferred' => $transferred,
'date' => clone $end,
]
);
$end = Navigation::subtractPeriod($end, $range, 1); $end = Navigation::subtractPeriod($end, $range, 1);
} }
$cache->store($entries); $cache->store($entries);

View File

@ -61,15 +61,12 @@ class AccountController extends Controller
*/ */
public function all(Account $account) public function all(Account $account)
{ {
$cache = new CacheProperties(); $cache = new CacheProperties;
$cache->addProperty('chart.account.all'); $cache->addProperty('chart.account.all');
$cache->addProperty($account->id); $cache->addProperty($account->id);
if ($cache->has()) { if ($cache->has()) {
Log::debug('Return chart.account.all from cache.');
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
Log::debug('Regenerate chart.account.all from scratch.');
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
@ -500,8 +497,6 @@ class AccountController extends Controller
$cache->addProperty('chart.account.account-balance-chart'); $cache->addProperty('chart.account.account-balance-chart');
$cache->addProperty($accounts); $cache->addProperty($accounts);
if ($cache->has()) { if ($cache->has()) {
Log::debug('Return chart.account.account-balance-chart from cache.');
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
Log::debug('Regenerate chart.account.account-balance-chart from scratch.'); Log::debug('Regenerate chart.account.account-balance-chart from scratch.');

View File

@ -9,14 +9,14 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Chart; namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Generator\Report\Category\MonthReportGenerator; use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Chart\MetaPieChartInterface; use FireflyIII\Helpers\Chart\MetaPieChartInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
@ -24,7 +24,6 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -41,8 +40,6 @@ use Response;
class BudgetReportController extends Controller class BudgetReportController extends Controller
{ {
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var BudgetRepositoryInterface */ /** @var BudgetRepositoryInterface */
private $budgetRepository; private $budgetRepository;
/** @var GeneratorInterface */ /** @var GeneratorInterface */
@ -58,7 +55,6 @@ class BudgetReportController extends Controller
function ($request, $next) { function ($request, $next) {
$this->generator = app(GeneratorInterface::class); $this->generator = app(GeneratorInterface::class);
$this->budgetRepository = app(BudgetRepositoryInterface::class); $this->budgetRepository = app(BudgetRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
return $next($request); return $next($request);
} }
@ -133,8 +129,6 @@ class BudgetReportController extends Controller
if ($cache->has()) { if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$format = Navigation::preferredCarbonLocalizedFormat($start, $end); $format = Navigation::preferredCarbonLocalizedFormat($start, $end);
$function = Navigation::preferredEndOfPeriod($start, $end); $function = Navigation::preferredEndOfPeriod($start, $end);
$chartData = []; $chartData = [];
@ -163,7 +157,7 @@ class BudgetReportController extends Controller
'entries' => [], 'entries' => [],
]; ];
} }
$allBudgetLimits = $repository->getAllBudgetLimits($start, $end); $allBudgetLimits = $this->budgetRepository->getAllBudgetLimits($start, $end);
$sumOfExpenses = []; $sumOfExpenses = [];
$leftOfLimits = []; $leftOfLimits = [];
while ($currentStart < $end) { while ($currentStart < $end) {
@ -243,7 +237,7 @@ class BudgetReportController extends Controller
->setBudgets($budgets)->withOpposingAccount()->disableFilter(); ->setBudgets($budgets)->withOpposingAccount()->disableFilter();
$accountIds = $accounts->pluck('id')->toArray(); $accountIds = $accounts->pluck('id')->toArray();
$transactions = $collector->getJournals(); $transactions = $collector->getJournals();
$set = MonthReportGenerator::filterExpenses($transactions, $accountIds); $set = Support::filterExpenses($transactions, $accountIds);
return $set; return $set;
} }

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Chart; namespace FireflyIII\Http\Controllers\Chart;
@ -86,15 +86,23 @@ class CategoryController extends Controller
'entries' => [], 'entries' => [],
'type' => 'bar', 'type' => 'bar',
], ],
[
'label' => strval(trans('firefly.sum')),
'entries' => [],
'type' => 'line',
'fill' => false,
],
]; ];
while ($start <= $end) { while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range); $currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
$sum = bcadd($spent, $earned);
$label = Navigation::periodShow($start, $range); $label = Navigation::periodShow($start, $range);
$chartData[0]['entries'][$label] = bcmul($spent, '-1'); $chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $earned; $chartData[1]['entries'][$label] = $earned;
$chartData[2]['entries'][$label] = $sum;
$start = Navigation::addPeriod($start, $range, 0); $start = Navigation::addPeriod($start, $range, 0);
} }
@ -194,13 +202,22 @@ class CategoryController extends Controller
'entries' => [], 'entries' => [],
'type' => 'bar', 'type' => 'bar',
], ],
[
'label' => strval(trans('firefly.sum')),
'entries' => [],
'type' => 'line',
'fill' => false,
],
]; ];
foreach (array_keys($periods) as $period) { foreach (array_keys($periods) as $period) {
$label = $periods[$period]; $label = $periods[$period];
$spent = $expenses[$category->id]['entries'][$period] ?? '0'; $spent = $expenses[$category->id]['entries'][$period] ?? '0';
$earned = $income[$category->id]['entries'][$period] ?? '0';
$sum = bcadd($spent, $earned);
$chartData[0]['entries'][$label] = bcmul($spent, '-1'); $chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $income[$category->id]['entries'][$period] ?? '0'; $chartData[1]['entries'][$label] = $earned;
$chartData[2]['entries'][$label] = $sum;
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
@ -241,13 +258,22 @@ class CategoryController extends Controller
'entries' => [], 'entries' => [],
'type' => 'bar', 'type' => 'bar',
], ],
[
'label' => strval(trans('firefly.sum')),
'entries' => [],
'type' => 'line',
'fill' => false,
],
]; ];
foreach (array_keys($periods) as $period) { foreach (array_keys($periods) as $period) {
$label = $periods[$period]; $label = $periods[$period];
$spent = $expenses['entries'][$period] ?? '0'; $spent = $expenses['entries'][$period] ?? '0';
$earned = $income['entries'][$period] ?? '0';
$sum = bcadd($spent, $earned);
$chartData[0]['entries'][$label] = bcmul($spent, '-1'); $chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $income['entries'][$period] ?? '0'; $chartData[1]['entries'][$label] = $earned;
$chartData[2]['entries'][$label] = $sum;
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
@ -312,15 +338,23 @@ class CategoryController extends Controller
'entries' => [], 'entries' => [],
'type' => 'bar', 'type' => 'bar',
], ],
[
'label' => strval(trans('firefly.sum')),
'entries' => [],
'type' => 'line',
'fill' => false,
],
]; ];
while ($start <= $end) { while ($start <= $end) {
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start); $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start);
$sum = bcadd($spent, $earned);
$label = Navigation::periodShow($start, '1D'); $label = Navigation::periodShow($start, '1D');
$chartData[0]['entries'][$label] = bcmul($spent, '-1'); $chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $earned; $chartData[1]['entries'][$label] = $earned;
$chartData[2]['entries'][$label] = $sum;
$start->addDay(); $start->addDay();

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Chart; namespace FireflyIII\Http\Controllers\Chart;
@ -23,8 +23,6 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
@ -41,10 +39,6 @@ use Response;
class CategoryReportController extends Controller class CategoryReportController extends Controller
{ {
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var CategoryRepositoryInterface */
private $categoryRepository;
/** @var GeneratorInterface */ /** @var GeneratorInterface */
private $generator; private $generator;
@ -57,8 +51,6 @@ class CategoryReportController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
$this->generator = app(GeneratorInterface::class); $this->generator = app(GeneratorInterface::class);
$this->categoryRepository = app(CategoryRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
return $next($request); return $next($request);
} }
@ -78,11 +70,8 @@ class CategoryReportController extends Controller
{ {
/** @var MetaPieChartInterface $helper */ /** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class); $helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts); $helper->setAccounts($accounts)->setCategories($categories)->setStart($start)->setEnd($end)->setCollectOtherObjects(intval($others) === 1);
$helper->setCategories($categories);
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(intval($others) === 1);
$chartData = $helper->generate('expense', 'account'); $chartData = $helper->generate('expense', 'account');
$data = $this->generator->pieChart($chartData); $data = $this->generator->pieChart($chartData);

View File

@ -120,9 +120,11 @@ class Controller extends BaseController
} }
} }
// @codeCoverageIgnoreStart
Session::flash('error', strval(trans('firefly.cannot_redirect_to_account'))); Session::flash('error', strval(trans('firefly.cannot_redirect_to_account')));
return redirect(route('index')); return redirect(route('index'));
// @codeCoverageIgnoreEnd
} }
/** /**

View File

@ -17,6 +17,7 @@ use Cache;
use FireflyIII\Http\Requests\CurrencyFormRequest; use FireflyIII\Http\Requests\CurrencyFormRequest;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Log; use Log;
use Preferences; use Preferences;
@ -30,6 +31,11 @@ use View;
class CurrencyController extends Controller class CurrencyController extends Controller
{ {
/** @var CurrencyRepositoryInterface */
protected $repository;
/** @var UserRepositoryInterface */
protected $userRepository;
/** /**
* *
@ -43,6 +49,8 @@ class CurrencyController extends Controller
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.currencies')); View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd'); View::share('mainTitleIcon', 'fa-usd');
$this->repository = app(CurrencyRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
return $next($request); return $next($request);
} }
@ -52,10 +60,16 @@ class CurrencyController extends Controller
/** /**
* @param Request $request * @param Request $request
* *
* @return View * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/ */
public function create(Request $request) public function create(Request $request)
{ {
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
return redirect(route('currencies.index'));
}
$subTitleIcon = 'fa-plus'; $subTitleIcon = 'fa-plus';
$subTitle = trans('firefly.create_currency'); $subTitle = trans('firefly.create_currency');
@ -93,14 +107,21 @@ class CurrencyController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param CurrencyRepositoryInterface $repository
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/ */
public function delete(Request $request, CurrencyRepositoryInterface $repository, TransactionCurrency $currency) public function delete(Request $request, TransactionCurrency $currency)
{ {
if (!$repository->canDeleteCurrency($currency)) { if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
// @codeCoverageIgnoreStart
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
return redirect(route('currencies.index'));
// @codeCoverageIgnoreEnd
}
if (!$this->repository->canDeleteCurrency($currency)) {
$request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name])); $request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
return redirect(route('currencies.index')); return redirect(route('currencies.index'));
@ -119,20 +140,27 @@ class CurrencyController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param CurrencyRepositoryInterface $repository
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function destroy(Request $request, CurrencyRepositoryInterface $repository, TransactionCurrency $currency) public function destroy(Request $request, TransactionCurrency $currency)
{ {
if (!$repository->canDeleteCurrency($currency)) { if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
// @codeCoverageIgnoreStart
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
return redirect(route('currencies.index'));
// @codeCoverageIgnoreEnd
}
if (!$this->repository->canDeleteCurrency($currency)) {
$request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name])); $request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
return redirect(route('currencies.index')); return redirect(route('currencies.index'));
} }
$repository->destroy($currency); $this->repository->destroy($currency);
$request->session()->flash('success', trans('firefly.deleted_currency', ['name' => $currency->name])); $request->session()->flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
return redirect($this->getPreviousUri('currencies.delete.uri')); return redirect($this->getPreviousUri('currencies.delete.uri'));
@ -142,10 +170,18 @@ class CurrencyController extends Controller
* @param Request $request * @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return View * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/ */
public function edit(Request $request, TransactionCurrency $currency) public function edit(Request $request, TransactionCurrency $currency)
{ {
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
// @codeCoverageIgnoreStart
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
return redirect(route('currencies.index'));
// @codeCoverageIgnoreEnd
}
$subTitleIcon = 'fa-pencil'; $subTitleIcon = 'fa-pencil';
$subTitle = trans('breadcrumbs.edit_currency', ['name' => $currency->name]); $subTitle = trans('breadcrumbs.edit_currency', ['name' => $currency->name]);
$currency->symbol = htmlentities($currency->symbol); $currency->symbol = htmlentities($currency->symbol);
@ -164,47 +200,45 @@ class CurrencyController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param CurrencyRepositoryInterface $repository
* *
* @return View * @return View
*/ */
public function index(Request $request, CurrencyRepositoryInterface $repository) public function index(Request $request)
{ {
$currencies = $repository->get(); $currencies = $this->repository->get();
$defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR'))); $defaultCurrency = $this->repository->getCurrencyByPreference(Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR')));
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
if (!auth()->user()->hasRole('owner')) {
$request->session()->flash('info', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')])); $request->session()->flash('info', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
} }
return view('currencies.index', compact('currencies', 'defaultCurrency')); return view('currencies.index', compact('currencies', 'defaultCurrency'));
} }
/** /**
*
* @param CurrencyFormRequest $request * @param CurrencyFormRequest $request
* @param CurrencyRepositoryInterface $repository
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function store(CurrencyFormRequest $request, CurrencyRepositoryInterface $repository) public function store(CurrencyFormRequest $request)
{ {
if (!auth()->user()->hasRole('owner')) { if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
// @codeCoverageIgnoreStart
Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.'); Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.');
return redirect($this->getPreviousUri('currencies.create.uri')); return redirect($this->getPreviousUri('currencies.create.uri'));
// @codeCoverageIgnoreEnd
} }
$data = $request->getCurrencyData(); $data = $request->getCurrencyData();
$currency = $repository->store($data); $currency = $this->repository->store($data);
$request->session()->flash('success', trans('firefly.created_currency', ['name' => $currency->name])); $request->session()->flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// @codeCoverageIgnoreStart
$request->session()->put('currencies.create.fromStore', true); $request->session()->put('currencies.create.fromStore', true);
return redirect(route('currencies.create'))->withInput(); return redirect(route('currencies.create'))->withInput();
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('currencies.create.uri')); return redirect($this->getPreviousUri('currencies.create.uri'));
@ -212,25 +246,32 @@ class CurrencyController extends Controller
/** /**
* @param CurrencyFormRequest $request * @param CurrencyFormRequest $request
* @param CurrencyRepositoryInterface $repository
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function update(CurrencyFormRequest $request, CurrencyRepositoryInterface $repository, TransactionCurrency $currency) public function update(CurrencyFormRequest $request, TransactionCurrency $currency)
{ {
$data = $request->getCurrencyData(); if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
if (auth()->user()->hasRole('owner')) { // @codeCoverageIgnoreStart
$currency = $repository->update($currency, $data); $request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
return redirect(route('currencies.index'));
// @codeCoverageIgnoreEnd
} }
$data = $request->getCurrencyData();
$currency = $this->repository->update($currency, $data);
$request->session()->flash('success', trans('firefly.updated_currency', ['name' => $currency->name])); $request->session()->flash('success', trans('firefly.updated_currency', ['name' => $currency->name]));
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// @codeCoverageIgnoreStart
$request->session()->put('currencies.edit.fromUpdate', true); $request->session()->put('currencies.edit.fromUpdate', true);
return redirect(route('currencies.edit', [$currency->id])); return redirect(route('currencies.edit', [$currency->id]));
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('currencies.edit.uri')); return redirect($this->getPreviousUri('currencies.edit.uri'));

View File

@ -58,7 +58,7 @@ class HelpController extends Controller
return Response::json($content); return Response::json($content);
} }
$content = $help->getFromGithub($language, $route); $content = $help->getFromGithub($route, $language);
$notYourLanguage = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>'; $notYourLanguage = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>';
// get backup language content (try English): // get backup language content (try English):
@ -66,10 +66,10 @@ class HelpController extends Controller
$language = 'en_US'; $language = 'en_US';
if ($help->inCache($route, $language)) { if ($help->inCache($route, $language)) {
Log::debug(sprintf('Help text %s was in cache.', $language)); Log::debug(sprintf('Help text %s was in cache.', $language));
$content = $help->getFromCache($route, $language); $content = $notYourLanguage . $help->getFromCache($route, $language);
} }
if (!$help->inCache($route, $language)) { if (!$help->inCache($route, $language)) {
$content = trim($notYourLanguage . $help->getFromGithub($language, $route)); $content = trim($notYourLanguage . $help->getFromGithub($route, $language));
} }
} }

View File

@ -8,7 +8,7 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
@ -20,6 +20,7 @@ use FireflyIII\Import\Setup\SetupInterface;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response as LaravelResponse; use Illuminate\Http\Response as LaravelResponse;
use Log; use Log;
@ -87,8 +88,6 @@ class ImportController extends Controller
{ {
Log::debug('Now at start of configure()'); Log::debug('Now at start of configure()');
if (!$this->jobInCorrectStep($job, 'configure')) { if (!$this->jobInCorrectStep($job, 'configure')) {
Log::debug('Job is not in correct state for configure()', ['status' => $job->status]);
return $this->redirectToCorrectStep($job); return $this->redirectToCorrectStep($job);
} }
@ -100,8 +99,6 @@ class ImportController extends Controller
$subTitleIcon = 'fa-wrench'; $subTitleIcon = 'fa-wrench';
return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon')); return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon'));
} }
/** /**
@ -145,8 +142,6 @@ class ImportController extends Controller
public function finished(ImportJob $job) public function finished(ImportJob $job)
{ {
if (!$this->jobInCorrectStep($job, 'finished')) { if (!$this->jobInCorrectStep($job, 'finished')) {
Log::debug('Job is not in correct state for finished()', ['status' => $job->status]);
return $this->redirectToCorrectStep($job); return $this->redirectToCorrectStep($job);
} }
@ -225,12 +220,12 @@ class ImportController extends Controller
* Step 4. Save the configuration. * Step 4. Save the configuration.
* *
* @param Request $request * @param Request $request
* @param ImportJobRepositoryInterface $repository
* @param ImportJob $job * @param ImportJob $job
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/ */
public function postConfigure(Request $request, ImportJob $job) public function postConfigure(Request $request, ImportJobRepositoryInterface $repository, ImportJob $job)
{ {
Log::debug('Now in postConfigure()', ['job' => $job->key]); Log::debug('Now in postConfigure()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'process')) { if (!$this->jobInCorrectStep($job, 'process')) {
@ -245,8 +240,7 @@ class ImportController extends Controller
$importer->saveImportConfiguration($data, $files); $importer->saveImportConfiguration($data, $files);
// update job: // update job:
$job->status = 'import_configuration_saved'; $repository->updateStatus($job, 'import_configuration_saved');
$job->save();
// return redirect to settings. // return redirect to settings.
// this could loop until the user is done. // this could loop until the user is done.
@ -285,12 +279,10 @@ class ImportController extends Controller
* @return View * @return View
* @throws FireflyException * @throws FireflyException
*/ */
public function settings(ImportJob $job) public function settings(ImportJobRepositoryInterface $repository, ImportJob $job)
{ {
Log::debug('Now in settings()', ['job' => $job->key]); Log::debug('Now in settings()', ['job' => $job->key]);
if (!$this->jobInCorrectStep($job, 'settings')) { if (!$this->jobInCorrectStep($job, 'settings')) {
Log::debug('Job should not be in settings()');
return $this->redirectToCorrectStep($job); return $this->redirectToCorrectStep($job);
} }
Log::debug('Continue in settings()'); Log::debug('Continue in settings()');
@ -308,8 +300,7 @@ class ImportController extends Controller
} }
Log::debug('Job does NOT require user config.'); Log::debug('Job does NOT require user config.');
$job->status = 'settings_complete'; $repository->updateStatus($job, 'settings_complete');
$job->save();
// if no more settings, save job and continue to process thing. // if no more settings, save job and continue to process thing.
return redirect(route('import.complete', [$job->key])); return redirect(route('import.complete', [$job->key]));
@ -350,15 +341,17 @@ class ImportController extends Controller
return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
} }
/** /**
* This is step 2. It creates an Import Job. Stores the import. * This is step 2. It creates an Import Job. Stores the import.
* *
* @param ImportUploadRequest $request * @param ImportUploadRequest $request
* @param ImportJobRepositoryInterface $repository * @param ImportJobRepositoryInterface $repository
* @param UserRepositoryInterface $userRepository
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository) public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository, UserRepositoryInterface $userRepository)
{ {
Log::debug('Now in upload()'); Log::debug('Now in upload()');
// create import job: // create import job:
@ -375,7 +368,7 @@ class ImportController extends Controller
$disk = Storage::disk('upload'); $disk = Storage::disk('upload');
// user is demo user, replace upload with prepared file. // user is demo user, replace upload with prepared file.
if (auth()->user()->hasRole('demo')) { if ($userRepository->hasRole(auth()->user(), 'demo')) {
$stubsDisk = Storage::disk('stubs'); $stubsDisk = Storage::disk('stubs');
$content = $stubsDisk->get('demo-import.csv'); $content = $stubsDisk->get('demo-import.csv');
$contentEncrypted = Crypt::encrypt($content); $contentEncrypted = Crypt::encrypt($content);
@ -384,14 +377,13 @@ class ImportController extends Controller
// also set up prepared configuration. // also set up prepared configuration.
$configuration = json_decode($stubsDisk->get('demo-configuration.json'), true); $configuration = json_decode($stubsDisk->get('demo-configuration.json'), true);
$job->configuration = $configuration; $repository->setConfiguration($job, $configuration);
$job->save();
Log::debug('Set configuration for demo user', $configuration); Log::debug('Set configuration for demo user', $configuration);
// also flash info // also flash info
Session::flash('info', trans('demo.import-configure-security')); Session::flash('info', trans('demo.import-configure-security'));
} }
if (!auth()->user()->hasRole('demo')) { if (!$userRepository->hasRole(auth()->user(), 'demo')) {
// user is not demo, process original upload: // user is not demo, process original upload:
$disk->put($newName, $contentEncrypted); $disk->put($newName, $contentEncrypted);
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]); Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
@ -411,18 +403,15 @@ class ImportController extends Controller
$configRaw = $configFileObject->fread($configFileObject->getSize()); $configRaw = $configFileObject->fread($configFileObject->getSize());
$configuration = json_decode($configRaw, true); $configuration = json_decode($configRaw, true);
// @codeCoverageIgnoreStart
if (!is_null($configuration) && is_array($configuration)) { if (!is_null($configuration) && is_array($configuration)) {
Log::debug('Found configuration', $configuration); Log::debug('Found configuration', $configuration);
$job->configuration = $configuration; $repository->setConfiguration($job, $configuration);
$job->save();
} }
// @codeCoverageIgnoreEnd
} }
// if user is demo user, replace config with prepared config:
return redirect(route('import.configure', [$job->key])); return redirect(route('import.configure', [$job->key]));
} }
/** /**
@ -440,6 +429,8 @@ class ImportController extends Controller
return $job->status === 'import_status_never_started'; return $job->status === 'import_status_never_started';
case 'settings': case 'settings':
case 'store-settings': case 'store-settings':
Log::debug(sprintf('Job %d with key %s has status %s', $job->id, $job->key, $job->status));
return $job->status === 'import_configuration_saved'; return $job->status === 'import_configuration_saved';
case 'finished': case 'finished':
return $job->status === 'import_complete'; return $job->status === 'import_complete';
@ -449,7 +440,7 @@ class ImportController extends Controller
return ($job->status === 'settings_complete') || ($job->status === 'import_running'); return ($job->status === 'settings_complete') || ($job->status === 'import_running');
} }
return false; return false; // @codeCoverageIgnore
} }
@ -475,7 +466,7 @@ class ImportController extends Controller
return $importer; return $importer;
} }
throw new FireflyException(sprintf('"%s" is not a valid file type', $type)); throw new FireflyException(sprintf('"%s" is not a valid file type', $type)); // @codeCoverageIgnore
} }
@ -507,7 +498,7 @@ class ImportController extends Controller
return redirect(route('import.finished', [$job->key])); return redirect(route('import.finished', [$job->key]));
} }
throw new FireflyException('Cannot redirect for job state ' . $job->status); throw new FireflyException('Cannot redirect for job state ' . $job->status); // @codeCoverageIgnore
} }
} }

View File

@ -128,7 +128,7 @@ class JavascriptController extends Controller
switch ($viewRange) { switch ($viewRange) {
default: default:
throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); // @codeCoverageIgnore
case '1D': case '1D':
case 'custom': case 'custom':
$format = (string)trans('config.month_and_day'); $format = (string)trans('config.month_and_day');

View File

@ -287,7 +287,7 @@ class JsonController extends Controller
{ {
$pref = Preferences::get('tour', true); $pref = Preferences::get('tour', true);
if (!$pref) { if (!$pref) {
throw new FireflyException('Cannot find preference for tour. Exit.'); 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']; $headers = ['main-content', 'sidebar-toggle', 'account-menu', 'budget-menu', 'report-menu', 'transaction-menu', 'option-menu', 'main-content-end'];
$steps = []; $steps = [];

View File

@ -229,7 +229,8 @@ class PiggyBankController extends Controller
'sumOfTargets' => $piggyBank->targetamount, 'sumOfTargets' => $piggyBank->targetamount,
'leftToSave' => $piggyBank->leftToSave, 'leftToSave' => $piggyBank->leftToSave,
]; ];
} else { }
if (isset($accounts[$account->id])) {
$accounts[$account->id]['sumOfSaved'] = bcadd($accounts[$account->id]['sumOfSaved'], strval($piggyBank->savedSoFar)); $accounts[$account->id]['sumOfSaved'] = bcadd($accounts[$account->id]['sumOfSaved'], strval($piggyBank->savedSoFar));
$accounts[$account->id]['sumOfTargets'] = bcadd($accounts[$account->id]['sumOfTargets'], $piggyBank->targetamount); $accounts[$account->id]['sumOfTargets'] = bcadd($accounts[$account->id]['sumOfTargets'], $piggyBank->targetamount);
$accounts[$account->id]['leftToSave'] = bcadd($accounts[$account->id]['leftToSave'], $piggyBank->leftToSave); $accounts[$account->id]['leftToSave'] = bcadd($accounts[$account->id]['leftToSave'], $piggyBank->leftToSave);
@ -272,32 +273,16 @@ class PiggyBankController extends Controller
public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank) public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{ {
$amount = $request->get('amount'); $amount = $request->get('amount');
Log::debug(sprintf('Found amount is %s', $amount));
/** @var Carbon $date */
$date = session('end', Carbon::now()->endOfMonth());
$leftOnAccount = $piggyBank->leftOnAccount($date);
$savedSoFar = strval($piggyBank->currentRelevantRep()->currentamount);
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = strval(min(round($leftOnAccount, 12), round($leftToSave, 12)));
if (bccomp($amount, $maxAmount) <= 0) { if ($repository->canAddAmount($piggyBank, $amount)) {
$repetition = $piggyBank->currentRelevantRep(); $repository->addAmount($piggyBank, $amount);
$currentAmount = $repetition->currentamount ?? '0'; Session::flash('success', strval(trans('firefly.added_amount_to_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name])));
$repetition->currentamount = bcadd($currentAmount, $amount);
$repetition->save();
// create event
$repository->createEvent($piggyBank, $amount);
Session::flash(
'success', strval(trans('firefly.added_amount_to_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name]))
);
Preferences::mark(); Preferences::mark();
return redirect(route('piggy-banks.index')); return redirect(route('piggy-banks.index'));
} }
Log::error('Cannot add ' . $amount . ' because max amount is ' . $maxAmount . ' (left on account is ' . $leftOnAccount . ')'); Log::error('Cannot add ' . $amount . ' because canAddAmount returned false.');
Session::flash('error', strval(trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)]))); Session::flash('error', strval(trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
return redirect(route('piggy-banks.index')); return redirect(route('piggy-banks.index'));
@ -312,26 +297,24 @@ class PiggyBankController extends Controller
*/ */
public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank) public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{ {
$amount = strval(round($request->get('amount'), 12)); $amount = $request->get('amount');
if ($repository->canRemoveAmount($piggyBank, $amount)) {
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $repository->removeAmount($piggyBank, $amount);
if (bccomp($amount, $savedSoFar) <= 0) {
$repetition = $piggyBank->currentRelevantRep();
$repetition->currentamount = bcsub($repetition->currentamount, $amount);
$repetition->save();
// create event
$repository->createEvent($piggyBank, bcmul($amount, '-1'));
Session::flash( Session::flash(
'success', strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])) 'success', strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name]))
); );
Preferences::mark(); Preferences::mark();
return redirect(route('piggy-banks.index')); return redirect(route('piggy-banks.index'));
} }
$amount = strval(round($request->get('amount'), 12));
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
if (bccomp($amount, $savedSoFar) <= 0) {
}
Session::flash('error', strval(trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)]))); Session::flash('error', strval(trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
return redirect(route('piggy-banks.index')); return redirect(route('piggy-banks.index'));
@ -391,9 +374,11 @@ class PiggyBankController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// @codeCoverageIgnoreStart
Session::put('piggy-banks.create.fromStore', true); Session::put('piggy-banks.create.fromStore', true);
return redirect(route('piggy-banks.create'))->withInput(); return redirect(route('piggy-banks.create'))->withInput();
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('piggy-banks.edit.uri')); return redirect($this->getPreviousUri('piggy-banks.edit.uri'));
@ -415,9 +400,11 @@ class PiggyBankController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// @codeCoverageIgnoreStart
Session::put('piggy-banks.edit.fromUpdate', true); Session::put('piggy-banks.edit.fromUpdate', true);
return redirect(route('piggy-banks.edit', [$piggyBank->id])); return redirect(route('piggy-banks.edit', [$piggyBank->id]));
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('piggy-banks.edit.uri')); return redirect($this->getPreviousUri('piggy-banks.edit.uri'));

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Popup; namespace FireflyIII\Http\Controllers\Popup;
@ -17,17 +17,14 @@ namespace FireflyIII\Http\Controllers\Popup;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collection\BalanceLine; use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Report\PopupReportInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\Binder\AccountList; use FireflyIII\Support\Binder\AccountList;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use InvalidArgumentException; use InvalidArgumentException;
use Response; use Response;
use View; use View;
@ -40,6 +37,44 @@ use View;
class ReportController extends Controller class ReportController extends Controller
{ {
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var BudgetRepositoryInterface */
private $budgetRepository;
/** @var CategoryRepositoryInterface */
private $categoryRepository;
/** @var PopupReportInterface */
private $popupHelper;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var AccountRepositoryInterface $repository */
$this->accountRepository = app(AccountRepositoryInterface::class);
/** @var BudgetRepositoryInterface $repository */
$this->budgetRepository = app(BudgetRepositoryInterface::class);
/** @var CategoryRepositoryInterface categoryRepository */
$this->categoryRepository = app(CategoryRepositoryInterface::class);
/** @var PopupReportInterface popupHelper */
$this->popupHelper = app(PopupReportInterface::class);
return $next($request);
}
);
}
/** /**
* @param Request $request * @param Request $request
* *
@ -59,7 +94,6 @@ class ReportController extends Controller
throw new FireflyException('Firefly cannot handle "' . e($attributes['location']) . '" '); throw new FireflyException('Firefly cannot handle "' . e($attributes['location']) . '" ');
case 'budget-spent-amount': case 'budget-spent-amount':
$html = $this->budgetSpentAmount($attributes); $html = $this->budgetSpentAmount($attributes);
break; break;
case 'expense-entry': case 'expense-entry':
$html = $this->expenseEntry($attributes); $html = $this->expenseEntry($attributes);
@ -89,62 +123,25 @@ class ReportController extends Controller
private function balanceAmount(array $attributes): string private function balanceAmount(array $attributes): string
{ {
$role = intval($attributes['role']); $role = intval($attributes['role']);
$budget = $this->budgetRepository->find(intval($attributes['budgetId']));
/** @var BudgetRepositoryInterface $budgetRepository */ $account = $this->accountRepository->find(intval($attributes['accountId']));
$budgetRepository = app(BudgetRepositoryInterface::class);
$budget = $budgetRepository->find(intval($attributes['budgetId']));
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::WITHDRAWAL];
switch (true) { switch (true) {
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)): case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
/** @var JournalCollectorInterface $collector */ // normal row with a budget:
$collector = app(JournalCollectorInterface::class); $journals = $this->popupHelper->balanceForBudget($budget, $account, $attributes);
$collector
->setAccounts(new Collection([$account]))
->setRange($attributes['startDate'], $attributes['endDate'])
->setBudget($budget);
$journals = $collector->getJournals();
break; break;
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)): case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
// normal row without a budget:
$journals = $this->popupHelper->balanceForNoBudget($account, $attributes);
$budget->name = strval(trans('firefly.no_budget')); $budget->name = strval(trans('firefly.no_budget'));
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setTypes($types)
->setRange($attributes['startDate'], $attributes['endDate'])
->withoutBudget();
$journals = $collector->getJournals();
break; break;
case ($role === BalanceLine::ROLE_DIFFROLE): case ($role === BalanceLine::ROLE_DIFFROLE):
/** @var JournalCollectorInterface $collector */ $journals = $this->popupHelper->balanceDifference($account, $attributes);
$collector = app(JournalCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setTypes($types)
->setRange($attributes['startDate'], $attributes['endDate'])
->withoutBudget();
$journals = $collector->getJournals();
$budget->name = strval(trans('firefly.leftUnbalanced')); $budget->name = strval(trans('firefly.leftUnbalanced'));
$journals = $journals->filter(
function (Transaction $transaction) {
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
if ($tags === 0) {
return true;
}
return false;
}
);
break; break;
case ($role === BalanceLine::ROLE_TAGROLE): case ($role === BalanceLine::ROLE_TAGROLE):
// row with tag info.
throw new FireflyException('Firefly cannot handle this type of info-button (BalanceLine::TagRole)'); throw new FireflyException('Firefly cannot handle this type of info-button (BalanceLine::TagRole)');
} }
$view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); $view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render();
@ -162,27 +159,8 @@ class ReportController extends Controller
*/ */
private function budgetSpentAmount(array $attributes): string private function budgetSpentAmount(array $attributes): string
{ {
// need to find the budget $budget = $this->budgetRepository->find(intval($attributes['budgetId']));
// then search for expenses in the given period $journals = $this->popupHelper->byBudget($budget, $attributes);
// list them in some table format.
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$budget = $repository->find(intval($attributes['budgetId']));
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector
->setAccounts($attributes['accounts'])
->setRange($attributes['startDate'], $attributes['endDate']);
if (is_null($budget->id)) {
$collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
}
if (!is_null($budget->id)) {
// get all expenses in budget in period:
$collector->setBudget($budget);
}
$journals = $collector->getJournals();
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
return $view; return $view;
@ -198,17 +176,8 @@ class ReportController extends Controller
*/ */
private function categoryEntry(array $attributes): string private function categoryEntry(array $attributes): string
{ {
/** @var CategoryRepositoryInterface $repository */ $category = $this->categoryRepository->find(intval($attributes['categoryId']));
$repository = app(CategoryRepositoryInterface::class); $journals = $this->popupHelper->byCategory($category, $attributes);
$category = $repository->find(intval($attributes['categoryId']));
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($attributes['accounts'])->setTypes($types)
->setRange($attributes['startDate'], $attributes['endDate'])
->setCategory($category);
$journals = $collector->getJournals(); // 7193
$view = view('popup.report.category-entry', compact('journals', 'category'))->render(); $view = view('popup.report.category-entry', compact('journals', 'category'))->render();
return $view; return $view;
@ -224,28 +193,8 @@ class ReportController extends Controller
*/ */
private function expenseEntry(array $attributes): string private function expenseEntry(array $attributes): string
{ {
/** @var AccountRepositoryInterface $repository */ $account = $this->accountRepository->find(intval($attributes['accountId']));
$repository = app(AccountRepositoryInterface::class); $journals = $this->popupHelper->byExpenses($account, $attributes);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
$journals = $collector->getJournals();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
// filter for transfers and withdrawals TO the given $account
$journals = $journals->filter(
function (Transaction $transaction) use ($report) {
// get the destinations:
$sources = $transaction->transactionJournal->sourceAccountList()->pluck('id')->toArray();
// do these intersect with the current list?
return !empty(array_intersect($report, $sources));
}
);
$view = view('popup.report.expense-entry', compact('journals', 'account'))->render(); $view = view('popup.report.expense-entry', compact('journals', 'account'))->render();
return $view; return $view;
@ -261,27 +210,8 @@ class ReportController extends Controller
*/ */
private function incomeEntry(array $attributes): string private function incomeEntry(array $attributes): string
{ {
/** @var AccountRepositoryInterface $repository */ $account = $this->accountRepository->find(intval($attributes['accountId']));
$repository = app(AccountRepositoryInterface::class); $journals = $this->popupHelper->byIncome($account, $attributes);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
$journals = $collector->getJournals();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
// filter the set so the destinations outside of $attributes['accounts'] are not included.
$journals = $journals->filter(
function (Transaction $transaction) use ($report) {
// get the destinations:
$destinations = $transaction->destinationAccountList($transaction->transactionJournal)->pluck('id')->toArray();
// do these intersect with the current list?
return !empty(array_intersect($report, $destinations));
}
);
$view = view('popup.report.income-entry', compact('journals', 'account'))->render(); $view = view('popup.report.income-entry', compact('journals', 'account'))->render();
return $view; return $view;

View File

@ -9,12 +9,14 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests\TokenFormRequest; use FireflyIII\Http\Requests\TokenFormRequest;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use PragmaRX\Google2FA\Contracts\Google2FA; use PragmaRX\Google2FA\Contracts\Google2FA;
use Preferences; use Preferences;
@ -55,8 +57,11 @@ class PreferencesController extends Controller
public function code(Google2FA $google2fa) public function code(Google2FA $google2fa)
{ {
$domain = $this->getDomain(); $domain = $this->getDomain();
$secretKey = 'FIREFLYIII';
$secretKey = str_pad($secretKey, intval(pow(2, ceil(log(strlen($secretKey), 2)))), 'X');
/** @noinspection PhpMethodParametersCountMismatchInspection */ /** @noinspection PhpMethodParametersCountMismatchInspection */
$secret = $google2fa->generateSecretKey(32, auth()->user()->id); $secret = $google2fa->generateSecretKey(16, $secretKey);
Session::flash('two-factor-secret', $secret); Session::flash('two-factor-secret', $secret);
$image = $google2fa->getQRCodeInline('Firefly III at ' . $domain, auth()->user()->email, $secret, 150); $image = $google2fa->getQRCodeInline('Firefly III at ' . $domain, auth()->user()->email, $secret, 150);
@ -131,7 +136,7 @@ class PreferencesController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function postIndex(Request $request) public function postIndex(Request $request, UserRepositoryInterface $repository)
{ {
// front page accounts // front page accounts
$frontPageAccounts = []; $frontPageAccounts = [];
@ -160,16 +165,15 @@ class PreferencesController extends Controller
Preferences::set('showDepositsFrontpage', $showDepositsFrontpage); Preferences::set('showDepositsFrontpage', $showDepositsFrontpage);
// save page size: // save page size:
Preferences::set('transactionPageSize', 50);
$transactionPageSize = intval($request->get('transactionPageSize')); $transactionPageSize = intval($request->get('transactionPageSize'));
if ($transactionPageSize > 0 && $transactionPageSize < 1337) { if ($transactionPageSize > 0 && $transactionPageSize < 1337) {
Preferences::set('transactionPageSize', $transactionPageSize); Preferences::set('transactionPageSize', $transactionPageSize);
} else {
Preferences::set('transactionPageSize', 50);
} }
$twoFactorAuthEnabled = false; $twoFactorAuthEnabled = false;
$hasTwoFactorAuthSecret = false; $hasTwoFactorAuthSecret = false;
if (!auth()->user()->hasRole('demo')) { if (!$repository->hasRole(auth()->user(), 'demo')) {
// two factor auth // two factor auth
$twoFactorAuthEnabled = intval($request->get('twoFactorAuthEnabled')); $twoFactorAuthEnabled = intval($request->get('twoFactorAuthEnabled'));
$hasTwoFactorAuthSecret = !is_null(Preferences::get('twoFactorAuthSecret')); $hasTwoFactorAuthSecret = !is_null(Preferences::get('twoFactorAuthSecret'));

View File

@ -9,14 +9,16 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use FireflyIII\Exceptions\ValidationException; use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Http\Middleware\IsLimitedUser;
use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Hash; use Hash;
use Log; use Log;
use Session; use Session;
@ -45,6 +47,8 @@ class ProfileController extends Controller
return $next($request); return $next($request);
} }
); );
$this->middleware(IsLimitedUser::class);
} }
/** /**
@ -52,16 +56,6 @@ class ProfileController extends Controller
*/ */
public function changePassword() public function changePassword()
{ {
if (intval(getenv('SANDSTORM')) === 1) {
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available')));
}
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
return redirect(route('profile.index'));
}
$title = auth()->user()->email; $title = auth()->user()->email;
$subTitle = strval(trans('firefly.change_your_password')); $subTitle = strval(trans('firefly.change_your_password'));
$subTitleIcon = 'fa-key'; $subTitleIcon = 'fa-key';
@ -74,16 +68,6 @@ class ProfileController extends Controller
*/ */
public function deleteAccount() public function deleteAccount()
{ {
if (intval(getenv('SANDSTORM')) === 1) {
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available')));
}
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
return redirect(route('profile.index'));
}
$title = auth()->user()->email; $title = auth()->user()->email;
$subTitle = strval(trans('firefly.delete_account')); $subTitle = strval(trans('firefly.delete_account'));
$subTitleIcon = 'fa-trash'; $subTitleIcon = 'fa-trash';
@ -111,32 +95,18 @@ class ProfileController extends Controller
*/ */
public function postChangePassword(ProfileFormRequest $request, UserRepositoryInterface $repository) public function postChangePassword(ProfileFormRequest $request, UserRepositoryInterface $repository)
{ {
if (intval(getenv('SANDSTORM')) === 1) { // the request has already validated both new passwords must be equal.
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available'))); $current = $request->get('current_password');
} $new = $request->get('new_password');
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
return redirect(route('profile.index'));
}
// old, new1, new2
if (!Hash::check($request->get('current_password'), auth()->user()->password)) {
Session::flash('error', strval(trans('firefly.invalid_current_password')));
return redirect(route('profile.change-password'));
}
try { try {
$this->validatePassword($request->get('current_password'), $request->get('new_password')); $this->validatePassword(auth()->user(), $current, $new);
} catch (ValidationException $e) { } catch (ValidationException $e) {
Session::flash('error', $e->getMessage()); Session::flash('error', $e->getMessage());
return redirect(route('profile.change-password')); return redirect(route('profile.change-password'));
} }
// update the user with the new password.
$repository->changePassword(auth()->user(), $request->get('new_password')); $repository->changePassword(auth()->user(), $request->get('new_password'));
Session::flash('success', strval(trans('firefly.password_changed'))); Session::flash('success', strval(trans('firefly.password_changed')));
@ -151,17 +121,6 @@ class ProfileController extends Controller
*/ */
public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request) public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request)
{ {
if (intval(getenv('SANDSTORM')) === 1) {
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available')));
}
if (auth()->user()->hasRole('demo')) {
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
return redirect(route('profile.index'));
}
// old, new1, new2
if (!Hash::check($request->get('password'), auth()->user()->password)) { if (!Hash::check($request->get('password'), auth()->user()->password)) {
Session::flash('error', strval(trans('firefly.invalid_password'))); Session::flash('error', strval(trans('firefly.invalid_password')));
@ -182,15 +141,21 @@ class ProfileController extends Controller
} }
/** /**
* @param string $old * @param User $user
* @param string $current
* @param string $new * @param string $new
* @param string $newConfirmation
* *
* @return bool * @return bool
* @throws ValidationException * @throws ValidationException
*/ */
protected function validatePassword(string $old, string $new): bool protected function validatePassword(User $user, string $current, string $new): bool
{ {
if ($new === $old) { if (!Hash::check($current, auth()->user()->password)) {
throw new ValidationException(strval(trans('firefly.invalid_current_password')));
}
if ($current === $new) {
throw new ValidationException(strval(trans('firefly.should_change'))); throw new ValidationException(strval(trans('firefly.should_change')));
} }

View File

@ -45,8 +45,6 @@ class CategoryController extends Controller
$cache->addProperty('category-period-expenses-report'); $cache->addProperty('category-period-expenses-report');
$cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) { if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
/** @var CategoryRepositoryInterface $repository */ /** @var CategoryRepositoryInterface $repository */
@ -79,8 +77,6 @@ class CategoryController extends Controller
$cache->addProperty('category-period-income-report'); $cache->addProperty('category-period-income-report');
$cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) { if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
/** @var CategoryRepositoryInterface $repository */ /** @var CategoryRepositoryInterface $repository */

View File

@ -9,12 +9,11 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Report\ReportGeneratorFactory; use FireflyIII\Generator\Report\ReportGeneratorFactory;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Requests\ReportFormRequest; use FireflyIII\Http\Requests\ReportFormRequest;
@ -26,6 +25,7 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Preferences; use Preferences;
use Response; use Response;
use Session; use Session;
@ -48,6 +48,7 @@ class ReportController extends Controller
{ {
parent::__construct(); parent::__construct();
$this->helper = app(ReportHelperInterface::class);
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
@ -55,8 +56,6 @@ class ReportController extends Controller
View::share('mainTitleIcon', 'fa-line-chart'); View::share('mainTitleIcon', 'fa-line-chart');
View::share('subTitleIcon', 'fa-calendar'); View::share('subTitleIcon', 'fa-calendar');
$this->helper = app(ReportHelperInterface::class);
return $next($request); return $next($request);
} }
); );
@ -73,7 +72,7 @@ class ReportController extends Controller
public function auditReport(Collection $accounts, Carbon $start, Carbon $end) public function auditReport(Collection $accounts, Carbon $start, Carbon $end)
{ {
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date')); // @codeCoverageIgnore
} }
if ($start < session('first')) { if ($start < session('first')) {
$start = session('first'); $start = session('first');
@ -109,7 +108,7 @@ class ReportController extends Controller
public function budgetReport(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) public function budgetReport(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
{ {
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date')); // @codeCoverageIgnore
} }
if ($start < session('first')) { if ($start < session('first')) {
$start = session('first'); $start = session('first');
@ -145,7 +144,7 @@ class ReportController extends Controller
public function categoryReport(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) public function categoryReport(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
{ {
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date')); // @codeCoverageIgnore
} }
if ($start < session('first')) { if ($start < session('first')) {
$start = session('first'); $start = session('first');
@ -251,10 +250,9 @@ class ReportController extends Controller
/** /**
* @param ReportFormRequest $request * @param ReportFormRequest $request
* *
* @return RedirectResponse * @return RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/ */
public function postIndex(ReportFormRequest $request): RedirectResponse public function postIndex(ReportFormRequest $request)
{ {
// report type: // report type:
$reportType = $request->get('report_type'); $reportType = $request->get('report_type');
@ -266,6 +264,7 @@ class ReportController extends Controller
$tags = join(',', $request->getTagList()->pluck('tag')->toArray()); $tags = join(',', $request->getTagList()->pluck('tag')->toArray());
if ($request->getAccountList()->count() === 0) { if ($request->getAccountList()->count() === 0) {
Log::debug('Account count is zero');
Session::flash('error', trans('firefly.select_more_than_one_account')); Session::flash('error', trans('firefly.select_more_than_one_account'));
return redirect(route('reports.index')); return redirect(route('reports.index'));
@ -293,14 +292,7 @@ class ReportController extends Controller
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date'));
} }
// lower threshold
if ($start < session('first')) {
$start = session('first');
}
switch ($reportType) { switch ($reportType) {
default:
throw new FireflyException(sprintf('Firefly does not support the "%s"-report yet.', $reportType));
case 'category': case 'category':
$uri = route('reports.report.category', [$accounts, $categories, $start, $end]); $uri = route('reports.report.category', [$accounts, $categories, $start, $end]);
break; break;
@ -332,7 +324,7 @@ class ReportController extends Controller
public function tagReport(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) public function tagReport(Collection $accounts, Collection $tags, Carbon $start, Carbon $end)
{ {
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date')); // @codeCoverageIgnore
} }
if ($start < session('first')) { if ($start < session('first')) {
$start = session('first'); $start = session('first');

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
@ -255,10 +255,11 @@ class RuleController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL: // @codeCoverageIgnoreStart
Session::put('rules.create.fromStore', true); Session::put('rules.create.fromStore', true);
return redirect(route('rules.create', [$ruleGroup]))->withInput(); return redirect(route('rules.create', [$ruleGroup]))->withInput();
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('rules.create.uri')); return redirect($this->getPreviousUri('rules.create.uri'));
@ -340,10 +341,11 @@ class RuleController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL: // @codeCoverageIgnoreStart
Session::put('rules.edit.fromUpdate', true); Session::put('rules.edit.fromUpdate', true);
return redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]); return redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('rules.edit.uri')); return redirect($this->getPreviousUri('rules.edit.uri'));
@ -473,7 +475,7 @@ class RuleController extends Controller
$actions[] = view( $actions[] = view(
'rules.partials.action', 'rules.partials.action',
[ [
'oldTrigger' => $entry, 'oldAction' => $entry,
'oldValue' => $request->old('rule-action-value')[$index], 'oldValue' => $request->old('rule-action-value')[$index],
'oldChecked' => $checked, 'oldChecked' => $checked,
'count' => $count, 'count' => $count,

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
@ -217,10 +217,11 @@ class RuleGroupController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL: // @codeCoverageIgnoreStart
Session::put('rule-groups.create.fromStore', true); Session::put('rule-groups.create.fromStore', true);
return redirect(route('rule-groups.create'))->withInput(); return redirect(route('rule-groups.create'))->withInput();
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('rule-groups.create.uri')); return redirect($this->getPreviousUri('rule-groups.create.uri'));
@ -261,10 +262,11 @@ class RuleGroupController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL: // @codeCoverageIgnoreStart
Session::put('rule-groups.edit.fromUpdate', true); Session::put('rule-groups.edit.fromUpdate', true);
return redirect(route('rule-groups.edit', [$ruleGroup->id]))->withInput(['return_to_edit' => 1]); return redirect(route('rule-groups.edit', [$ruleGroup->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
} }
// redirect to previous URL. // redirect to previous URL.

View File

@ -9,20 +9,19 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use Exception;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\TagFormRequest; use FireflyIII\Http\Requests\TagFormRequest;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Navigation; use Navigation;
use Preferences; use Preferences;
use Session; use Session;
@ -145,7 +144,7 @@ class TagController extends Controller
* *
* @return View * @return View
*/ */
public function edit(Tag $tag) public function edit(Tag $tag, TagRepositoryInterface $repository)
{ {
$subTitle = trans('firefly.edit_tag', ['tag' => $tag->tag]); $subTitle = trans('firefly.edit_tag', ['tag' => $tag->tag]);
$subTitleIcon = 'fa-tag'; $subTitleIcon = 'fa-tag';
@ -159,8 +158,8 @@ class TagController extends Controller
/* /*
* Can this tag become another type? * Can this tag become another type?
*/ */
$allowAdvance = $tag->tagAllowAdvance(); $allowAdvance = $repository->tagAllowAdvance($tag);
$allowToBalancingAct = $tag->tagAllowBalancing(); $allowToBalancingAct = $repository->tagAllowBalancing($tag);
// edit tag options: // edit tag options:
if ($allowAdvance === false) { if ($allowAdvance === false) {
@ -223,99 +222,87 @@ class TagController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param JournalCollectorInterface $collector * @param TagRepositoryInterface $repository
* @param Tag $tag * @param Tag $tag
* @param string $moment
* *
* @return View * @return View
*/ */
public function show(Request $request, JournalCollectorInterface $collector, Tag $tag) public function show(Request $request, TagRepositoryInterface $repository, Tag $tag, string $moment = '')
{ {
$start = clone session('start', Carbon::now()->startOfMonth()); // default values:
$end = clone session('end', Carbon::now()->endOfMonth());
$subTitle = $tag->tag; $subTitle = $tag->tag;
$subTitleIcon = 'fa-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); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$periods = $this->getPeriodOverview($tag); $count = 0;
$loop = 0;
// use collector:
$collector->setAllAssetAccounts()
->setLimit($pageSize)->setPage($page)->setTag($tag)->withOpposingAccount()->disableInternalFilter()
->withBudgetInformation()->withCategoryInformation()->setRange($start, $end);
$journals = $collector->getPaginatedJournals();
$journals->setPath('tags/show/' . $tag->id);
$sum = $journals->sum(
function (Transaction $transaction) {
return $transaction->transaction_amount;
}
);
return view('tags.show', compact('tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end'));
}
/**
* @param Request $request
* @param JournalCollectorInterface $collector
* @param Tag $tag
*
* @return View
*/
public function showAll(Request $request, JournalCollectorInterface $collector, Tag $tag)
{
$subTitle = $tag->tag;
$subTitleIcon = 'fa-tag';
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag)
->withOpposingAccount()->disableInternalFilter()
->withBudgetInformation()->withCategoryInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('tags/show/' . $tag->id . '/all');
$sum = $journals->sum(
function (Transaction $transaction) {
return $transaction->transaction_amount;
}
);
return view('tags.show', compact('tag', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end'));
}
public function showByDate(Request $request, JournalCollectorInterface $collector, Tag $tag, string $date)
{
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = null;
$end = null;
$periods = new Collection;
try {
$start = new Carbon($date); // prep for "all" view.
$end = Navigation::endOfPeriod($start, $range); if ($moment === 'all') {
} catch (Exception $e) { $subTitle = trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
$start = Navigation::startOfPeriod($this->repository->firstUseDate($tag), $range); $start = $repository->firstUseDate($tag);
$end = Navigation::startOfPeriod($this->repository->lastUseDate($tag), $range); $end = new Carbon;
} }
$subTitle = $tag->tag; // prep for "specific date" view.
$subTitleIcon = 'fa-tag'; if (strlen($moment) > 0 && $moment !== 'all') {
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $start = new Carbon($moment);
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $end = Navigation::endOfPeriod($start, $range);
$subTitle = trans(
'firefly.journals_in_period_for_tag',
['tag' => $tag->tag,
'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$periods = $this->getPeriodOverview($tag); $periods = $this->getPeriodOverview($tag);
}
// use collector: // prep for current period
$collector->setAllAssetAccounts() if (strlen($moment) === 0) {
->setLimit($pageSize)->setPage($page)->setTag($tag)->withOpposingAccount()->disableInternalFilter() $start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
->withBudgetInformation()->withCategoryInformation()->setRange($start, $end); $end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview($tag);
$subTitle = trans(
'firefly.journals_in_period_for_tag',
['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
// grab journals, but be prepared to jump a period back to get the right ones:
Log::info('Now at tag loop start.');
while ($count === 0 && $loop < 3) {
$loop++;
Log::info('Count is zero, search for journals.');
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
->setTag($tag)->withBudgetInformation()->withCategoryInformation();
$journals = $collector->getPaginatedJournals(); $journals = $collector->getPaginatedJournals();
$journals->setPath('tags/show/' . $tag->id); $journals->setPath('tags/show/' . $tag->id);
$count = $journals->getCollection()->count();
$sum = $journals->sum( if ($count === 0) {
function (Transaction $transaction) { $start->subDay();
return $transaction->transaction_amount; $start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
} }
}
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)]
); );
return view('tags.show', compact('tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end'));
} }
$sum = '0';
return view('tags.show', compact('tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end', 'moment'));
}
/** /**
* @param TagFormRequest $request * @param TagFormRequest $request
@ -331,10 +318,11 @@ class TagController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('create_another')) === 1) { if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL: // @codeCoverageIgnoreStart
Session::put('tags.create.fromStore', true); Session::put('tags.create.fromStore', true);
return redirect(route('tags.create'))->withInput(); return redirect(route('tags.create'))->withInput();
// @codeCoverageIgnoreEnd
} }
return redirect($this->getPreviousUri('tags.create.uri')); return redirect($this->getPreviousUri('tags.create.uri'));
@ -356,10 +344,11 @@ class TagController extends Controller
Preferences::mark(); Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) { if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL: // @codeCoverageIgnoreStart
Session::put('tags.edit.fromUpdate', true); Session::put('tags.edit.fromUpdate', true);
return redirect(route('tags.edit', [$tag->id]))->withInput(['return_to_edit' => 1]); return redirect(route('tags.edit', [$tag->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
} }
// redirect to previous URL. // redirect to previous URL.
@ -396,9 +385,9 @@ class TagController extends Controller
// get expenses and what-not in this period and this tag. // get expenses and what-not in this period and this tag.
$arr = [ $arr = [
'date_string' => $end->format('Y-m-d'), 'string' => $end->format('Y-m-d'),
'date_name' => Navigation::periodShow($end, $range), 'name' => Navigation::periodShow($end, $range),
'date' => $end, 'date' => clone $end,
'spent' => $this->repository->spentInperiod($tag, $end, $currentEnd), 'spent' => $this->repository->spentInperiod($tag, $end, $currentEnd),
'earned' => $this->repository->earnedInperiod($tag, $end, $currentEnd), 'earned' => $this->repository->earnedInperiod($tag, $end, $currentEnd),
]; ];

View File

@ -173,7 +173,6 @@ class ConvertController extends Controller
$joined = $sourceType->type . '-' . $destinationType->type; $joined = $sourceType->type . '-' . $destinationType->type;
switch ($joined) { switch ($joined) {
default: default:
throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one
$destination = $sourceAccount; $destination = $sourceAccount;

View File

@ -14,13 +14,13 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Transaction; namespace FireflyIII\Http\Controllers\Transaction;
use Carbon\Carbon; use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\MassDeleteJournalRequest; use FireflyIII\Http\Requests\MassDeleteJournalRequest;
use FireflyIII\Http\Requests\MassEditJournalRequest; use FireflyIII\Http\Requests\MassEditJournalRequest;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Preferences; use Preferences;
@ -119,7 +119,12 @@ class MassController extends Controller
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$accountList = ExpandedForm::makeSelectList($repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
// get budgets
/** @var BudgetRepositoryInterface $budgetRepository */
$budgetRepository = app(BudgetRepositoryInterface::class);
$budgets = $budgetRepository->getBudgets();
// skip transactions that have multiple destinations // skip transactions that have multiple destinations
// or multiple sources: // or multiple sources:
@ -177,7 +182,7 @@ class MassController extends Controller
$journals = $filtered; $journals = $filtered;
return view('transactions.mass.edit', compact('journals', 'subTitle', 'accountList')); return view('transactions.mass.edit', compact('journals', 'subTitle', 'accounts', 'budgets'));
} }
/** /**
@ -200,7 +205,7 @@ class MassController extends Controller
$sourceAccountName = $request->get('source_account_name')[$journal->id] ?? ''; $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] ?? ''; $destAccountName = $request->get('destination_account_name')[$journal->id] ?? '';
$budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0; $budgetId = $request->get('budget_id')[$journal->id] ?? 0;
$category = $request->get('category')[$journal->id]; $category = $request->get('category')[$journal->id];
$tags = $journal->tags->pluck('tag')->toArray(); $tags = $journal->tags->pluck('tag')->toArray();
@ -214,12 +219,12 @@ class MassController extends Controller
'destination_account_id' => intval($destAccountId), 'destination_account_id' => intval($destAccountId),
'destination_account_name' => $destAccountName, 'destination_account_name' => $destAccountName,
'amount' => round($request->get('amount')[$journal->id], 12), 'amount' => round($request->get('amount')[$journal->id], 12),
'currency_id' => intval($request->get('amount_currency_id_amount_' . $journal->id)), 'currency_id' => $journal->transaction_currency_id,
'date' => new Carbon($request->get('date')[$journal->id]), 'date' => new Carbon($request->get('date')[$journal->id]),
'interest_date' => $journal->interest_date, 'interest_date' => $journal->interest_date,
'book_date' => $journal->book_date, 'book_date' => $journal->book_date,
'process_date' => $journal->process_date, 'process_date' => $journal->process_date,
'budget_id' => $budgetId, 'budget_id' => intval($budgetId),
'category' => $category, 'category' => $category,
'tags' => $tags, 'tags' => $tags,

View File

@ -9,21 +9,24 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface; use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Navigation; use Navigation;
use Preferences; use Preferences;
use Response; use Response;
use Steam;
use View; use View;
/** /**
@ -59,109 +62,77 @@ class TransactionController extends Controller
* *
* @return View * @return View
*/ */
public function index(Request $request, JournalRepositoryInterface $repository, string $what) public function index(Request $request, JournalRepositoryInterface $repository, string $what, string $moment = '')
{ {
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // default values:
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what);
$subTitle = trans('firefly.title_' . $what); $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$count = 0;
$loop = 0;
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $start = null;
// to make sure we only grab a subset, based on the current date (in session): $end = null;
$start = session('start', Navigation::startOfPeriod(new Carbon, $range)); $periods = new Collection;
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
/** @var JournalCollectorInterface $collector */ // prep for "all" view.
$collector = app(JournalCollectorInterface::class); if ($moment === 'all') {
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->withBudgetInformation() $subTitle = trans('firefly.all_' . $what);
->withCategoryInformation();
$collector->withOpposingAccount();
$collector->disableInternalFilter();
$journals = $collector->getPaginatedJournals();
$journals->setPath('transactions/' . $what);
unset($start, $end);
// then also show a list of periods where the user can click on, based on the
// user's range and the oldest journal the user has:
$first = $repository->first(); $first = $repository->first();
$blockStart = is_null($first->id) ? new Carbon : $first->date; $start = $first->date ?? new Carbon;
$blockStart = Navigation::startOfPeriod($blockStart, $range); $end = new Carbon;
$blockEnd = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
while ($blockEnd >= $blockStart) {
Log::debug(sprintf('Now at blockEnd: %s', $blockEnd->format('Y-m-d')));
$blockEnd = Navigation::startOfPeriod($blockEnd, $range);
$dateStr = $blockEnd->format('Y-m-d');
$dateName = Navigation::periodShow($blockEnd, $range);
$entries->push([$dateStr, $dateName]);
$blockEnd = Navigation::subtractPeriod($blockEnd, $range, 1);
} }
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'entries')); // prep for "specific date" view.
if (strlen($moment) > 0 && $moment !== 'all') {
$start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range);
$subTitle = trans(
'firefly.title_' . $what . '_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$periods = $this->getPeriodOverview($what);
} }
/** // prep for current period
* @param Request $request if (strlen($moment) === 0) {
* @param string $what $start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
* $end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
* @return View $periods = $this->getPeriodOverview($what);
*/ $subTitle = trans(
public function indexAll(Request $request, string $what) 'firefly.title_' . $what . '_between',
{ ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); );
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); }
$types = config('firefly.transactionTypesByWhat.' . $what); // grab journals, but be prepared to jump a period back to get the right ones:
$subTitle = sprintf('%s (%s)', trans('firefly.title_' . $what), strtolower(trans('firefly.everything'))); Log::info('Now at transaction loop start.');
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); while ($count === 0 && $loop < 3) {
$loop++;
Log::info('Count is zero, search for journals.');
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class); $collector = app(JournalCollectorInterface::class);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->withBudgetInformation()->withCategoryInformation(); $collector->setAllAssetAccounts()->setRange($start, $end)->setTypes($types)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
$collector->withOpposingAccount(); ->disableInternalFilter();
$collector->disableInternalFilter();
$journals = $collector->getPaginatedJournals(); $journals = $collector->getPaginatedJournals();
$journals->setPath('transactions/' . $what . '/all'); $journals->setPath('/budgets/list/no-budget');
$count = $journals->getCollection()->count();
return view('transactions.index-all', compact('subTitle', 'what', 'subTitleIcon', 'journals')); if ($count === 0) {
$start->subDay();
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfPeriod($start, $range);
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
}
} }
/** if ($moment != 'all' && $loop > 1) {
* @param Request $request $subTitle = trans(
* @param string $what 'firefly.title_' . $what . '_between',
* ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
* @param string $date );
* }
* @return View
*/
public function indexByDate(Request $request, string $what, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what);
$subTitle = trans('firefly.title_' . $what) . ' (' . Navigation::periodShow($carbon, $range) . ')';
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
Log::debug(sprintf('Transaction index by date will show between %s and %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'periods', 'start', 'end', 'moment'));
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
$collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation();
$collector->withOpposingAccount();
$collector->disableInternalFilter();
$journals = $collector->getPaginatedJournals();
$journals->setPath('transactions/' . $what . '/' . $date);
return view('transactions.index-date', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'carbon'));
} }
@ -180,10 +151,9 @@ class TransactionController extends Controller
$ids = array_unique($ids); $ids = array_unique($ids);
foreach ($ids as $id) { foreach ($ids as $id) {
$journal = $repository->find(intval($id)); $journal = $repository->find(intval($id));
if ($journal && $journal->date->format('Y-m-d') == $date->format('Y-m-d')) { if ($journal && $journal->date->isSameDay($date)) {
$journal->order = $order; $repository->setOrder($journal, $order);
$order++; $order++;
$journal->save();
} }
} }
} }
@ -215,4 +185,79 @@ class TransactionController extends Controller
} }
/**
* @param string $what
*
* @return Collection
* @throws FireflyException
*/
private function getPeriodOverview(string $what): Collection
{
$repository = app(JournalRepositoryInterface::class);
$first = $repository->first();
$start = $first->date ?? new Carbon;
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
$types = config('firefly.transactionTypesByWhat.' . $what);
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($what);
$cache->addProperty('transaction-list-entries');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
Log::debug(sprintf('Going to get period expenses and incomes between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d')));
while ($end >= $start) {
Log::debug('Loop start!');
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
// count journals without budget in this period:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withOpposingAccount()->setTypes($types)->disableInternalFilter();
$set = $collector->getJournals();
$sum = $set->sum('transaction_amount');
$journals = $set->count();
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$array = [
'string' => $dateStr,
'name' => $dateName,
'count' => $journals,
'spent' => 0,
'earned' => 0,
'transferred' => 0,
'date' => clone $end,
];
Log::debug(sprintf('What is %s', $what));
switch ($what) {
case 'withdrawal':
$array['spent'] = $sum;
break;
case 'deposit':
$array['earned'] = $sum;
break;
case 'transfers':
case 'transfer':
$array['transferred'] = Steam::positive($sum);
break;
}
$entries->push($array);
$end = Navigation::subtractPeriod($end, $range, 1);
}
Log::debug('End of loop');
$cache->store($entries);
return $entries;
}
} }

View File

@ -0,0 +1,61 @@
<?php
/**
* IsLimitedUser.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\Middleware;
use Closure;
use FireflyIII\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Session;
/**
* Class IsAdmin
*
* @package FireflyIII\Http\Middleware
*/
class IsLimitedUser
{
/**
* Handle an incoming request. May not be a limited user (ie. Sandstorm env. or demo user).
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
*
* @return mixed
*/
public function handle(Request $request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('login');
}
/** @var User $user */
$user = auth()->user();
if ($user->hasRole('demo')) {
Session::flash('warning', strval(trans('firefly.not_available_demo_user')));
return redirect(route('index'));
}
if (intval(getenv('SANDSTORM')) === 1) {
Session::flash('warning', strval(trans('firefly.sandstorm_not_available')));
return redirect(route('index'));
}
return $next($request);
}
}

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
@ -37,7 +37,7 @@ class UserFormRequest extends Request
{ {
return [ return [
'email' => $this->string('email'), 'email' => $this->string('email'),
'blocked' => $this->integer('blocked'), 'blocked' => $this->integer('blocked') === 1,
'blocked_code' => $this->string('blocked_code'), 'blocked_code' => $this->string('blocked_code'),
'password' => $this->string('password'), 'password' => $this->string('password'),
]; ];
@ -50,7 +50,7 @@ class UserFormRequest extends Request
{ {
return [ return [
'id' => 'required|exists:users,id', 'id' => 'required|exists:users,id',
'email' => 'required', 'email' => 'email|required',
'password' => 'confirmed', 'password' => 'confirmed',
'blocked_code' => 'between:0,30', 'blocked_code' => 'between:0,30',
'blocked' => 'between:0,1|numeric', 'blocked' => 'between:0,1|numeric',

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Carbon\Carbon; use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator as BreadCrumbGenerator; use DaveJamesMiller\Breadcrumbs\Generator as BreadCrumbGenerator;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
@ -67,38 +67,31 @@ Breadcrumbs::register(
); );
Breadcrumbs::register( Breadcrumbs::register(
'accounts.show', function (BreadCrumbGenerator $breadcrumbs, Account $account) { 'accounts.show', function (BreadCrumbGenerator $breadcrumbs, Account $account, string $moment, Carbon $start, Carbon $end) {
$what = config('firefly.shortNamesByFullName.' . $account->accountType->type); $what = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$breadcrumbs->parent('accounts.index', $what); $breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push($account->name, route('accounts.show', [$account->id])); $breadcrumbs->push($account->name, route('accounts.show', [$account->id]));
}
);
Breadcrumbs::register( // push when is all:
'accounts.show.date', function (BreadCrumbGenerator $breadcrumbs, Account $account, Carbon $start = null, Carbon $end = null) { if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('accounts.show', [$account->id, 'all']));
$title = '';
$route = '';
if (!is_null($start) && !is_null($end)) {
$startString = $start->formatLocalized(strval(trans('config.month_and_day')));
$endString = $end->formatLocalized(strval(trans('config.month_and_day')));
$title = sprintf('%s (%s)', $account->name, trans('firefly.from_to_breadcrumb', ['start' => $startString, 'end' => $endString]));
$route = route('accounts.show.date', [$account->id, $start->format('Y-m-d')]);
} }
if (is_null($start) && is_null($end)) { // when is specific period:
$title = $title = $account->name . ' (' . strtolower(strval(trans('firefly.everything'))) . ')'; if (strlen($moment) > 0 && $moment !== 'all') {
$route = route('accounts.show.date', [$account->id, 'all']); $title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
);
$breadcrumbs->push($title, route('accounts.show', [$account->id, $moment]));
} }
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push($title, $route);
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'accounts.delete', function (BreadCrumbGenerator $breadcrumbs, Account $account) { 'accounts.delete', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account); $breadcrumbs->parent('accounts.show', $account, '', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.delete_account', ['name' => e($account->name)]), route('accounts.delete', [$account->id])); $breadcrumbs->push(trans('firefly.delete_account', ['name' => e($account->name)]), route('accounts.delete', [$account->id]));
} }
); );
@ -106,7 +99,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'accounts.edit', function (BreadCrumbGenerator $breadcrumbs, Account $account) { 'accounts.edit', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account); $breadcrumbs->parent('accounts.show', $account, '', new Carbon, new Carbon);
$what = config('firefly.shortNamesByFullName.' . $account->accountType->type); $what = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$breadcrumbs->push(trans('firefly.edit_' . $what . '_account', ['name' => e($account->name)]), route('accounts.edit', [$account->id])); $breadcrumbs->push(trans('firefly.edit_' . $what . '_account', ['name' => e($account->name)]), route('accounts.edit', [$account->id]));
@ -256,9 +249,24 @@ Breadcrumbs::register(
); );
Breadcrumbs::register( Breadcrumbs::register(
'budgets.no-budget', function (BreadCrumbGenerator $breadcrumbs, $subTitle) { 'budgets.no-budget', function (BreadCrumbGenerator $breadcrumbs, string $moment, Carbon $start, Carbon $end) {
$breadcrumbs->parent('budgets.index'); $breadcrumbs->parent('budgets.index');
$breadcrumbs->push($subTitle, route('budgets.no-budget')); $breadcrumbs->push(trans('firefly.journals_without_budget'), route('budgets.no-budget'));
// push when is all:
if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('budgets.no-budget', ['all']));
}
// when is specific period:
if (strlen($moment) > 0 && $moment !== 'all') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
);
$breadcrumbs->push($title, route('budgets.no-budget', [$moment]));
}
} }
); );
@ -275,11 +283,8 @@ Breadcrumbs::register(
$breadcrumbs->push(e($budget->name), route('budgets.show', [$budget->id])); $breadcrumbs->push(e($budget->name), route('budgets.show', [$budget->id]));
$title = trans( $title = trans(
'firefly.budget_in_period_breadcrumb', [ 'firefly.between_dates_breadcrumb', ['start' => $budgetLimit->start_date->formatLocalized(strval(trans('config.month_and_day'))),
'name' => $budget->name, 'end' => $budgetLimit->end_date->formatLocalized(strval(trans('config.month_and_day'))),]
'start' => $budgetLimit->start_date->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $budgetLimit->end_date->formatLocalized(strval(trans('config.month_and_day'))),
]
); );
$breadcrumbs->push( $breadcrumbs->push(
@ -306,52 +311,61 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'categories.edit', function (BreadCrumbGenerator $breadcrumbs, Category $category) { 'categories.edit', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category); $breadcrumbs->parent('categories.show', $category, '', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.edit_category', ['name' => e($category->name)]), route('categories.edit', [$category->id])); $breadcrumbs->push(trans('firefly.edit_category', ['name' => e($category->name)]), route('categories.edit', [$category->id]));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.delete', function (BreadCrumbGenerator $breadcrumbs, Category $category) { 'categories.delete', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category); $breadcrumbs->parent('categories.show', $category, '', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.delete_category', ['name' => e($category->name)]), route('categories.delete', [$category->id])); $breadcrumbs->push(trans('firefly.delete_category', ['name' => e($category->name)]), route('categories.delete', [$category->id]));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.show', function (BreadCrumbGenerator $breadcrumbs, Category $category) { 'categories.show', function (BreadCrumbGenerator $breadcrumbs, Category $category, string $moment, Carbon $start, Carbon $end) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name), route('categories.show', [$category->id]));
$breadcrumbs->parent('categories.index');
$breadcrumbs->push($category->name, route('categories.show', [$category->id]));
// push when is all:
if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('categories.show', [$category->id, 'all']));
}
// when is specific period:
if (strlen($moment) > 0 && $moment !== 'all') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
);
$breadcrumbs->push($title, route('categories.show', [$category->id, $moment]));
}
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.show.all', function (BreadCrumbGenerator $breadcrumbs, Category $category) { 'categories.no-category', function (BreadCrumbGenerator $breadcrumbs, string $moment, Carbon $start, Carbon $end) {
$breadcrumbs->parent('categories.index'); $breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name) . '(' . strtolower(trans('firefly.all_periods')) . ')', route('categories.show.all', [$category->id])); $breadcrumbs->push(trans('firefly.journals_without_category'), route('categories.no-category'));
// push when is all:
if ($moment === 'all') {
$breadcrumbs->push(trans('firefly.everything'), route('categories.no-category', ['all']));
}
// when is specific period:
if (strlen($moment) > 0 && $moment !== 'all') {
$title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
);
$breadcrumbs->push($title, route('categories.no-category', [$moment]));
}
} }
); );
Breadcrumbs::register(
'categories.show.date', function (BreadCrumbGenerator $breadcrumbs, Category $category, Carbon $date) {
// get current period preference.
$range = Preferences::get('viewRange', '1M')->data;
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name), route('categories.show', [$category->id]));
$breadcrumbs->push(Navigation::periodShow($date, $range), route('categories.show.date', [$category->id, $date->format('Y-m-d')]));
}
);
Breadcrumbs::register(
'categories.no-category', function (BreadCrumbGenerator $breadcrumbs, $subTitle) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push($subTitle, route('categories.no-category'));
}
);
/** /**
* CURRENCIES * CURRENCIES
@ -719,36 +733,30 @@ Breadcrumbs::register(
* TRANSACTIONS * TRANSACTIONS
*/ */
Breadcrumbs::register( Breadcrumbs::register(
'transactions.index', function (BreadCrumbGenerator $breadcrumbs, string $what) { 'transactions.index', function (BreadCrumbGenerator $breadcrumbs, string $what, string $moment = '', Carbon $start, Carbon $end) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', [$what])); $breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', [$what]));
} if ($moment === 'all') {
); $breadcrumbs->push(trans('firefly.everything'), route('transactions.index', [$what, 'all']));
}
Breadcrumbs::register( // when is specific period:
'transactions.index.all', function (BreadCrumbGenerator $breadcrumbs, string $what) { if (strlen($moment) > 0 && $moment !== 'all') {
$breadcrumbs->parent('transactions.index', $what); $title = trans(
'firefly.between_dates_breadcrumb', ['start' => $start->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $end->formatLocalized(strval(trans('config.month_and_day')))]
);
$breadcrumbs->push($title, route('transactions.index', [$what, $moment]));
}
$title = sprintf('%s (%s)', trans('breadcrumbs.' . $what . '_list'), strtolower(trans('firefly.everything')));
$breadcrumbs->push($title, route('transactions.index.all', [$what]));
}
);
Breadcrumbs::register(
'transactions.index.date', function (BreadCrumbGenerator $breadcrumbs, string $what, Carbon $date) {
$breadcrumbs->parent('transactions.index', $what);
$range = Preferences::get('viewRange', '1M')->data;
$title = trans('breadcrumbs.' . $what . '_list') . ' (' . Navigation::periodShow($date, $range) . ')';
$breadcrumbs->push($title, route('transactions.index.date', [$what, $date->format('Y-m-d')]));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'transactions.create', function (BreadCrumbGenerator $breadcrumbs, string $what) { 'transactions.create', function (BreadCrumbGenerator $breadcrumbs, string $what) {
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('transactions.create', [$what])); $breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('transactions.create', [$what]));
} }
); );
@ -770,7 +778,7 @@ Breadcrumbs::register(
'transactions.show', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) { 'transactions.show', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
$what = strtolower($journal->transactionType->type); $what = strtolower($journal->transactionType->type);
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->push($journal->description, route('transactions.show', [$journal->id])); $breadcrumbs->push($journal->description, route('transactions.show', [$journal->id]));
} }
); );
@ -792,11 +800,12 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'transactions.mass.edit', function (BreadCrumbGenerator $breadcrumbs, Collection $journals) { 'transactions.mass.edit', function (BreadCrumbGenerator $breadcrumbs, Collection $journals) {
if($journals->count() > 0) { if ($journals->count() > 0) {
$journalIds = $journals->pluck('id')->toArray(); $journalIds = $journals->pluck('id')->toArray();
$what = strtolower($journals->first()->transactionType->type); $what = strtolower($journals->first()->transactionType->type);
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.edit', $journalIds)); $breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.edit', $journalIds));
return; return;
} }
@ -809,7 +818,7 @@ Breadcrumbs::register(
$journalIds = $journals->pluck('id')->toArray(); $journalIds = $journals->pluck('id')->toArray();
$what = strtolower($journals->first()->transactionType->type); $what = strtolower($journals->first()->transactionType->type);
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->parent('transactions.index', $what, '', new Carbon, new Carbon);
$breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.delete', $journalIds)); $breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.delete', $journalIds));
} }
); );

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Import; namespace FireflyIII\Import;

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Models; namespace FireflyIII\Models;
@ -48,6 +48,7 @@ class AccountType extends Model
protected $dates = ['created_at', 'updated_at']; protected $dates = ['created_at', 'updated_at'];
// //
/** /**
* @return HasMany * @return HasMany
*/ */

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Models; namespace FireflyIII\Models;

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Models; namespace FireflyIII\Models;

View File

@ -9,12 +9,11 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Models; namespace FireflyIII\Models;
use Crypt; use Crypt;
use FireflyIII\Support\Models\TagTrait;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -27,7 +26,7 @@ use Watson\Validating\ValidatingTrait;
*/ */
class Tag extends Model class Tag extends Model
{ {
use ValidatingTrait, SoftDeletes, TagTrait; use ValidatingTrait, SoftDeletes;
/** /**
* The attributes that should be casted to native types. * The attributes that should be casted to native types.

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Providers; namespace FireflyIII\Providers;
@ -29,6 +29,8 @@ use FireflyIII\Helpers\Report\BalanceReportHelper;
use FireflyIII\Helpers\Report\BalanceReportHelperInterface; use FireflyIII\Helpers\Report\BalanceReportHelperInterface;
use FireflyIII\Helpers\Report\BudgetReportHelper; use FireflyIII\Helpers\Report\BudgetReportHelper;
use FireflyIII\Helpers\Report\BudgetReportHelperInterface; use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Helpers\Report\PopupReport;
use FireflyIII\Helpers\Report\PopupReportInterface;
use FireflyIII\Helpers\Report\ReportHelper; use FireflyIII\Helpers\Report\ReportHelper;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Import\ImportProcedure; use FireflyIII\Import\ImportProcedure;
@ -127,6 +129,8 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind(UserRepositoryInterface::class, UserRepository::class); $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
$this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class); $this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class);
// more generators:
$this->app->bind(PopupReportInterface::class, PopupReport::class);
$this->app->bind(HelpInterface::class, Help::class); $this->app->bind(HelpInterface::class, Help::class);
$this->app->bind(ReportHelperInterface::class, ReportHelper::class); $this->app->bind(ReportHelperInterface::class, ReportHelper::class);
$this->app->bind(FiscalHelperInterface::class, FiscalHelper::class); $this->app->bind(FiscalHelperInterface::class, FiscalHelper::class);

View File

@ -113,7 +113,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
*/ */
public function findByCode(string $currencyCode): TransactionCurrency public function findByCode(string $currencyCode): TransactionCurrency
{ {
$currency = TransactionCurrency::whereCode($currencyCode)->first(); $currency = TransactionCurrency::where('code', $currencyCode)->first();
if (is_null($currency)) { if (is_null($currency)) {
$currency = new TransactionCurrency; $currency = new TransactionCurrency;
} }
@ -170,7 +170,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
*/ */
public function getCurrencyByPreference(Preference $preference): TransactionCurrency public function getCurrencyByPreference(Preference $preference): TransactionCurrency
{ {
$preferred = TransactionCurrency::whereCode($preference->data)->first(); $preferred = TransactionCurrency::where('code', $preference->data)->first();
if (is_null($preferred)) { if (is_null($preferred)) {
$preferred = TransactionCurrency::first(); $preferred = TransactionCurrency::first();
} }

View File

@ -86,6 +86,20 @@ class ImportJobRepository implements ImportJobRepositoryInterface
return $result; return $result;
} }
/**
* @param ImportJob $job
* @param array $configuration
*
* @return ImportJob
*/
public function setConfiguration(ImportJob $job, array $configuration): ImportJob
{
$job->configuration = $configuration;
$job->save();
return $job;
}
/** /**
* @param User $user * @param User $user
*/ */
@ -93,4 +107,18 @@ class ImportJobRepository implements ImportJobRepositoryInterface
{ {
$this->user = $user; $this->user = $user;
} }
/**
* @param ImportJob $job
* @param string $status
*
* @return ImportJob
*/
public function updateStatus(ImportJob $job, string $status): ImportJob
{
$job->status = $status;
$job->save();
return $job;
}
} }

View File

@ -37,8 +37,24 @@ interface ImportJobRepositoryInterface
*/ */
public function findByKey(string $key): ImportJob; public function findByKey(string $key): ImportJob;
/**
* @param ImportJob $job
* @param array $configuration
*
* @return ImportJob
*/
public function setConfiguration(ImportJob $job, array $configuration): ImportJob;
/** /**
* @param User $user * @param User $user
*/ */
public function setUser(User $user); public function setUser(User $user);
/**
* @param ImportJob $job
* @param string $status
*
* @return ImportJob
*/
public function updateStatus(ImportJob $job, string $status): ImportJob;
} }

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Repositories\Journal; namespace FireflyIII\Repositories\Journal;
@ -135,6 +135,20 @@ class JournalRepository implements JournalRepositoryInterface
return TransactionType::orderBy('type', 'ASC')->get(); return TransactionType::orderBy('type', 'ASC')->get();
} }
/**
* @param TransactionJournal $journal
* @param int $order
*
* @return bool
*/
public function setOrder(TransactionJournal $journal, int $order): bool
{
$journal->order = $order;
$journal->save();
return true;
}
/** /**
* @param User $user * @param User $user
*/ */

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Repositories\Journal; namespace FireflyIII\Repositories\Journal;
@ -67,6 +67,14 @@ interface JournalRepositoryInterface
*/ */
public function getTransactionTypes(): Collection; public function getTransactionTypes(): Collection;
/**
* @param TransactionJournal $journal
* @param int $order
*
* @return bool
*/
public function setOrder(TransactionJournal $journal, int $order): bool;
/** /**
* @param User $user * @param User $user
*/ */

View File

@ -32,6 +32,54 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
/** @var User */ /** @var User */
private $user; private $user;
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function addAmount(PiggyBank $piggyBank, string $amount): bool
{
$repetition = $piggyBank->currentRelevantRep();
$currentAmount = $repetition->currentamount ?? '0';
$repetition->currentamount = bcadd($currentAmount, $amount);
$repetition->save();
// create event
$this->createEvent($piggyBank, $amount);
return true;
}
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function canAddAmount(PiggyBank $piggyBank, string $amount): bool
{
$leftOnAccount = $piggyBank->leftOnAccount(new Carbon);
$savedSoFar = strval($piggyBank->currentRelevantRep()->currentamount);
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = strval(min(round($leftOnAccount, 12), round($leftToSave, 12)));
return bccomp($amount, $maxAmount) <= 0;
}
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool
{
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
return bccomp($amount, $savedSoFar) <= 0;
}
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* @param string $amount * @param string $amount
@ -119,6 +167,24 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $set; return $set;
} }
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function removeAmount(PiggyBank $piggyBank, string $amount): bool
{
$repetition = $piggyBank->currentRelevantRep();
$repetition->currentamount = bcsub($repetition->currentamount, $amount);
$repetition->save();
// create event
$this->createEvent($piggyBank, bcmul($amount, '-1'));
return true;
}
/** /**
* Set all piggy banks to order 0. * Set all piggy banks to order 0.
* *

View File

@ -26,6 +26,30 @@ use Illuminate\Support\Collection;
interface PiggyBankRepositoryInterface interface PiggyBankRepositoryInterface
{ {
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function addAmount(PiggyBank $piggyBank, string $amount): bool;
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function canAddAmount(PiggyBank $piggyBank, string $amount): bool;
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool;
/** /**
* Create a new event. * Create a new event.
* *
@ -82,6 +106,14 @@ interface PiggyBankRepositoryInterface
*/ */
public function getPiggyBanksWithAmount(): Collection; public function getPiggyBanksWithAmount(): Collection;
/**
* @param PiggyBank $piggyBank
* @param string $amount
*
* @return bool
*/
public function removeAmount(PiggyBank $piggyBank, string $amount): bool;
/** /**
* Set all piggy banks to order 0. * Set all piggy banks to order 0.
* *

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Repositories\Tag; namespace FireflyIII\Repositories\Tag;
@ -246,6 +246,73 @@ class TagRepository implements TagRepositoryInterface
} }
/**
* Can a tag become an advance payment?
*
* @param Tag $tag
*
* @return bool
*/
public function tagAllowAdvance(Tag $tag): bool
{
/*
* If this tag is a balancing act, and it contains transfers, it cannot be
* changed to an advancePayment.
*/
if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') {
foreach ($tag->transactionjournals as $journal) {
if ($journal->isTransfer()) {
return false;
}
}
}
/*
* If this tag contains more than one expenses, it cannot become an advance payment.
*/
$count = 0;
foreach ($tag->transactionjournals as $journal) {
if ($journal->isWithdrawal()) {
$count++;
}
}
if ($count > 1) {
return false;
}
return true;
}
/**
* Can a tag become a balancing act?
*
* @param Tag $tag
*
* @return bool
*/
public function tagAllowBalancing(Tag $tag): bool
{
/*
* If has more than two transactions already, cannot become a balancing act:
*/
if ($tag->transactionjournals->count() > 2) {
return false;
}
/*
* If any transaction is a deposit, cannot become a balancing act.
*/
foreach ($tag->transactionjournals as $journal) {
if ($journal->isDeposit()) {
return false;
}
}
return true;
}
/** /**
* @param Tag $tag * @param Tag $tag
* @param array $data * @param array $data

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Repositories\Tag; namespace FireflyIII\Repositories\Tag;
@ -27,6 +27,7 @@ use Illuminate\Support\Collection;
*/ */
interface TagRepositoryInterface interface TagRepositoryInterface
{ {
/** /**
* This method will connect a journal with a tag. * This method will connect a journal with a tag.
* *
@ -125,6 +126,20 @@ interface TagRepositoryInterface
*/ */
public function store(array $data): Tag; public function store(array $data): Tag;
/**
* @param Tag $tag
*
* @return bool
*/
public function tagAllowAdvance(Tag $tag): bool;
/**
* @param Tag $tag
*
* @return bool
*/
public function tagAllowBalancing(Tag $tag): bool;
/** /**
* Update a tag. * Update a tag.
* *

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Repositories\User; namespace FireflyIII\Repositories\User;
@ -66,6 +66,23 @@ class UserRepository implements UserRepositoryInterface
return true; return true;
} }
/**
* @param User $user
* @param bool $isBlocked
* @param string $code
*
* @return bool
*/
public function changeStatus(User $user, bool $isBlocked, string $code): bool
{
// change blocked status and code:
$user->blocked = $isBlocked;
$user->blocked_code = $code;
$user->save();
return true;
}
/** /**
* @return int * @return int
*/ */
@ -147,4 +164,15 @@ class UserRepository implements UserRepositoryInterface
return $return; return $return;
} }
/**
* @param User $user
* @param string $role
*
* @return bool
*/
public function hasRole(User $user, string $role): bool
{
return $user->hasRole($role);
}
} }

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Repositories\User; namespace FireflyIII\Repositories\User;
@ -50,6 +50,15 @@ interface UserRepositoryInterface
*/ */
public function changePassword(User $user, string $password); public function changePassword(User $user, string $password);
/**
* @param User $user
* @param bool $isBlocked
* @param string $code
*
* @return bool
*/
public function changeStatus(User $user, bool $isBlocked, string $code): bool;
/** /**
* Returns a count of all users. * Returns a count of all users.
* *
@ -79,4 +88,12 @@ interface UserRepositoryInterface
* @return array * @return array
*/ */
public function getUserData(User $user): array; public function getUserData(User $user): array;
/**
* @param User $user
* @param string $role
*
* @return bool
*/
public function hasRole(User $user, string $role): bool;
} }

View File

@ -13,11 +13,11 @@ declare(strict_types = 1);
namespace FireflyIII\Support; namespace FireflyIII\Support;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Preferences as Prefs; use Preferences as Prefs;
/** /**
@ -97,7 +97,6 @@ class Amount
// alternative is currency before amount // alternative is currency before amount
$format = $pos_a . $pos_b . '%s' . $pos_c . $space . $pos_d . '%v' . $pos_e; $format = $pos_a . $pos_b . '%s' . $pos_c . $space . $pos_d . '%v' . $pos_e;
} }
Log::debug(sprintf('Final format: "%s"', $format));
return $format; return $format;
} }
@ -130,7 +129,7 @@ class Amount
setlocale(LC_MONETARY, $locale); setlocale(LC_MONETARY, $locale);
$float = round($amount, 12); $float = round($amount, 12);
$info = localeconv(); $info = localeconv();
$formatted = number_format($float, $format->decimal_places, $info['mon_decimal_point'], $info['mon_thousands_sep']); $formatted = number_format($float, intval($format->decimal_places), $info['mon_decimal_point'], $info['mon_thousands_sep']);
// some complicated switches to format the amount correctly: // some complicated switches to format the amount correctly:
$precedes = $amount < 0 ? $info['n_cs_precedes'] : $info['p_cs_precedes']; $precedes = $amount < 0 ? $info['n_cs_precedes'] : $info['p_cs_precedes'];
@ -171,7 +170,7 @@ class Amount
*/ */
public function formatByCode(string $currencyCode, string $amount, bool $coloured = true): string public function formatByCode(string $currencyCode, string $amount, bool $coloured = true): string
{ {
$currency = TransactionCurrency::whereCode($currencyCode)->first(); $currency = TransactionCurrency::where('code', $currencyCode)->first();
return $this->formatAnything($currency, $amount, $coloured); return $this->formatAnything($currency, $amount, $coloured);
} }
@ -224,7 +223,7 @@ class Amount
} else { } else {
$currencyPreference = Prefs::get('currencyPreference', config('firefly.default_currency', 'EUR')); $currencyPreference = Prefs::get('currencyPreference', config('firefly.default_currency', 'EUR'));
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); $currency = TransactionCurrency::where('code', $currencyPreference->data)->first();
if ($currency) { if ($currency) {
$cache->store($currency->code); $cache->store($currency->code);
@ -248,7 +247,7 @@ class Amount
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} else { } else {
$currencyPreference = Prefs::get('currencyPreference', config('firefly.default_currency', 'EUR')); $currencyPreference = Prefs::get('currencyPreference', config('firefly.default_currency', 'EUR'));
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); $currency = TransactionCurrency::where('code', $currencyPreference->data)->first();
$cache->store($currency->symbol); $cache->store($currency->symbol);
@ -257,7 +256,8 @@ class Amount
} }
/** /**
* @return \FireflyIII\Models\TransactionCurrency * @return TransactionCurrency
* @throws FireflyException
*/ */
public function getDefaultCurrency(): TransactionCurrency public function getDefaultCurrency(): TransactionCurrency
{ {
@ -267,7 +267,10 @@ class Amount
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
$currencyPreference = Prefs::get('currencyPreference', config('firefly.default_currency', 'EUR')); $currencyPreference = Prefs::get('currencyPreference', config('firefly.default_currency', 'EUR'));
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); $currency = TransactionCurrency::where('code', $currencyPreference->data)->first();
if (is_null($currency)) {
throw new FireflyException(sprintf('No currency found with code "%s"', $currencyPreference->data));
}
$cache->store($currency); $cache->store($currency);
return $currency; return $currency;

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Support\Binder; namespace FireflyIII\Support\Binder;
@ -39,11 +39,13 @@ class JournalList implements BinderInterface
$object = TransactionJournal::whereIn('transaction_journals.id', $ids) $object = TransactionJournal::whereIn('transaction_journals.id', $ids)
->expanded() ->expanded()
->where('transaction_journals.user_id', auth()->user()->id) ->where('transaction_journals.user_id', auth()->user()->id)
->get([ ->get(
[
'transaction_journals.*', 'transaction_journals.*',
'transaction_types.type AS transaction_type_type', 'transaction_types.type AS transaction_type_type',
'transaction_currencies.code AS transaction_currency_code', 'transaction_currencies.code AS transaction_currency_code',
]); ]
);
if ($object->count() > 0) { if ($object->count() > 0) {
return $object; return $object;

View File

@ -1,90 +0,0 @@
<?php
/**
* TagTrait.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\Support\Models;
use Illuminate\Support\Collection;
/**
*
* @property Collection $transactionjournals
* @property string $tagMode
*
* Class TagSupport
*
* @package FireflyIII\Support\Models
*/
trait TagTrait
{
/**
* Can a tag become an advance payment?
*
* @return bool
*/
public function tagAllowAdvance(): bool
{
/*
* If this tag is a balancing act, and it contains transfers, it cannot be
* changes to an advancePayment.
*/
if ($this->tagMode == 'balancingAct' || $this->tagMode == 'nothing') {
foreach ($this->transactionjournals as $journal) {
if ($journal->isTransfer()) {
return false;
}
}
}
/*
* If this tag contains more than one expenses, it cannot become an advance payment.
*/
$count = 0;
foreach ($this->transactionjournals as $journal) {
if ($journal->isWithdrawal()) {
$count++;
}
}
if ($count > 1) {
return false;
}
return true;
}
/**
* Can a tag become a balancing act?
*
* @return bool
*/
public function tagAllowBalancing(): bool
{
/*
* If has more than two transactions already, cannot become a balancing act:
*/
if ($this->transactionjournals->count() > 2) {
return false;
}
/*
* If any transaction is a deposit, cannot become a balancing act.
*/
foreach ($this->transactionjournals as $journal) {
if ($journal->isDeposit()) {
return false;
}
}
return true;
}
}

View File

@ -7,7 +7,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Support\Models; namespace FireflyIII\Support\Models;
@ -15,10 +15,8 @@ namespace FireflyIII\Support\Models;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**

View File

@ -72,7 +72,7 @@ class Navigation
* @return \Carbon\Carbon * @return \Carbon\Carbon
* @throws FireflyException * @throws FireflyException
*/ */
public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon public function endOfPeriod(\Carbon\Carbon $end, string $repeatFreq): Carbon
{ {
$currentEnd = clone $end; $currentEnd = clone $end;

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Support\Search; namespace FireflyIII\Support\Search;
@ -37,6 +37,8 @@ class Search implements SearchInterface
private $limit = 100; private $limit = 100;
/** @var Collection */ /** @var Collection */
private $modifiers; private $modifiers;
/** @var string */
private $originalQuery = '';
/** @var User */ /** @var User */
private $user; private $user;
/** @var array */ /** @var array */
@ -58,7 +60,11 @@ class Search implements SearchInterface
*/ */
public function getWordsAsString(): string public function getWordsAsString(): string
{ {
return join(' ', $this->words); $string = join(' ', $this->words);
if (strlen($string) === 0) {
return is_string($this->originalQuery) ? $this->originalQuery : '';
}
return $string;
} }
/** /**
@ -75,6 +81,7 @@ class Search implements SearchInterface
public function parseQuery(string $query) public function parseQuery(string $query)
{ {
$filteredQuery = $query; $filteredQuery = $query;
$this->originalQuery = $query;
$pattern = '/[a-z_]*:[0-9a-z-.]*/i'; $pattern = '/[a-z_]*:[0-9a-z-.]*/i';
$matches = []; $matches = [];
preg_match_all($pattern, $query, $matches); preg_match_all($pattern, $query, $matches);

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
namespace FireflyIII\Validation; namespace FireflyIII\Validation;
@ -29,7 +29,6 @@ use Google2FA;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Translation\Translator; use Illuminate\Contracts\Translation\Translator;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use Session;
/** /**
* Class FireflyValidator * Class FireflyValidator
@ -66,7 +65,7 @@ class FireflyValidator extends Validator
return false; return false;
} }
$secret = Session::get('two-factor-secret'); $secret = session('two-factor-secret');
return Google2FA::verifyKey($secret, $value); return Google2FA::verifyKey($secret, $value);
} }

View File

@ -72,7 +72,8 @@
"symfony/css-selector": "3.1.*", "symfony/css-selector": "3.1.*",
"symfony/dom-crawler": "3.1.*", "symfony/dom-crawler": "3.1.*",
"barryvdh/laravel-debugbar": "2.*", "barryvdh/laravel-debugbar": "2.*",
"barryvdh/laravel-ide-helper": "2.*" "barryvdh/laravel-ide-helper": "2.*",
"satooshi/php-coveralls": "^1.0"
}, },
"autoload": { "autoload": {
"classmap": [ "classmap": [

537
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "5286d8b4bfb450a5cecae36d2f67a862", "content-hash": "3570b1bf4768d7a7db0667317e82c668",
"packages": [ "packages": [
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@ -623,16 +623,16 @@
}, },
{ {
"name": "erusev/parsedown", "name": "erusev/parsedown",
"version": "1.6.1", "version": "1.6.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/erusev/parsedown.git", "url": "https://github.com/erusev/parsedown.git",
"reference": "20ff8bbb57205368b4b42d094642a3e52dac85fb" "reference": "1bf24f7334fe16c88bf9d467863309ceaf285b01"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/20ff8bbb57205368b4b42d094642a3e52dac85fb", "url": "https://api.github.com/repos/erusev/parsedown/zipball/1bf24f7334fe16c88bf9d467863309ceaf285b01",
"reference": "20ff8bbb57205368b4b42d094642a3e52dac85fb", "reference": "1bf24f7334fe16c88bf9d467863309ceaf285b01",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -661,20 +661,20 @@
"markdown", "markdown",
"parser" "parser"
], ],
"time": "2016-11-02T15:56:58+00:00" "time": "2017-03-29T16:04:15+00:00"
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v5.4.15", "version": "v5.4.17",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "ecc6468b8af30b77566a8519ce8898740ef691d7" "reference": "f7675d59e3863a58ecdff1a5ee1dcd0cff788f4b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/ecc6468b8af30b77566a8519ce8898740ef691d7", "url": "https://api.github.com/repos/laravel/framework/zipball/f7675d59e3863a58ecdff1a5ee1dcd0cff788f4b",
"reference": "ecc6468b8af30b77566a8519ce8898740ef691d7", "reference": "f7675d59e3863a58ecdff1a5ee1dcd0cff788f4b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -790,7 +790,7 @@
"framework", "framework",
"laravel" "laravel"
], ],
"time": "2017-03-02T14:41:40+00:00" "time": "2017-04-03T13:07:39+00:00"
}, },
{ {
"name": "laravelcollective/html", "name": "laravelcollective/html",
@ -974,16 +974,16 @@
}, },
{ {
"name": "league/flysystem", "name": "league/flysystem",
"version": "1.0.35", "version": "1.0.37",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/flysystem.git", "url": "https://github.com/thephpleague/flysystem.git",
"reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062" "reference": "78b5cc4feb61a882302df4fbaf63b7662e5e4ccd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/dda7f3ab94158a002d9846a97dc18ebfb7acc062", "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/78b5cc4feb61a882302df4fbaf63b7662e5e4ccd",
"reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062", "reference": "78b5cc4feb61a882302df4fbaf63b7662e5e4ccd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1053,20 +1053,20 @@
"sftp", "sftp",
"storage" "storage"
], ],
"time": "2017-02-09T11:33:58+00:00" "time": "2017-03-22T15:43:14+00:00"
}, },
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "1.22.0", "version": "1.22.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Seldaek/monolog.git", "url": "https://github.com/Seldaek/monolog.git",
"reference": "bad29cb8d18ab0315e6c477751418a82c850d558" "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/bad29cb8d18ab0315e6c477751418a82c850d558", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1e044bc4b34e91743943479f1be7a1d5eb93add0",
"reference": "bad29cb8d18ab0315e6c477751418a82c850d558", "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1131,7 +1131,7 @@
"logging", "logging",
"psr-3" "psr-3"
], ],
"time": "2016-11-26T00:15:39+00:00" "time": "2017-03-13T07:08:03+00:00"
}, },
{ {
"name": "mtdowling/cron-expression", "name": "mtdowling/cron-expression",
@ -1232,16 +1232,16 @@
}, },
{ {
"name": "paragonie/random_compat", "name": "paragonie/random_compat",
"version": "v2.0.9", "version": "v2.0.10",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/paragonie/random_compat.git", "url": "https://github.com/paragonie/random_compat.git",
"reference": "6968531206671f94377b01dc7888d5d1b858a01b" "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/6968531206671f94377b01dc7888d5d1b858a01b", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"reference": "6968531206671f94377b01dc7888d5d1b858a01b", "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1276,7 +1276,7 @@
"pseudorandom", "pseudorandom",
"random" "random"
], ],
"time": "2017-03-03T20:43:42+00:00" "time": "2017-03-13T16:27:32+00:00"
}, },
{ {
"name": "pragmarx/google2fa", "name": "pragmarx/google2fa",
@ -1388,30 +1388,30 @@
}, },
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",
"version": "3.5.2", "version": "3.6.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/uuid.git", "url": "https://github.com/ramsey/uuid.git",
"reference": "5677cfe02397dd6b58c861870dfaa5d9007d3954" "reference": "4ae32dd9ab8860a4bbd750ad269cba7f06f7934e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/5677cfe02397dd6b58c861870dfaa5d9007d3954", "url": "https://api.github.com/repos/ramsey/uuid/zipball/4ae32dd9ab8860a4bbd750ad269cba7f06f7934e",
"reference": "5677cfe02397dd6b58c861870dfaa5d9007d3954", "reference": "4ae32dd9ab8860a4bbd750ad269cba7f06f7934e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"paragonie/random_compat": "^1.0|^2.0", "paragonie/random_compat": "^1.0|^2.0",
"php": ">=5.4" "php": "^5.4 || ^7.0"
}, },
"replace": { "replace": {
"rhumsaa/uuid": "self.version" "rhumsaa/uuid": "self.version"
}, },
"require-dev": { "require-dev": {
"apigen/apigen": "^4.1", "apigen/apigen": "^4.1",
"codeception/aspect-mock": "1.0.0", "codeception/aspect-mock": "^1.0 | ^2.0",
"doctrine/annotations": "~1.2.0", "doctrine/annotations": "~1.2.0",
"goaop/framework": "1.0.0-alpha.2", "goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1",
"ircmaxell/random-lib": "^1.1", "ircmaxell/random-lib": "^1.1",
"jakub-onderka/php-parallel-lint": "^0.9.0", "jakub-onderka/php-parallel-lint": "^0.9.0",
"mockery/mockery": "^0.9.4", "mockery/mockery": "^0.9.4",
@ -1466,7 +1466,7 @@
"identifier", "identifier",
"uuid" "uuid"
], ],
"time": "2016-11-22T19:21:44+00:00" "time": "2017-03-26T20:37:53+00:00"
}, },
{ {
"name": "rcrowe/twigbridge", "name": "rcrowe/twigbridge",
@ -1637,16 +1637,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "0e5e6899f82230fcb1153bcaf0e106ffaa44b870" "reference": "c30243cc51f726812be3551316b109a2f5deaf8d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/0e5e6899f82230fcb1153bcaf0e106ffaa44b870", "url": "https://api.github.com/repos/symfony/console/zipball/c30243cc51f726812be3551316b109a2f5deaf8d",
"reference": "0e5e6899f82230fcb1153bcaf0e106ffaa44b870", "reference": "c30243cc51f726812be3551316b109a2f5deaf8d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1696,7 +1696,7 @@
], ],
"description": "Symfony Console Component", "description": "Symfony Console Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-02-16T14:07:22+00:00" "time": "2017-04-04T14:33:42+00:00"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
@ -1753,16 +1753,16 @@
}, },
{ {
"name": "symfony/debug", "name": "symfony/debug",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/debug.git", "url": "https://github.com/symfony/debug.git",
"reference": "9b98854cb45bc59d100b7d4cc4cf9e05f21026b9" "reference": "56f613406446a4a0a031475cfd0a01751de22659"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/9b98854cb45bc59d100b7d4cc4cf9e05f21026b9", "url": "https://api.github.com/repos/symfony/debug/zipball/56f613406446a4a0a031475cfd0a01751de22659",
"reference": "9b98854cb45bc59d100b7d4cc4cf9e05f21026b9", "reference": "56f613406446a4a0a031475cfd0a01751de22659",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1806,31 +1806,31 @@
], ],
"description": "Symfony Debug Component", "description": "Symfony Debug Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-02-16T16:34:18+00:00" "time": "2017-03-28T21:38:24+00:00"
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v3.2.4", "version": "v2.8.19",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/event-dispatcher.git", "url": "https://github.com/symfony/event-dispatcher.git",
"reference": "9137eb3a3328e413212826d63eeeb0217836e2b6" "reference": "88b65f0ac25355090e524aba4ceb066025df8bd2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9137eb3a3328e413212826d63eeeb0217836e2b6", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/88b65f0ac25355090e524aba4ceb066025df8bd2",
"reference": "9137eb3a3328e413212826d63eeeb0217836e2b6", "reference": "88b65f0ac25355090e524aba4ceb066025df8bd2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.5.9" "php": ">=5.3.9"
}, },
"require-dev": { "require-dev": {
"psr/log": "~1.0", "psr/log": "~1.0",
"symfony/config": "~2.8|~3.0", "symfony/config": "^2.0.5|~3.0.0",
"symfony/dependency-injection": "~2.8|~3.0", "symfony/dependency-injection": "~2.6|~3.0.0",
"symfony/expression-language": "~2.8|~3.0", "symfony/expression-language": "~2.6|~3.0.0",
"symfony/stopwatch": "~2.8|~3.0" "symfony/stopwatch": "~2.3|~3.0.0"
}, },
"suggest": { "suggest": {
"symfony/dependency-injection": "", "symfony/dependency-injection": "",
@ -1839,7 +1839,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.2-dev" "dev-master": "2.8-dev"
} }
}, },
"autoload": { "autoload": {
@ -1866,20 +1866,20 @@
], ],
"description": "Symfony EventDispatcher Component", "description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-01-02T20:32:22+00:00" "time": "2017-04-03T20:37:06+00:00"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/finder.git", "url": "https://github.com/symfony/finder.git",
"reference": "8c71141cae8e2957946b403cc71a67213c0380d6" "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/8c71141cae8e2957946b403cc71a67213c0380d6", "url": "https://api.github.com/repos/symfony/finder/zipball/b20900ce5ea164cd9314af52725b0bb5a758217a",
"reference": "8c71141cae8e2957946b403cc71a67213c0380d6", "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1915,20 +1915,20 @@
], ],
"description": "Symfony Finder Component", "description": "Symfony Finder Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-01-02T20:32:22+00:00" "time": "2017-03-20T09:32:19+00:00"
}, },
{ {
"name": "symfony/http-foundation", "name": "symfony/http-foundation",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/http-foundation.git", "url": "https://github.com/symfony/http-foundation.git",
"reference": "a90da6dd679605d88c9803a57a6fc1fb7a19a6e0" "reference": "cb0b6418f588952c9290b3df4ca650f1b7ab570a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/a90da6dd679605d88c9803a57a6fc1fb7a19a6e0", "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cb0b6418f588952c9290b3df4ca650f1b7ab570a",
"reference": "a90da6dd679605d88c9803a57a6fc1fb7a19a6e0", "reference": "cb0b6418f588952c9290b3df4ca650f1b7ab570a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1968,20 +1968,20 @@
], ],
"description": "Symfony HttpFoundation Component", "description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-02-16T22:46:52+00:00" "time": "2017-04-04T15:30:56+00:00"
}, },
{ {
"name": "symfony/http-kernel", "name": "symfony/http-kernel",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/http-kernel.git", "url": "https://github.com/symfony/http-kernel.git",
"reference": "4cd0d4bc31819095c6ef13573069f621eb321081" "reference": "8285ab5faf1306b1a5ebcf287fe91c231a6de88e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/4cd0d4bc31819095c6ef13573069f621eb321081", "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8285ab5faf1306b1a5ebcf287fe91c231a6de88e",
"reference": "4cd0d4bc31819095c6ef13573069f621eb321081", "reference": "8285ab5faf1306b1a5ebcf287fe91c231a6de88e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2050,7 +2050,7 @@
], ],
"description": "Symfony HttpKernel Component", "description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-02-16T23:59:56+00:00" "time": "2017-04-05T12:52:03+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
@ -2221,16 +2221,16 @@
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/process.git", "url": "https://github.com/symfony/process.git",
"reference": "0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856" "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856", "url": "https://api.github.com/repos/symfony/process/zipball/57fdaa55827ae14d617550ebe71a820f0a5e2282",
"reference": "0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856", "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2266,20 +2266,20 @@
], ],
"description": "Symfony Process Component", "description": "Symfony Process Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-02-16T14:07:22+00:00" "time": "2017-03-27T18:07:02+00:00"
}, },
{ {
"name": "symfony/routing", "name": "symfony/routing",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/routing.git", "url": "https://github.com/symfony/routing.git",
"reference": "af464432c177dbcdbb32295113b7627500331f2d" "reference": "d6605f9a5767bc5bc4895e1c762ba93964608aee"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/af464432c177dbcdbb32295113b7627500331f2d", "url": "https://api.github.com/repos/symfony/routing/zipball/d6605f9a5767bc5bc4895e1c762ba93964608aee",
"reference": "af464432c177dbcdbb32295113b7627500331f2d", "reference": "d6605f9a5767bc5bc4895e1c762ba93964608aee",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2341,20 +2341,20 @@
"uri", "uri",
"url" "url"
], ],
"time": "2017-01-28T02:37:08+00:00" "time": "2017-03-02T15:58:09+00:00"
}, },
{ {
"name": "symfony/translation", "name": "symfony/translation",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/translation.git", "url": "https://github.com/symfony/translation.git",
"reference": "d6825c6bb2f1da13f564678f9f236fe8242c0029" "reference": "c740eee70783d2af4d3d6b70d5146f209e6b4d13"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/d6825c6bb2f1da13f564678f9f236fe8242c0029", "url": "https://api.github.com/repos/symfony/translation/zipball/c740eee70783d2af4d3d6b70d5146f209e6b4d13",
"reference": "d6825c6bb2f1da13f564678f9f236fe8242c0029", "reference": "c740eee70783d2af4d3d6b70d5146f209e6b4d13",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2367,7 +2367,7 @@
"require-dev": { "require-dev": {
"psr/log": "~1.0", "psr/log": "~1.0",
"symfony/config": "~2.8|~3.0", "symfony/config": "~2.8|~3.0",
"symfony/intl": "~2.8|~3.0", "symfony/intl": "^2.8.18|^3.2.5",
"symfony/yaml": "~2.8|~3.0" "symfony/yaml": "~2.8|~3.0"
}, },
"suggest": { "suggest": {
@ -2405,26 +2405,29 @@
], ],
"description": "Symfony Translation Component", "description": "Symfony Translation Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-02-16T22:46:52+00:00" "time": "2017-03-21T21:44:32+00:00"
}, },
{ {
"name": "symfony/var-dumper", "name": "symfony/var-dumper",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/var-dumper.git", "url": "https://github.com/symfony/var-dumper.git",
"reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20" "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/cb50260b674ee1c2d4ab49f2395a42e0b4681e20", "url": "https://api.github.com/repos/symfony/var-dumper/zipball/81dce20f69a8b40427e1f4e6462178df87cafc03",
"reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20", "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.5.9",
"symfony/polyfill-mbstring": "~1.0" "symfony/polyfill-mbstring": "~1.0"
}, },
"conflict": {
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
},
"require-dev": { "require-dev": {
"twig/twig": "~1.20|~2.0" "twig/twig": "~1.20|~2.0"
}, },
@ -2468,7 +2471,7 @@
"debug", "debug",
"dump" "dump"
], ],
"time": "2017-02-16T22:46:52+00:00" "time": "2017-03-12T16:07:05+00:00"
}, },
{ {
"name": "tijsverkoyen/css-to-inline-styles", "name": "tijsverkoyen/css-to-inline-styles",
@ -2951,6 +2954,102 @@
], ],
"time": "2016-04-29T12:21:54+00:00" "time": "2016-04-29T12:21:54+00:00"
}, },
{
"name": "guzzle/guzzle",
"version": "v3.9.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle3.git",
"reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9",
"reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.3.3",
"symfony/event-dispatcher": "~2.1"
},
"replace": {
"guzzle/batch": "self.version",
"guzzle/cache": "self.version",
"guzzle/common": "self.version",
"guzzle/http": "self.version",
"guzzle/inflection": "self.version",
"guzzle/iterator": "self.version",
"guzzle/log": "self.version",
"guzzle/parser": "self.version",
"guzzle/plugin": "self.version",
"guzzle/plugin-async": "self.version",
"guzzle/plugin-backoff": "self.version",
"guzzle/plugin-cache": "self.version",
"guzzle/plugin-cookie": "self.version",
"guzzle/plugin-curlauth": "self.version",
"guzzle/plugin-error-response": "self.version",
"guzzle/plugin-history": "self.version",
"guzzle/plugin-log": "self.version",
"guzzle/plugin-md5": "self.version",
"guzzle/plugin-mock": "self.version",
"guzzle/plugin-oauth": "self.version",
"guzzle/service": "self.version",
"guzzle/stream": "self.version"
},
"require-dev": {
"doctrine/cache": "~1.3",
"monolog/monolog": "~1.0",
"phpunit/phpunit": "3.7.*",
"psr/log": "~1.0",
"symfony/class-loader": "~2.1",
"zendframework/zend-cache": "2.*,<2.3",
"zendframework/zend-log": "2.*,<2.3"
},
"suggest": {
"guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.9-dev"
}
},
"autoload": {
"psr-0": {
"Guzzle": "src/",
"Guzzle\\Tests": "tests/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Guzzle Community",
"homepage": "https://github.com/guzzle/guzzle/contributors"
}
],
"description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"abandoned": "guzzlehttp/guzzle",
"time": "2015-03-18T18:23:50+00:00"
},
{ {
"name": "hamcrest/hamcrest-php", "name": "hamcrest/hamcrest-php",
"version": "v1.2.2", "version": "v1.2.2",
@ -3375,16 +3474,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "4.0.7", "version": "4.0.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "09e2277d14ea467e5a984010f501343ef29ffc69" "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/09e2277d14ea467e5a984010f501343ef29ffc69", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
"reference": "09e2277d14ea467e5a984010f501343ef29ffc69", "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3434,7 +3533,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2017-03-01T09:12:17+00:00" "time": "2017-04-02T07:44:40+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -3624,16 +3723,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "5.7.15", "version": "5.7.19",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "b99112aecc01f62acf3d81a3f59646700a1849e5" "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b99112aecc01f62acf3d81a3f59646700a1849e5", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69c4f49ff376af2692bad9cebd883d17ebaa98a1",
"reference": "b99112aecc01f62acf3d81a3f59646700a1849e5", "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3702,7 +3801,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2017-03-02T15:22:43+00:00" "time": "2017-04-03T02:22:27+00:00"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
@ -3763,6 +3862,64 @@
], ],
"time": "2016-12-08T20:27:08+00:00" "time": "2016-12-08T20:27:08+00:00"
}, },
{
"name": "satooshi/php-coveralls",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/satooshi/php-coveralls.git",
"reference": "da51d304fe8622bf9a6da39a8446e7afd432115c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/da51d304fe8622bf9a6da39a8446e7afd432115c",
"reference": "da51d304fe8622bf9a6da39a8446e7afd432115c",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-simplexml": "*",
"guzzle/guzzle": "^2.8|^3.0",
"php": ">=5.3.3",
"psr/log": "^1.0",
"symfony/config": "^2.1|^3.0",
"symfony/console": "^2.1|^3.0",
"symfony/stopwatch": "^2.0|^3.0",
"symfony/yaml": "^2.0|^3.0"
},
"suggest": {
"symfony/http-kernel": "Allows Symfony integration"
},
"bin": [
"bin/coveralls"
],
"type": "library",
"autoload": {
"psr-4": {
"Satooshi\\": "src/Satooshi/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kitamura Satoshi",
"email": "with.no.parachute@gmail.com",
"homepage": "https://www.facebook.com/satooshi.jp"
}
],
"description": "PHP client library for Coveralls API",
"homepage": "https://github.com/satooshi/php-coveralls",
"keywords": [
"ci",
"coverage",
"github",
"test"
],
"time": "2016-01-20T17:35:46+00:00"
},
{ {
"name": "sebastian/code-unit-reverse-lookup", "name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.1", "version": "1.0.1",
@ -4278,16 +4435,16 @@
}, },
{ {
"name": "symfony/class-loader", "name": "symfony/class-loader",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/class-loader.git", "url": "https://github.com/symfony/class-loader.git",
"reference": "2847d56f518ad5721bf85aa9174b3aa3fd12aa03" "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/class-loader/zipball/2847d56f518ad5721bf85aa9174b3aa3fd12aa03", "url": "https://api.github.com/repos/symfony/class-loader/zipball/c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9",
"reference": "2847d56f518ad5721bf85aa9174b3aa3fd12aa03", "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4330,7 +4487,63 @@
], ],
"description": "Symfony ClassLoader Component", "description": "Symfony ClassLoader Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-01-21T17:06:35+00:00" "time": "2017-02-18T17:28:00+00:00"
},
{
"name": "symfony/config",
"version": "v3.2.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "8444bde28e3c2a33e571e6f180c2d78bfdc4480d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/8444bde28e3c2a33e571e6f180c2d78bfdc4480d",
"reference": "8444bde28e3c2a33e571e6f180c2d78bfdc4480d",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/filesystem": "~2.8|~3.0"
},
"require-dev": {
"symfony/yaml": "~3.0"
},
"suggest": {
"symfony/yaml": "To use the yaml reference dumper"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Config\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
"time": "2017-04-04T15:30:56+00:00"
}, },
{ {
"name": "symfony/dom-crawler", "name": "symfony/dom-crawler",
@ -4389,17 +4602,115 @@
"time": "2017-01-21T17:13:55+00:00" "time": "2017-01-21T17:13:55+00:00"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/filesystem",
"version": "v3.2.4", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/yaml.git", "url": "https://github.com/symfony/filesystem.git",
"reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8" "reference": "64421e6479c4a8e60d790fb666bd520992861b66"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/9724c684646fcb5387d579b4bfaa63ee0b0c64c8", "url": "https://api.github.com/repos/symfony/filesystem/zipball/64421e6479c4a8e60d790fb666bd520992861b66",
"reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8", "reference": "64421e6479c4a8e60d790fb666bd520992861b66",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2017-03-26T15:47:15+00:00"
},
{
"name": "symfony/stopwatch",
"version": "v3.2.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
"reference": "c5ee0f8650c84b4d36a5f76b3b504233feaabf75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/c5ee0f8650c84b4d36a5f76b3b504233feaabf75",
"reference": "c5ee0f8650c84b4d36a5f76b3b504233feaabf75",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Stopwatch\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com",
"time": "2017-02-18T17:28:00+00:00"
},
{
"name": "symfony/yaml",
"version": "v3.2.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/62b4cdb99d52cb1ff253c465eb1532a80cebb621",
"reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4441,7 +4752,7 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-02-16T22:46:52+00:00" "time": "2017-03-20T09:45:15+00:00"
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
return [ return [
'name' => 'Firefly III', 'name' => 'Firefly III',
@ -70,7 +70,7 @@ return [
//Barryvdh\Debugbar\ServiceProvider::class, //Barryvdh\Debugbar\ServiceProvider::class,
DaveJamesMiller\Breadcrumbs\ServiceProvider::class, DaveJamesMiller\Breadcrumbs\ServiceProvider::class,
TwigBridge\ServiceProvider::class, TwigBridge\ServiceProvider::class,
'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider', PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider::class,
/* /*
* More service providers. * More service providers.
@ -125,7 +125,7 @@ return [
'URL' => Illuminate\Support\Facades\URL::class, 'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class, 'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class, 'View' => Illuminate\Support\Facades\View::class,
'Twig' => 'TwigBridge\Facade\Twig', 'Twig' => TwigBridge\Facade\Twig::class,
'Form' => Collective\Html\FormFacade::class, 'Form' => Collective\Html\FormFacade::class,
'Html' => Collective\Html\HtmlFacade::class, 'Html' => Collective\Html\HtmlFacade::class,
'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade',
@ -137,7 +137,7 @@ return [
'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm', 'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm',
'Entrust' => 'Zizaco\Entrust\EntrustFacade', 'Entrust' => 'Zizaco\Entrust\EntrustFacade',
'Input' => 'Illuminate\Support\Facades\Input', 'Input' => 'Illuminate\Support\Facades\Input',
'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade', 'Google2FA' => PragmaRX\Google2FA\Vendor\Laravel\Facade::class,
], ],
]; ];

View File

@ -23,7 +23,7 @@ return [
'is_demo_site' => false, 'is_demo_site' => false,
], ],
'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true),
'version' => '4.3.7', 'version' => '4.3.8',
'maxUploadSize' => 5242880, 'maxUploadSize' => 5242880,
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
'list_length' => 10, 'list_length' => 10,

View File

@ -8,7 +8,10 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Carbon\Carbon;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -33,10 +36,89 @@ $factory->define(
} }
); );
$factory->define(
FireflyIII\Models\ImportJob::class, function (Faker\Generator $faker) {
return [
'id' => $faker->numberBetween(1, 10),
'user_id' => 1,
'key' => $faker->words(1, true),
'file_type' => 'csv',
'status' => 'import_status_never_started',
'configuration' => null,
'extended_status' => [
'total_steps' => 0,
'steps_done' => 0,
'import_count' => 0,
'importTag' => 0,
'errors' => [],
],
];
}
);
$factory->define(
FireflyIII\Models\TransactionJournal::class, function (Faker\Generator $faker) {
return [
'id' => $faker->numberBetween(1, 10),
'user_id' => 1,
'transaction_type_id' => 1,
'bill_id' => null,
'transaction_currency_id' => 1,
'description' => $faker->words(3, true),
'date' => '2017-01-01',
'interest_date' => null,
'book_date' => null,
'process_date' => null,
'order' => 0,
'tag_count' => 0,
'encrypted' => 0,
'completed' => 1,
];
}
);
$factory->define(
FireflyIII\Models\Bill::class, function (Faker\Generator $faker) {
return [
'id' => $faker->numberBetween(1, 10),
'user_id' => 1,
'name' => $faker->words(3, true),
'match' => $faker->words(3, true),
'amount_min' => '100.00',
'amount_max' => '100.00',
'date' => '2017-01-01',
'repeat_freq' => 'monthly',
'skip' => 0,
'automatch' => 1,
'name_encrypted' => 0,
'match_encrypted' => 0,
];
}
);
$factory->define(
FireflyIII\Models\PiggyBank::class, function (Faker\Generator $faker) {
return [
'id' => $faker->numberBetween(1, 10),
'account_id' => $faker->numberBetween(1, 10),
'name' => $faker->words(3, true),
'target_amount' => '1000.00',
'startdate' => '2017-01-01',
'order' => 1,
'active' => 1,
'encrypted' => 0,
];
}
);
$factory->define( $factory->define(
FireflyIII\Models\Tag::class, function (Faker\Generator $faker) { FireflyIII\Models\Tag::class, function (Faker\Generator $faker) {
return [ return [
'id' => $faker->numberBetween(1, 10), 'id' => $faker->numberBetween(100, 150),
'user_id' => 1,
'tagMode' => 'nothing',
'tag' => $faker->words(1, true), 'tag' => $faker->words(1, true),
]; ];
} }
@ -60,6 +142,31 @@ $factory->define(
} }
); );
$factory->define(
FireflyIII\Models\PiggyBankEvent::class, function (Faker\Generator $faker) {
return [
'id' => $faker->numberBetween(1, 10),
'piggy_bank_id' => $faker->numberBetween(1, 10),
'transaction_journal_id' => $faker->numberBetween(1, 10),
'date' => $faker->date('Y-m-d'),
'amount' => '100',
];
}
);
$factory->define(
FireflyIII\Models\BudgetLimit::class, function (Faker\Generator $faker) {
return [
'id' => $faker->numberBetween(1, 10),
'start_date' => '2017-01-01',
'end_date' => '2017-01-31',
'amount' => '300',
'budget_id' => $faker->numberBetween(1, 6),
];
}
);
$factory->define( $factory->define(
FireflyIII\Models\Account::class, function (Faker\Generator $faker) { FireflyIII\Models\Account::class, function (Faker\Generator $faker) {
return [ return [
@ -80,6 +187,7 @@ $factory->define(
'description' => $faker->words(3, true), 'description' => $faker->words(3, true),
'source_account_name' => $faker->words(3, true), 'source_account_name' => $faker->words(3, true),
'destination_account_id' => $faker->numberBetween(1, 10), 'destination_account_id' => $faker->numberBetween(1, 10),
'date' => new Carbon,
'destination_account_name' => $faker->words(3, true), 'destination_account_name' => $faker->words(3, true),
'amount' => strval($faker->randomFloat(2, -100, 100)), 'amount' => strval($faker->randomFloat(2, -100, 100)),
'budget_id' => 0, 'budget_id' => 0,

View File

@ -8,7 +8,7 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
@ -76,6 +76,23 @@ class CreateSupportTables extends Migration
} }
} }
private function createConfigurationTable()
{
if (!Schema::hasTable('configuration')) {
Schema::create(
'configuration', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('name', 50);
$table->text('data');
$table->unique(['name']);
}
);
}
}
/** /**
* *
*/ */
@ -99,23 +116,6 @@ class CreateSupportTables extends Migration
} }
} }
private function createConfigurationTable()
{
if (!Schema::hasTable('configuration')) {
Schema::create(
'configuration', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('name', 50);
$table->text('data');
$table->unique(['name']);
}
);
}
}
/** /**
* *
*/ */

View File

@ -8,7 +8,7 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
@ -18,6 +18,14 @@ use Illuminate\Database\Schema\Blueprint;
*/ */
class CreateUsersTable extends Migration class CreateUsersTable extends Migration
{ {
/**
* Reverse the migrations.
*/
public function down()
{
Schema::drop('users');
}
/** /**
* Run the migrations. * Run the migrations.
* *
@ -40,12 +48,4 @@ class CreateUsersTable extends Migration
); );
} }
} }
/**
* Reverse the migrations.
*/
public function down()
{
Schema::drop('users');
}
} }

View File

@ -8,7 +8,7 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;

View File

@ -8,7 +8,7 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;

View File

@ -8,7 +8,7 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;

View File

@ -43,11 +43,11 @@ class ChangesForV431 extends Migration
); );
// change field "start_date" to "startdate" // change field "start_date" to "startdate"
// Schema::table( // Schema::table(
// 'budget_limits', function (Blueprint $table) { // 'budget_limits', function (Blueprint $table) {
// $table->renameColumn('startdate', 'start_date'); // $table->renameColumn('startdate', 'start_date');
// } // }
// ); // );
} }

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;

View File

@ -8,7 +8,7 @@
* *
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;

View File

@ -9,8 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use FireflyIII\Models\Role; use FireflyIII\Models\Role;
@ -30,7 +29,7 @@ class PermissionSeeder extends Seeder
$owner->save(); $owner->save();
$demo = new Role; $demo = new Role;
$demo->name ='demo'; $demo->name = 'demo';
$demo->display_name = 'Demo User'; $demo->display_name = 'Demo User';
$demo->description = 'User is a demo user'; $demo->description = 'User is a demo user';
$demo->save(); $demo->save();

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types=1);
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;

View File

@ -8,11 +8,15 @@
* See the LICENSE file for details. * See the LICENSE file for details.
*/ */
/** global: budgetChartUri */ /** global: budgetChartUri,budgetLimitID */
$(function () { $(function () {
"use strict"; "use strict";
if (budgetLimitID > 0) {
lineChart(budgetChartUri, 'budgetOverview');
}
if (budgetLimitID == 0) {
columnChart(budgetChartUri, 'budgetOverview'); columnChart(budgetChartUri, 'budgetOverview');
}
}); });

View File

@ -115,6 +115,26 @@ return [
'multi_select_no_selection' => 'Nichts ausgewählt', 'multi_select_no_selection' => 'Nichts ausgewählt',
'multi_select_all_selected' => 'Alle ausgewählt', 'multi_select_all_selected' => 'Alle ausgewählt',
'multi_select_filter_placeholder' => 'Suche..', 'multi_select_filter_placeholder' => 'Suche..',
'between_dates_breadcrumb' => 'Between :start and :end',
'all_journals_without_budget' => 'All transactions without a budget',
'journals_without_budget' => 'Transactions without a budget',
'all_journals_without_category' => 'All transactions without a category',
'journals_without_category' => 'Transactions without a category',
'all_journals_for_account' => 'All transactions for account :name',
'journals_in_period_for_account' => 'All transactions for account :name between :start and :end',
'transferred' => 'Transferred',
'all_withdrawal' => 'All expenses',
'all_transactions' => 'All transactions',
'title_withdrawal_between' => 'All expenses between :start and :end',
'all_deposit' => 'All revenue',
'title_deposit_between' => 'All revenue between :start and :end',
'all_transfers' => 'All transfers',
'title_transfers_between' => 'All transfers between :start and :end',
'all_transfer' => 'All transfers',
'title_transfer_between' => 'All transfers between :start and :end',
'all_journals_for_category' => 'All transactions for category :name',
'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
'not_available_demo_user' => 'The feature you try to access is not available to demo users.',
// repeat frequencies: // repeat frequencies:
@ -855,7 +875,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?',
'tag_title_nothing' => 'Standard-Tags', 'tag_title_nothing' => 'Standard-Tags',
'tag_title_balancingAct' => 'Ausgleich Tags', 'tag_title_balancingAct' => 'Ausgleich Tags',
'tag_title_advancePayment' => 'Vorauszahlung Tags', 'tag_title_advancePayment' => 'Vorauszahlung Tags',
'tags_introduction' => 'In der Regel sind Tags einzelne Worte, erdacht um Einträge mit Begriffen wie <span class="label label-info">Ausgaben</span>, <span class="label label-info">Rechnungen</span> oder <span class="label label-info">Partyvorbereitung</span> schnell zusammenzufassen. In Firefly III können Tags weitere Eigenschaften wie ein Datum, eine Beschreibung und einen Ort enthalten. Dieses erlaubt es Ihnen Transaktionen in sinnvoller Weise miteinander zu verknüpfen. Zum Beispiel können Sie einen Tag mit dem Titel <span class="label label-success"> Weihnachtsessen mit Freunden</span> erstellen und Informationen über das Restaurant hinzufügen. Solche Tags sind "einzigartig", sie werden nur für einen Anlass genutzt, enthalten aber eventuell mehrere Transaktionen.', 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like <span class="label label-info">expensive</span>, <span class="label label-info">bill</span> or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called <span class="label label-success">Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.',
'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',

View File

@ -115,6 +115,26 @@ return [
'multi_select_no_selection' => 'None selected', 'multi_select_no_selection' => 'None selected',
'multi_select_all_selected' => 'All selected', 'multi_select_all_selected' => 'All selected',
'multi_select_filter_placeholder' => 'Find..', 'multi_select_filter_placeholder' => 'Find..',
'between_dates_breadcrumb' => 'Between :start and :end',
'all_journals_without_budget' => 'All transactions without a budget',
'journals_without_budget' => 'Transactions without a budget',
'all_journals_without_category' => 'All transactions without a category',
'journals_without_category' => 'Transactions without a category',
'all_journals_for_account' => 'All transactions for account :name',
'journals_in_period_for_account' => 'All transactions for account :name between :start and :end',
'transferred' => 'Transferred',
'all_withdrawal' => 'All expenses',
'all_transactions' => 'All transactions',
'title_withdrawal_between' => 'All expenses between :start and :end',
'all_deposit' => 'All revenue',
'title_deposit_between' => 'All revenue between :start and :end',
'all_transfers' => 'All transfers',
'title_transfers_between' => 'All transfers between :start and :end',
'all_transfer' => 'All transfers',
'title_transfer_between' => 'All transfers between :start and :end',
'all_journals_for_category' => 'All transactions for category :name',
'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
'not_available_demo_user' => 'The feature you try to access is not available to demo users.',
// repeat frequencies: // repeat frequencies:
@ -854,7 +874,7 @@ return [
'tag_title_nothing' => 'Default tags', 'tag_title_nothing' => 'Default tags',
'tag_title_balancingAct' => 'Balancing act tags', 'tag_title_balancingAct' => 'Balancing act tags',
'tag_title_advancePayment' => 'Advance payment tags', 'tag_title_advancePayment' => 'Advance payment tags',
'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like <span class="label label-info">expensive</span>, <span class="label label-info">bill</span> or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called <span class="label label-success"> Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like <span class="label label-info">expensive</span>, <span class="label label-info">bill</span> or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called <span class="label label-success">Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.',
'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',

View File

@ -115,6 +115,26 @@ return [
'multi_select_no_selection' => 'None selected', 'multi_select_no_selection' => 'None selected',
'multi_select_all_selected' => 'All selected', 'multi_select_all_selected' => 'All selected',
'multi_select_filter_placeholder' => 'Find..', 'multi_select_filter_placeholder' => 'Find..',
'between_dates_breadcrumb' => 'Between :start and :end',
'all_journals_without_budget' => 'All transactions without a budget',
'journals_without_budget' => 'Transactions without a budget',
'all_journals_without_category' => 'All transactions without a category',
'journals_without_category' => 'Transactions without a category',
'all_journals_for_account' => 'All transactions for account :name',
'journals_in_period_for_account' => 'All transactions for account :name between :start and :end',
'transferred' => 'Transferred',
'all_withdrawal' => 'All expenses',
'all_transactions' => 'All transactions',
'title_withdrawal_between' => 'All expenses between :start and :end',
'all_deposit' => 'All revenue',
'title_deposit_between' => 'All revenue between :start and :end',
'all_transfers' => 'All transfers',
'title_transfers_between' => 'All transfers between :start and :end',
'all_transfer' => 'All transfers',
'title_transfer_between' => 'All transfers between :start and :end',
'all_journals_for_category' => 'All transactions for category :name',
'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
'not_available_demo_user' => 'The feature you try to access is not available to demo users.',
// repeat frequencies: // repeat frequencies:
@ -854,7 +874,7 @@ return [
'tag_title_nothing' => 'Default tags', 'tag_title_nothing' => 'Default tags',
'tag_title_balancingAct' => 'Balancing act tags', 'tag_title_balancingAct' => 'Balancing act tags',
'tag_title_advancePayment' => 'Advance payment tags', 'tag_title_advancePayment' => 'Advance payment tags',
'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like <span class="label label-info">expensive</span>, <span class="label label-info">bill</span> or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called <span class="label label-success"> Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.', 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like <span class="label label-info">expensive</span>, <span class="label label-info">bill</span> or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called <span class="label label-success">Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.',
'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',

View File

@ -15,7 +15,7 @@ return [
'month_and_day' => '%e %B %Y', 'month_and_day' => '%e %B %Y',
'date_time' => '%B %e %Y @ %T', 'date_time' => '%B %e %Y @ %T',
'specific_day' => '%e %B %Y', 'specific_day' => '%e %B %Y',
'week_in_year' => '%W %Y', 'week_in_year' => 'Semaine %W %Y',
'quarter_of_year' => '%B %Y', 'quarter_of_year' => '%B %Y',
'year' => '%Y', 'year' => '%Y',
'half_year' => '%B %Y', 'half_year' => '%B %Y',

View File

@ -15,7 +15,7 @@ return [
'import_configure_title' => 'Configurer l\'import', 'import_configure_title' => 'Configurer l\'import',
'import_configure_intro' => 'Il y a des options pour l\'import CSV. Veuillez indiquer si votre fichier CSV contient les en-têtes dans la première colonne, et quel est le format des dates de vos champs date. Cela peut nécessiter quelques essais. Le délimiteur de champ est généralement un « , », mais pourrait également être un « ; ». Cochez cette case avec soin.', 'import_configure_intro' => 'Il y a des options pour l\'import CSV. Veuillez indiquer si votre fichier CSV contient les en-têtes dans la première colonne, et quel est le format des dates de vos champs date. Cela peut nécessiter quelques essais. Le délimiteur de champ est généralement un « , », mais pourrait également être un « ; ». Cochez cette case avec soin.',
'import_configure_form' => 'Basic CSV import options', 'import_configure_form' => 'Options dimportation CSV basique',
'header_help' => 'Cochez cette case si la première ligne de votre fichier CSV contient les entêtes des colonnes', 'header_help' => 'Cochez cette case si la première ligne de votre fichier CSV contient les entêtes des colonnes',
'date_help' => 'Le format de la date et de lheure dans votre fichier CSV. Utiliser les formats comme indiqué sur <a href="https://secure.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">cette page</a>. La valeur par défaut va analyser les dates ressemblant à ceci: :dateExample.', 'date_help' => 'Le format de la date et de lheure dans votre fichier CSV. Utiliser les formats comme indiqué sur <a href="https://secure.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">cette page</a>. La valeur par défaut va analyser les dates ressemblant à ceci: :dateExample.',
'delimiter_help' => 'Choisissez le délimiteur de champ qui est utilisé dans votre fichier dentrée. Si vous nêtes pas certain, la virgule est loption la plus sûre.', 'delimiter_help' => 'Choisissez le délimiteur de champ qui est utilisé dans votre fichier dentrée. Si vous nêtes pas certain, la virgule est loption la plus sûre.',

View File

@ -8,17 +8,17 @@
*/ */
return [ return [
'no_demo_text' => 'Sorry, there is no extra demo-explanation text for <abbr title=":route">this page</abbr>.', 'no_demo_text' => 'Désolé, il ny a aucun texte supplémentaire de démonstration ou d\'explication pour <abbr title=":route"> cette page</abbr>.',
'see_help_icon' => 'However, the <i class="fa fa-question-circle"></i>-icon in the top right corner may tell you more.', 'see_help_icon' => 'Cependant, l\'icône <i class="fa fa-question-circle"></i> située dans le coin supérieur droit peut vous en dire plus.',
'index' => 'Welcome to <strong>Firefly III</strong>! On this page you get a quick overview of your finances. For more information, check out Accounts &rarr; <a href=":asset">Asset Accounts</a> and of course the <a href=":budgets">Budgets</a> and <a href=":reports">Reports</a> pages. Or just take a look around and see where you end up.', 'index' => 'Bienvenue chez <strong>Firefly III</strong> ! Sur cette page, vous obtenez un aperçu rapide de vos finances. Pour plus dinformations, consultez comptes &rarr; <a href=":asset"> Comptes dactif</a> et, bien sûr, les pages des <a href=":budgets"> Budgets</a> et des <a href=":reports"> rapports</a>. Ou juste jetez un coup dœil et voyez où vous vous retrouvez.',
'accounts-index' => 'Asset accounts are your personal bank accounts. Expense accounts are the accounts you spend money at, such as stores and friends. Revenue accounts are accounts you receive money from, such as your job, the government or other sources of income. On this page you can edit or remove them.', 'accounts-index' => 'Asset accounts are your personal bank accounts. Expense accounts are the accounts you spend money at, such as stores and friends. Revenue accounts are accounts you receive money from, such as your job, the government or other sources of income. On this page you can edit or remove them.',
'budgets-index' => 'This page shows you an overview of your budgets. The top bar shows the amount that is available to be budgeted. This can be customized for any period by clicking the amount on the right. The amount you\'ve actually spent is shown in the bar below. Below that are the expenses per budget and what you\'ve budgeted for them.', 'budgets-index' => 'Cette page vous présente un aperçu de vos budgets. La barre du haut affiche le montant disponible à budgétiser. Cela peut être personnalisé pour toute période en cliquant sur le montant sur la droite. Le montant que vous avez réellement dépensé saffiche dans la barre ci-dessous. Visualisez ainsi les dépenses budgétisées et votre prévisionnel.',
'reports-index-start' => 'Firefly III supports four types of reports. Read about them by clicking on the <i class="fa fa-question-circle"></i>-icon in the top right corner.', 'reports-index-start' => 'Firefly III prend en charge quatre types de rapports. Apprenez-en plus à leur sujet en cliquant sur l\'icône <i class="fa fa-question-circle"></i> située dans le coin supérieur droit.',
'reports-index-examples' => 'Be sure to check out these examples: <a href=":one">a monthly financial overview</a>, <a href=":two">a yearly financial overview</a> and <a href=":three">a budget overview</a>.', 'reports-index-examples' => 'Noubliez pas de consultez ces exemples : <a href=":one"> un aperçu financier mensuel</a>, <a href=":two"> une vue densemble financière annuelle</a> ainsi <a href=":three"> quune présentation du budget</a>.',
'currencies-index' => 'Firefly III supports multiple currencies. Although it defaults to the Euro it can be set to the US Dollar and many other currencies. As you can see a small selection of currencies has been included but you can add your own if you wish to. Changing the default currency will not change the currency of existing transactions however: Firefly III supports the use of multiple currencies at the same time.', 'currencies-index' => 'Firefly III prend en charge plusieurs devises. Bien que l\'Euro soit la devise par défaut, cette dernière peut être changée pour le Dollar américain et de nombreuses autres devises. Comme vous pouvez le remarquer une petite sélection des monnaies a été incluse, mais vous pouvez ajouter vos propres devises si vous le souhaitez. Gardez à l\'esprit que la modification de la devise par défaut ne modifie pas la monnaie des transactions existantes : Firefly III prend en charge lutilisation de plusieurs devises en même temps.',
'transactions-index' => 'These expenses, deposits and transfers are not particularly imaginative. They have been generated automatically.', 'transactions-index' => 'Ces dépenses, dépôts et transferts ne sont pas particulièrement imaginatifs. Ils ont été générés automatiquement.',
'piggy-banks-index' => 'As you can see, there are three piggy banks. Use the plus and minus buttons to influence the amount of money in each piggy bank. Click the name of the piggy bank to see the administration for each piggy bank.', 'piggy-banks-index' => 'Comme vous pouvez le voir, il y a trois tirelires. Utilisez les boutons plus et moins pour influer sur le montant dargent dans chaque tirelire. Cliquez sur le nom de la tirelire pour voir ladministration pour chaque tirelire.',
'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-index' => 'Bien sûr, nimporte quel fichier CSV peut être importé dans Firefly III ',
'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-security' => 'Pour des raisons de sécurité, votre téléchargement a été remplacé par un fichier local.',
'import-configure-configuration' => 'The configuration you see below is correct for the local file.', 'import-configure-configuration' => 'La configuration que vous voyez ci-dessous est correcte pour le fichier local.',
]; ];

View File

@ -26,7 +26,7 @@ return [
'showEverything' => 'Tout Afficher', 'showEverything' => 'Tout Afficher',
'never' => 'Jamais', 'never' => 'Jamais',
'search_results_for' => 'Résultats de recherche pour ":query"', 'search_results_for' => 'Résultats de recherche pour ":query"',
'advanced_search' => 'Advanced search', 'advanced_search' => 'Recherche avancée',
'advanced_search_intro' => 'There are several modifiers that you can use in your search to narrow down the results. If you use any of these, the search will <em>only</em> return transactions. Please click the <i class="fa fa-question-circle"></i>-icon for more information.', 'advanced_search_intro' => 'There are several modifiers that you can use in your search to narrow down the results. If you use any of these, the search will <em>only</em> return transactions. Please click the <i class="fa fa-question-circle"></i>-icon for more information.',
'bounced_error' => 'Le message envoyé à :email a été rejeté, donc pas d\'accès pour vous.', 'bounced_error' => 'Le message envoyé à :email a été rejeté, donc pas d\'accès pour vous.',
'deleted_error' => 'Ces informations d\'identification ne sont pas présentes dans nos données.', 'deleted_error' => 'Ces informations d\'identification ne sont pas présentes dans nos données.',
@ -53,8 +53,8 @@ return [
'flash_info_multiple' => 'Il y a un message| Il y a :count messages', 'flash_info_multiple' => 'Il y a un message| Il y a :count messages',
'flash_error_multiple' => 'Il y a une erreur|Il y a :count errors', 'flash_error_multiple' => 'Il y a une erreur|Il y a :count errors',
'net_worth' => 'Valeur nette', 'net_worth' => 'Valeur nette',
'route_has_no_help' => 'There is no help for this route.', 'route_has_no_help' => 'Il n\'y a pas d\'aide pour cette page.',
'help_may_not_be_your_language' => 'This help text is in English. It is not yet available in your language', 'help_may_not_be_your_language' => 'Ce texte daide est en anglais. Il nest pas encore disponible dans votre langue',
'two_factor_welcome' => 'Bonjour, :user !', 'two_factor_welcome' => 'Bonjour, :user !',
'two_factor_enter_code' => 'Pour continuer, veuillez entrer votre code dauthentification à deux facteurs. Votre application peut la générer pour vous.', 'two_factor_enter_code' => 'Pour continuer, veuillez entrer votre code dauthentification à deux facteurs. Votre application peut la générer pour vous.',
'two_factor_code_here' => 'Entrez votre code ici', 'two_factor_code_here' => 'Entrez votre code ici',
@ -69,12 +69,12 @@ return [
'warning_much_data' => ':days de données peuvent prendre un certain temps à charger.', 'warning_much_data' => ':days de données peuvent prendre un certain temps à charger.',
'registered' => 'Vous avez été enregistré avec succès !', 'registered' => 'Vous avez été enregistré avec succès !',
'search' => 'Rechercher', 'search' => 'Rechercher',
'search_found_accounts' => 'Found :count account(s) for your query.', 'search_found_accounts' => 'Trouvé :count compte(s) correspondant à votre requête.',
'search_found_categories' => 'Found :count category(ies) for your query.', 'search_found_categories' => 'Trouvé :count catégorie(s) correspondant à votre requête.',
'search_found_budgets' => 'Found :count budget(s) for your query.', 'search_found_budgets' => 'Trouvé :count budget(s) correspondant à votre requête.',
'search_found_tags' => 'Found :count tag(s) for your query.', 'search_found_tags' => 'Trouvé :count mot(s)-clef(s) correspondant à votre requête.',
'search_found_transactions' => 'Found :count transaction(s) for your query.', 'search_found_transactions' => 'Trouvé :count transaction(s) correspondant à votre requête.',
'results_limited' => 'The results are limited to :count entries.', 'results_limited' => 'Les résultats sont limités à :count entrées.',
'tagbalancingAct' => 'Balancing act', 'tagbalancingAct' => 'Balancing act',
'tagadvancePayment' => 'Advance payment', 'tagadvancePayment' => 'Advance payment',
'tagnothing' => '', 'tagnothing' => '',
@ -84,26 +84,26 @@ return [
'Credit card' => 'Credit card', 'Credit card' => 'Credit card',
'source_accounts' => 'Compte(s) source', 'source_accounts' => 'Compte(s) source',
'destination_accounts' => 'Compte(s) de destination', 'destination_accounts' => 'Compte(s) de destination',
'user_id_is' => 'Your user id is <strong>:user</strong>', 'user_id_is' => 'Votre identifiant dutilisateur est <strong>:user</strong>',
'field_supports_markdown' => 'This field supports <a href="https://en.support.wordpress.com/markdown-quick-reference/">Markdown</a>.', 'field_supports_markdown' => 'Ce champ prend en charge la <a href="https://en.support.wordpress.com/markdown-quick-reference/">syntaxe Markdown</a>.',
'need_more_help' => 'If you need more help using Firefly III, please <a href="https://github.com/firefly-iii/firefly-iii/issues">open a ticket on Github</a>.', 'need_more_help' => 'Si vous désirez plus de renseignements sur l\'utilisation de Firefly III, merci <a href="https://github.com/firefly-iii/firefly-iii/issues">d\'ouvrir un ticket sur Github</a>.',
'nothing_to_display' => 'There are no transactions to show you', 'nothing_to_display' => 'Il ny a aucune transaction à afficher',
'show_all_no_filter' => 'Show all transactions without grouping them by date.', 'show_all_no_filter' => 'Montrer toutes les transactions sans les classer par date.',
'expenses_by_category' => 'Expenses by category', 'expenses_by_category' => 'Dépenses par catégorie',
'expenses_by_budget' => 'Expenses by budget', 'expenses_by_budget' => 'Dépenses par budget',
'income_by_category' => 'Income by category', 'income_by_category' => 'Revenu par catégorie',
'cannot_redirect_to_account' => 'Firefly III cannot redirect you to the correct page. Apologies.', 'cannot_redirect_to_account' => 'Firefly III n\'est pas en mesure de vous rediriger vers la bonne page. Veuillez nous en excuser.',
'sum_of_expenses' => 'Sum of expenses', 'sum_of_expenses' => 'Montant des dépenses',
'sum_of_income' => 'Sum of income', 'sum_of_income' => 'Montant des revenus',
'spent_in_specific_budget' => 'Spent in budget ":budget"', 'spent_in_specific_budget' => 'Spent in budget ":budget"',
'sum_of_expenses_in_budget' => 'Spent total in budget ":budget"', 'sum_of_expenses_in_budget' => 'Spent total in budget ":budget"',
'left_in_budget_limit' => 'Left to spend according to budgeting', 'left_in_budget_limit' => 'Left to spend according to budgeting',
'cannot_change_demo' => 'You cannot change the password of the demonstration account.', 'cannot_change_demo' => 'Vous ne pouvez pas modifier le mot de passe du compte de démonstration.',
'cannot_delete_demo' => 'You cannot remove the demonstration account.', 'cannot_delete_demo' => 'Vous ne pouvez pas supprimer le compte de démonstration.',
'cannot_reset_demo_user' => 'You cannot reset the password of the demonstration account', 'cannot_reset_demo_user' => 'Vous ne pouvez pas réinitialiser le mot de passe du compte démonstration',
'per_period' => 'Per period', 'per_period' => 'Per period',
'all_periods' => 'All periods', 'all_periods' => 'Toutes les périodes',
'current_period' => 'Current period', 'current_period' => 'Période en cours',
'show_the_current_period_and_overview' => 'Show the current period and overview', 'show_the_current_period_and_overview' => 'Show the current period and overview',
'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.', 'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.',
'budget_in_period' => '":name" between :start and :end', 'budget_in_period' => '":name" between :start and :end',
@ -115,6 +115,26 @@ return [
'multi_select_no_selection' => 'None selected', 'multi_select_no_selection' => 'None selected',
'multi_select_all_selected' => 'All selected', 'multi_select_all_selected' => 'All selected',
'multi_select_filter_placeholder' => 'Find..', 'multi_select_filter_placeholder' => 'Find..',
'between_dates_breadcrumb' => 'Between :start and :end',
'all_journals_without_budget' => 'All transactions without a budget',
'journals_without_budget' => 'Transactions without a budget',
'all_journals_without_category' => 'All transactions without a category',
'journals_without_category' => 'Transactions without a category',
'all_journals_for_account' => 'All transactions for account :name',
'journals_in_period_for_account' => 'All transactions for account :name between :start and :end',
'transferred' => 'Transferred',
'all_withdrawal' => 'All expenses',
'all_transactions' => 'All transactions',
'title_withdrawal_between' => 'All expenses between :start and :end',
'all_deposit' => 'All revenue',
'title_deposit_between' => 'All revenue between :start and :end',
'all_transfers' => 'All transfers',
'title_transfers_between' => 'All transfers between :start and :end',
'all_transfer' => 'All transfers',
'title_transfer_between' => 'All transfers between :start and :end',
'all_journals_for_category' => 'All transactions for category :name',
'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
'not_available_demo_user' => 'The feature you try to access is not available to demo users.',
// repeat frequencies: // repeat frequencies:
@ -854,7 +874,7 @@ return [
'tag_title_nothing' => 'Tags par défaut', 'tag_title_nothing' => 'Tags par défaut',
'tag_title_balancingAct' => 'Balancing act tags', 'tag_title_balancingAct' => 'Balancing act tags',
'tag_title_advancePayment' => 'Tags de paiement anticipé', 'tag_title_advancePayment' => 'Tags de paiement anticipé',
'tags_introduction' => 'Les tags sont généralement des mots au singulier, conçus pour grouper rapidement des éléments en utilisant des mots comme <span class="label label-info">cher</span>, <span class="label label-info">factures</span> ou <span class="label label-info">pour-fêtes</span>. Dans Firefly III, les tags peuvent avoir plus de propriétés comme une date, la description et lemplacement. Cela vous permet de réunir des opérations de façon plus significative. Par exemple, vous pourriez faire un tag appelé <span class="label label-success">Dîner de Noël avec des amis</span> et ajouter des informations sur le restaurant. Ces tags sont au singulier, vous devez seulement les utiliser pour une occasion unique, peut-être avec plusieurs opérations.', 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like <span class="label label-info">expensive</span>, <span class="label label-info">bill</span> or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called <span class="label label-success">Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
'tags_group' => 'Les tags groupent des opérations ensemble, ce qui permet de stocker des remboursements (dans le cas où vous avancer de l\'argent aux autres) et d\'autres types "d\'équilibres" où résumer les dépenses (les remboursements de votre nouvelle TV) ou quand les dépenses et les dépôts s\'annulent les uns les autres (achat de quelque chose avec de l\'argent mis de coté). C\'est comme vous souhaitez. Utiliser les tags à l\'ancienne est toujours possible.', 'tags_group' => 'Les tags groupent des opérations ensemble, ce qui permet de stocker des remboursements (dans le cas où vous avancer de l\'argent aux autres) et d\'autres types "d\'équilibres" où résumer les dépenses (les remboursements de votre nouvelle TV) ou quand les dépenses et les dépôts s\'annulent les uns les autres (achat de quelque chose avec de l\'argent mis de coté). C\'est comme vous souhaitez. Utiliser les tags à l\'ancienne est toujours possible.',
'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',

View File

@ -25,17 +25,17 @@ return [
'match' => 'Correspondre à', 'match' => 'Correspondre à',
'repeat_freq' => 'Répétitions', 'repeat_freq' => 'Répétitions',
'journal_currency_id' => 'Devise', 'journal_currency_id' => 'Devise',
'currency_id' => 'Currency', 'currency_id' => 'Devise',
'attachments' => 'Attachments', 'attachments' => 'Documents joints',
'journal_amount' => 'Montant', 'journal_amount' => 'Montant',
'journal_asset_source_account' => 'Compte dactif (source)', 'journal_asset_source_account' => 'Compte dactif (source)',
'journal_source_account_name' => 'Compte de recettes (source)', 'journal_source_account_name' => 'Compte de recettes (source)',
'journal_source_account_id' => 'Compte dactif (source)', 'journal_source_account_id' => 'Compte dactif (source)',
'BIC' => 'BIC', 'BIC' => 'Code BIC',
'account_from_id' => 'Compte d\'origine', 'account_from_id' => 'Compte d\'origine',
'account_to_id' => 'Compte de destination', 'account_to_id' => 'Compte de destination',
'source_account' => 'Source account', 'source_account' => 'Compte d\'origine',
'destination_account' => 'Destination account', 'destination_account' => 'Compte destinataire',
'journal_destination_account_id' => 'Compte dactif (destination)', 'journal_destination_account_id' => 'Compte dactif (destination)',
'asset_destination_account' => 'Compte dactif (destination)', 'asset_destination_account' => 'Compte dactif (destination)',
'asset_source_account' => 'Compte dactif (source)', 'asset_source_account' => 'Compte dactif (source)',
@ -82,15 +82,15 @@ return [
'book_date' => 'Book date', 'book_date' => 'Book date',
'process_date' => 'Date de traitement', 'process_date' => 'Date de traitement',
'category' => 'Catégorie', 'category' => 'Catégorie',
'tags' => 'Tags', 'tags' => 'Mots-clés',
'deletePermanently' => 'Supprimer définitivement', 'deletePermanently' => 'Supprimer définitivement',
'cancel' => 'Annuler', 'cancel' => 'Annuler',
'targetdate' => 'Date cible', 'targetdate' => 'Date cible',
'tag' => 'Tag', 'tag' => 'Mot-clé',
'under' => 'En dessous de', 'under' => 'En dessous de',
'symbol' => 'Symbole', 'symbol' => 'Symbole',
'code' => 'Code', 'code' => 'Code',
'iban' => 'IBAN', 'iban' => 'Numéro IBAN',
'accountNumber' => 'N° de compte', 'accountNumber' => 'N° de compte',
'has_headers' => 'Entêtes ', 'has_headers' => 'Entêtes ',
'date_format' => 'Format de la date', 'date_format' => 'Format de la date',
@ -151,18 +151,18 @@ return [
'category_keep_transactions' => 'La seule opération liée à cette catégorie ne sera pas supprimée.|Les :count opérations liées à cette catégorie ne seront pas supprimées.', 'category_keep_transactions' => 'La seule opération liée à cette catégorie ne sera pas supprimée.|Les :count opérations liées à cette catégorie ne seront pas supprimées.',
'tag_keep_transactions' => 'La seule opération liée à ce tag ne sera pas supprimée.|Les :count opérations liées à ce tag ne seront pas supprimées.', 'tag_keep_transactions' => 'La seule opération liée à ce tag ne sera pas supprimée.|Les :count opérations liées à ce tag ne seront pas supprimées.',
'email' => 'Email address', 'email' => 'Adresse Email',
'password' => 'Password', 'password' => 'Mot de passe',
'password_confirmation' => 'Password (again)', 'password_confirmation' => 'Entrer à nouveau le mot de passe',
'blocked' => 'Is blocked?', 'blocked' => 'Est bloqué?',
'blocked_code' => 'Reason for block', 'blocked_code' => 'Raison du blocage',
// admin // admin
'domain' => 'Domaine', 'domain' => 'Domaine',
'single_user_mode' => 'Mode utilisateur unique', 'single_user_mode' => 'Mode utilisateur unique',
'must_confirm_account' => 'New users must activate account', 'must_confirm_account' => 'Les nouveaux utilisateurs doivent activer le compte',
'is_demo_site' => 'Is demo site', 'is_demo_site' => 'Est un site de démonstration',
// import // import

View File

@ -12,7 +12,7 @@
return [ return [
'buttons' => 'Boutons', 'buttons' => 'Boutons',
'icon' => 'Icône', 'icon' => 'Icône',
'id' => 'ID', 'id' => 'Identifiant',
'create_date' => 'Créé le', 'create_date' => 'Créé le',
'update_date' => 'Mis à jour le', 'update_date' => 'Mis à jour le',
'balance_before' => 'Solde avant', 'balance_before' => 'Solde avant',
@ -26,7 +26,7 @@ return [
'matchedOn' => 'Correspond à', 'matchedOn' => 'Correspond à',
'matchesOn' => 'Correspond à', 'matchesOn' => 'Correspond à',
'account_type' => 'Type de compte', 'account_type' => 'Type de compte',
'created_at' => 'Created at', 'created_at' => 'Créé le',
'new_balance' => 'Nouveau solde', 'new_balance' => 'Nouveau solde',
'account' => 'Compte', 'account' => 'Compte',
'matchingAmount' => 'Montant', 'matchingAmount' => 'Montant',
@ -39,12 +39,12 @@ return [
'repeat_freq' => 'Répétitions', 'repeat_freq' => 'Répétitions',
'description' => 'Description', 'description' => 'Description',
'amount' => 'Montant', 'amount' => 'Montant',
'internal_reference' => 'Internal reference', 'internal_reference' => 'Référence interne',
'date' => 'Date', 'date' => 'Date',
'interest_date' => 'Date des intérêts', 'interest_date' => 'Date des intérêts',
'book_date' => 'Book date', 'book_date' => 'Date de réservation',
'process_date' => 'Date de traitement', 'process_date' => 'Date de traitement',
'due_date' => 'Due date', 'due_date' => 'Échéance',
'payment_date' => 'Date de paiement', 'payment_date' => 'Date de paiement',
'invoice_date' => 'Date de facturation', 'invoice_date' => 'Date de facturation',
'interal_reference' => 'Référence interne', 'interal_reference' => 'Référence interne',
@ -60,7 +60,7 @@ return [
'transfer' => 'Transfert', 'transfer' => 'Transfert',
'type' => 'Type', 'type' => 'Type',
'completed' => 'Terminé', 'completed' => 'Terminé',
'iban' => 'IBAN', 'iban' => 'Numéro IBAN',
'paid_current_period' => 'Payé cette période', 'paid_current_period' => 'Payé cette période',
'email' => 'E-mail', 'email' => 'E-mail',
'registered_at' => 'Enregistré le', 'registered_at' => 'Enregistré le',
@ -69,21 +69,21 @@ return [
'is_admin' => 'Est admin', 'is_admin' => 'Est admin',
'has_two_factor' => 'A 2FA', 'has_two_factor' => 'A 2FA',
'confirmed_from' => 'Confirmed from', 'confirmed_from' => 'Confirmed from',
'registered_from' => 'Registered from', 'registered_from' => 'Inscrit depuis le',
'blocked_code' => 'Code de blocage', 'blocked_code' => 'Code de blocage',
'domain' => 'Domaine', 'domain' => 'Domaine',
'registration_attempts' => 'Registration attempts', 'registration_attempts' => 'Registration attempts',
'source_account' => 'Source account', 'source_account' => 'Compte d\'origine',
'destination_account' => 'Destination account', 'destination_account' => 'Compte destinataire',
'accounts_count' => 'Number of accounts', 'accounts_count' => 'Nombre de comptes',
'journals_count' => 'Number of transactions', 'journals_count' => 'Nombre d\'opérations',
'attachments_count' => 'Number of attachments', 'attachments_count' => 'Nombre de pièces jointes',
'bills_count' => 'Number of bills', 'bills_count' => 'Number of bills',
'categories_count' => 'Number of categories', 'categories_count' => 'Number of categories',
'export_jobs_count' => 'Number of export jobs', 'export_jobs_count' => 'Number of export jobs',
'import_jobs_count' => 'Number of import jobs', 'import_jobs_count' => 'Number of import jobs',
'budget_count' => 'Number of budgets', 'budget_count' => 'Number of budgets',
'rule_and_groups_count' => 'Number of rules and rule groups', 'rule_and_groups_count' => 'Nombre de règles et de groupes de règles',
'tags_count' => 'Number of tags', 'tags_count' => 'Number of tags',
]; ];

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