Improve test coverage.

This commit is contained in:
James Cole 2019-08-02 05:25:24 +02:00
parent 4bd8e1b11e
commit fc70afa3ea
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
23 changed files with 202 additions and 57923 deletions

View File

@ -82,17 +82,12 @@ class TagFactory
public function findOrCreate(string $tag): ?Tag
{
$tag = trim($tag);
if (null === $this->tags) {
$this->tags = $this->user->tags()->get();
}
/** @var Tag $object */
foreach ($this->tags as $object) {
if (strtolower($object->tag) === strtolower($tag)) {
return $object;
/** @var Tag $dbTag */
$dbTag = $this->user->tags()->where('tag', $tag)->first();
if (null !== $tag) {
return $dbTag;
}
}
$newTag = $this->create(
[
'tag' => $tag,
@ -103,7 +98,6 @@ class TagFactory
'zoom_level' => null,
]
);
$this->tags->push($newTag);
return $newTag;
}

View File

@ -77,7 +77,7 @@ class Category
public function getCategories(): Collection
{
$set = $this->categories->sortBy(
function (CategoryModel $category) {
static function (CategoryModel $category) {
return $category->spent;
}
);

View File

@ -387,6 +387,10 @@ class CurrencyController extends Controller
/** @var User $user */
$user = auth()->user();
$data = $request->getCurrencyData();
if (false === $data['enabled'] && $this->repository->currencyInUse($currency)) {
$data['enabled'] = true;
}
if (!$this->userRepository->hasRole($user, 'owner')) {
// @codeCoverageIgnoreStart
$request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')]));

View File

@ -68,7 +68,7 @@ class AutoCompleteController extends Controller
$filteredAccountTypes[] = $type;
}
}
Log::debug('Now in accounts(). Filtering results.', $filteredAccountTypes);
Log::debug(sprintf('Now in accounts("%s"). Filtering results.', $search), $filteredAccountTypes);
$return = [];
$result = $repository->searchAccount((string)$search, $filteredAccountTypes);
@ -107,7 +107,7 @@ class AutoCompleteController extends Controller
// filter the account types:
$allowedAccountTypes = [AccountType::REVENUE];
Log::debug('Now in accounts(). Filtering results.', $allowedAccountTypes);
Log::debug('Now in revenueAccounts(). Filtering results.', $allowedAccountTypes);
$return = [];
$result = $repository->searchAccount((string)$search, $allowedAccountTypes);
@ -138,7 +138,7 @@ class AutoCompleteController extends Controller
// filter the account types:
$allowedAccountTypes = [AccountType::EXPENSE];
Log::debug('Now in accounts(). Filtering results.', $allowedAccountTypes);
Log::debug(sprintf('Now in expenseAccounts(%s). Filtering results.', $search), $allowedAccountTypes);
$return = [];
$result = $repository->searchAccount((string)$search, $allowedAccountTypes);

View File

@ -83,7 +83,7 @@ class SearchController extends Controller
*/
public function search(Request $request, SearchInterface $searcher): JsonResponse
{
$fullQuery = (string)$request->get('query');
$fullQuery = (string)$request->get('search');
$searcher->parseQuery($fullQuery);
$searcher->setLimit((int)config('firefly.search_result_limit'));

View File

@ -34,6 +34,7 @@ use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Rule;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
@ -568,8 +569,15 @@ class ImportArrayStorage
$tag = $repository->store($data);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
$journalIds = $collection->pluck('id')->toArray();
Log::debug('Looping groups...');
// TODO double loop.
/** @var TransactionGroup $group */
foreach ($collection as $group) {
Log::debug(sprintf('Looping journals in group #%d', $group->id));
/** @var TransactionJournal $journal */
$journalIds = $group->transactionJournals->pluck('id')->toArray();
$tagId = $tag->id;
foreach ($journalIds as $journalId) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
@ -583,7 +591,7 @@ class ImportArrayStorage
// @codeCoverageIgnoreEnd
}
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $collection->count(), $tag->id, $tag->tag));
}
$this->repository->setTag($this->importJob, $tag);
}

View File

@ -503,6 +503,7 @@ class BudgetRepository implements BudgetRepositoryInterface
public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
// prep data array:
/** @var Budget $budget */
@ -517,7 +518,6 @@ class BudgetRepository implements BudgetRepositoryInterface
// get all transactions:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setBudgets($budgets);
$journals = $collector->getExtractedJournals();
@ -529,7 +529,6 @@ class BudgetRepository implements BudgetRepositoryInterface
$date = $journal['date']->format($carbonFormat);
$data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $journal['amount']);
}
return $data;
}

View File

@ -25,9 +25,12 @@ namespace FireflyIII\Repositories\Currency;
use Carbon\Carbon;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\Preference;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\CurrencyDestroyService;
@ -62,7 +65,8 @@ class CurrencyRepository implements CurrencyRepositoryInterface
*/
public function countJournals(TransactionCurrency $currency): int
{
return $currency->transactions()->count();
return $currency->transactions()->count() + $currency->transactionJournals()->count();
}
/**
@ -75,14 +79,14 @@ class CurrencyRepository implements CurrencyRepositoryInterface
Log::debug(sprintf('Now in currencyInUse() for #%d ("%s")', $currency->id, $currency->code));
$countJournals = $this->countJournals($currency);
if ($countJournals > 0) {
Log::debug(sprintf('Count journals is %d, return true.', $countJournals));
Log::info(sprintf('Count journals is %d, return true.', $countJournals));
return true;
}
// is the only currency left
if (1 === $this->getAll()->count()) {
Log::debug('Is the last currency in the system, return true. ', $countJournals);
Log::info('Is the last currency in the system, return true. ');
return true;
}
@ -90,7 +94,41 @@ class CurrencyRepository implements CurrencyRepositoryInterface
// is being used in accounts:
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string)$currency->id))->count();
if ($meta > 0) {
Log::debug(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
return true;
}
// is being used in bills:
$bills = Bill::where('transaction_currency_id', $currency->id)->count();
if ($bills > 0) {
Log::info(sprintf('Used in %d bills as currency, return true. ', $bills));
return true;
}
// is being used in recurring transactions
$recurringAmount = RecurrenceTransaction::where('transaction_currency_id', $currency->id)->count();
$recurringForeign = RecurrenceTransaction::where('foreign_currency_id', $currency->id)->count();
if ($recurringAmount > 0 || $recurringForeign > 0) {
Log::info(sprintf('Used in %d recurring transactions as (foreign) currency id, return true. ', $recurringAmount + $recurringForeign));
return true;
}
// is being used in accounts (as integer)
$meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int)$currency->id))->count();
if ($meta > 0) {
Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta));
return true;
}
// is being used in available budgets
$availableBudgets = AvailableBudget::where('transaction_currency_id', $currency->id)->count();
if ($availableBudgets > 0) {
Log::info(sprintf('Used in %d available budgets as currency, return true. ', $availableBudgets));
return true;
}
@ -98,7 +136,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
// is being used in budget limits
$budgetLimit = BudgetLimit::where('transaction_currency_id', $currency->id)->count();
if ($budgetLimit > 0) {
Log::debug(sprintf('Used in %d budget limits as currency, return true. ', $budgetLimit));
Log::info(sprintf('Used in %d budget limits as currency, return true. ', $budgetLimit));
return true;
}
@ -106,19 +144,19 @@ class CurrencyRepository implements CurrencyRepositoryInterface
// is the default currency for the user or the system
$defaultCode = app('preferences')->getForUser($this->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
if ($currency->code === $defaultCode) {
Log::debug('Is the default currency of the user, return true.');
Log::info('Is the default currency of the user, return true.');
return true;
}
// is the default currency for the system
$defaultSystemCode = config('firefly.default_currency', 'EUR');
$result = $currency->code === $defaultSystemCode;
if (true === $result) {
Log::debug('Is the default currency of the SYSTEM, return true.');
return true;
}
// $defaultSystemCode = config('firefly.default_currency', 'EUR');
// $result = $currency->code === $defaultSystemCode;
// if (true === $result) {
// Log::info('Is the default currency of the SYSTEM, return true.');
//
// return true;
// }
Log::debug('Currency is not used, return false.');
return false;

View File

@ -73,7 +73,7 @@ class ListLoginsRequest extends SpectreRequest
}
// sort logins by date created:
$sorted = $collection->sortByDesc(
function (Login $login) {
static function (Login $login) {
return $login->getUpdatedAt()->timestamp;
}
);

View File

@ -53,7 +53,6 @@ trait BasicDataSupport
unset($data[$entryId]);
}
}
return $data;
}

View File

@ -31,6 +31,7 @@ use Log;
/**
* Class AssetAccountMapper
* Can also handle liability accounts.
*/
class AssetAccountMapper
{
@ -41,6 +42,17 @@ class AssetAccountMapper
/** @var User */
private $user;
/** @var array */
private $types;
/**
* AssetAccountMapper constructor.
*/
public function __construct()
{
$this->types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
}
/**
* Based upon data in the importable, try to find or create the asset account account.
*
@ -56,12 +68,12 @@ class AssetAccountMapper
if ((int)$accountId > 0) {
// find asset account with this ID:
$result = $this->repository->findNull($accountId);
if (null !== $result && $result->accountType->type === AccountType::ASSET) {
Log::debug(sprintf('Found asset account "%s" based on given ID %d', $result->name, $accountId));
if (null !== $result && in_array($result->accountType->type, $this->types, true)) {
Log::debug(sprintf('Found %s "%s" based on given ID %d', $result->accountType->type, $result->name, $accountId));
return $result;
}
if (null !== $result && $result->accountType->type !== AccountType::ASSET) {
if (null !== $result && in_array($result->accountType->type, $this->types, true)) {
Log::warning(
sprintf('Found account "%s" based on given ID %d but its a %s, return nothing.', $result->name, $accountId, $result->accountType->type)
);
@ -76,8 +88,8 @@ class AssetAccountMapper
Log::debug(sprintf('Array does not contain a value for %s. Continue', $field));
continue;
}
$result = $this->repository->$function($value, [AccountType::ASSET]);
Log::debug(sprintf('Going to run %s() with argument "%s" (asset account)', $function, $value));
$result = $this->repository->$function($value, $this->types);
Log::debug(sprintf('Going to run %s() with argument "%s" (asset account or liability)', $function, $value));
if (null !== $result) {
Log::debug(sprintf('Found asset account "%s". Return it!', $result->name));

View File

@ -228,19 +228,12 @@ class ImportableConverter
{
$type = 'unknown';
if ($source === AccountType::ASSET && $destination === AccountType::ASSET) {
Log::debug('Source and destination are asset accounts. This is a transfer.');
$type = 'transfer';
}
if ($source === AccountType::REVENUE) {
Log::debug('Source is a revenue account. This is a deposit.');
$type = 'deposit';
}
if ($destination === AccountType::EXPENSE) {
Log::debug('Destination is an expense account. This is a withdrawal.');
$type = 'withdrawal';
}
$newType = config(sprintf('firefly.account_to_transaction.%s.%s', $source, $destination));
if (null !== $newType) {
Log::debug(sprintf('Source is %s, destination is %s, so this is a %s.', $source, $destination, $newType));
return (string)$newType;
}
return $type;
}

View File

@ -60,15 +60,19 @@ class OpposingAccountMapper
Log::debug(sprintf('Because amount is %s, will instead search for accounts of type %s', $amount, $expectedType));
}
// append expected types with liability types:
$expectedTypes = [$expectedType, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
if ((int)$accountId > 0) {
// find any account with this ID:
$result = $this->repository->findNull($accountId);
if (null !== $result && ($result->accountType->type === $expectedType || $result->accountType->type === AccountType::ASSET)) {
if (null !== $result && (in_array($result->accountType->type, $expectedTypes, true) || $result->accountType->type === AccountType::ASSET)) {
Log::debug(sprintf('Found account "%s" (%s) based on given ID %d. Return it!', $result->name, $result->accountType->type, $accountId));
return $result;
}
if (null !== $result && $result->accountType->type !== $expectedType) {
if (null !== $result && !in_array($result->accountType->type, $expectedTypes, true)) {
Log::warning(
sprintf(
'Found account "%s" (%s) based on given ID %d, but need a %s. Return nothing.', $result->name, $result->accountType->type, $accountId,
@ -90,7 +94,7 @@ class OpposingAccountMapper
}
// first search for $expectedType, then find asset:
$searchTypes = [$expectedType, AccountType::ASSET];
$searchTypes = [$expectedType, AccountType::ASSET, AccountType::DEBT, AccountType::MORTGAGE, AccountType::LOAN];
foreach ($searchTypes as $type) {
// find by (respectively):
// IBAN, accountNumber, name,

View File

@ -174,7 +174,9 @@ class StageImportDataHandler
}
$entry = [
'type' => $type,
// transaction data:
'transactions' => [
[
'date' => $transaction->getMadeOn()->format('Y-m-d'),
'tags' => $tags,
'user' => $this->importJob->user_id,
@ -190,13 +192,9 @@ class StageImportDataHandler
'bill_id' => null,
'bill_name' => null,
'original-source' => sprintf('spectre-v%s', config('firefly.version')),
// transaction data:
'transactions' => [
[
'type' => $type,
'currency_id' => null,
'currency_code' => $currencyCode,
'description' => null,
'amount' => $amount,
'budget_id' => null,
'budget_name' => null,

View File

@ -25,7 +25,6 @@ namespace FireflyIII\Support;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Fiscal\FiscalHelperInterface;
use Log;
/**
* Class Navigation.
@ -307,16 +306,15 @@ class Navigation
$displayFormat = (string)trans('config.year');
}
$begin = clone $start;
$entries = [];
while ($begin < $end) {
$formatted = $begin->format($format);
$displayed = $begin->formatLocalized($displayFormat);
$entries[$formatted] = $displayed;
$begin->$increment();
}
return $entries;
}

57796
public/v1/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -94,7 +94,7 @@
this.target = this.$refs.input;
let types = this.allowedTypes.join(',');
this.name = this.accountName;
this.accountAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/accounts?types=" + types + "&query=";
this.accountAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/accounts?types=" + types + "&search=";
this.triggerTransactionType();
},
@ -104,7 +104,7 @@
},
accountTypeFilters() {
let types = this.accountTypeFilters.join(',');
this.accountAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/accounts?types=" + types + "&query=";
this.accountAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/accounts?types=" + types + "&search=";
}
},
methods:

View File

@ -82,7 +82,7 @@
},
mounted() {
this.target = this.$refs.input;
this.categoryAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/categories?query=";
this.categoryAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/categories?search=";
},
methods: {
hasError: function () {

View File

@ -73,7 +73,7 @@
if (this.tag.length < 2) {
return;
}
const url = document.getElementsByTagName('base')[0].href + `json/tags?query=${this.tag}`;
const url = document.getElementsByTagName('base')[0].href + `json/tags?search=${this.tag}`;
clearTimeout(this.debounce);
this.debounce = setTimeout(() => {

View File

@ -849,6 +849,7 @@ return [
'bulk_edit' => 'Edit selected in bulk',
'mass_delete' => 'Delete selected',
'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.',
'cannot_change_amount_reconciled' => 'You can\'t change the amount of reconciled transactions.',
'no_budget' => '(no budget)',
'no_budget_squared' => '(no budget)',
'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious. You can delete part of a split transaction from this page, so take care.',

View File

@ -20,7 +20,7 @@
<div class="form-group">
<label for="query" class="col-sm-1 control-label">{{ 'search_query'|_ }}</label>
<div class="col-sm-10">
<input autocomplete="off" type="text" name="q" id="query" value="{{ fullQuery }}" class="form-control"
<input autocomplete="off" type="text" name="search" id="query" value="{{ fullQuery }}" class="form-control"
placeholder="{{ fullQuery }}">
</div>
</div>

View File

@ -99,6 +99,7 @@
{# TWO: WITHDRAWAL TO TRANSFER #}
{% if sourceType.type == 'Withdrawal' and destinationType.type == 'Transfer' %}
<a href="{{ route('accounts.show', [transaction.source_id]) }}"
title="{{ transaction.source_iban|default(transaction.source_name) }}">{{ transaction.source_name }}</a>
{# hide source in hidden input #}

View File

@ -17,7 +17,9 @@
<div class="box-body">
<p>
{{ 'cannot_edit_other_fields'|_ }}
<span class="text-danger">{{ 'cannot_change_amount_reconciled'|_ }}</span>
</p>
<table class="table table-striped table-condensed">
<tr>
<th class="">&nbsp;</th>
@ -45,9 +47,8 @@
</td>
{# AMOUNT #}
<td>
{% if journal.reconciled == false %}
<div class="input-group input-group-sm">
<span class="input-group-addon">{{ journal.currency_symbol }}</span>
<input name="amount[{{ journal.transaction_journal_id }}]" class="form-control" autocomplete="off"
step="any" type="number" value="{{ journal.amount }}">
@ -64,6 +65,29 @@
value="{{ journal.foreign_currency_id }}">
</div>
{% endif %}
{% endif %}
<span class="text-sm">
{% if journal.reconciled != false %}
{% if journal.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places) }})
{% endif %}
{% elseif journal.transaction_type_type == 'Transfer' %}
<span class="text-info">
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_symbol_decimal_places, false) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places, false) }})
{% endif %}
</span>
{% else %}
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places) }})
{% endif %}
{% endif %}
{% endif %}
</span>
</td>
<td>
{# DATE #}