mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-21 19:47:48 +00:00
Improve test coverage.
This commit is contained in:
@@ -59,7 +59,7 @@ fi
|
||||
#env $(grep -v "^\#" .env | xargs)
|
||||
php artisan cache:clear
|
||||
php artisan migrate --seed
|
||||
php artisan firefly:decrypt-all
|
||||
php artisan firefly-iii:decrypt-all
|
||||
|
||||
# upgrade database commands:
|
||||
php artisan firefly-iii:transaction-identifiers
|
||||
|
@@ -27,7 +27,6 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
@@ -74,7 +73,6 @@ class AccountController extends Controller
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function expenseOverview(DateRequest $request): JsonResponse
|
||||
{
|
||||
@@ -161,7 +159,6 @@ class AccountController extends Controller
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function overview(DateRequest $request): JsonResponse
|
||||
{
|
||||
@@ -224,7 +221,6 @@ class AccountController extends Controller
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function revenueOverview(DateRequest $request): JsonResponse
|
||||
{
|
||||
|
@@ -27,11 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -66,7 +64,6 @@ class CategoryController extends Controller
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function overview(DateRequest $request): JsonResponse
|
||||
{
|
||||
|
@@ -128,7 +128,7 @@ class CurrencyController extends Controller
|
||||
|
||||
// filter list on currency preference:
|
||||
$collection = $unfiltered->filter(
|
||||
function (Account $account) use ($currency, $accountRepository) {
|
||||
static function (Account $account) use ($currency, $accountRepository) {
|
||||
$currencyId = (int)$accountRepository->getMetaValue($account, 'currency_id');
|
||||
|
||||
return $currencyId === $currency->id;
|
||||
@@ -229,7 +229,7 @@ class CurrencyController extends Controller
|
||||
|
||||
// filter and paginate list:
|
||||
$collection = $unfiltered->filter(
|
||||
function (Bill $bill) use ($currency) {
|
||||
static function (Bill $bill) use ($currency) {
|
||||
return $bill->transaction_currency_id === $currency->id;
|
||||
}
|
||||
);
|
||||
@@ -505,7 +505,7 @@ class CurrencyController extends Controller
|
||||
|
||||
// filter selection
|
||||
$collection = $unfiltered->filter(
|
||||
function (Recurrence $recurrence) use ($currency) {
|
||||
static function (Recurrence $recurrence) use ($currency) {
|
||||
/** @var RecurrenceTransaction $transaction */
|
||||
foreach ($recurrence->recurrenceTransactions as $transaction) {
|
||||
if ($transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id) {
|
||||
@@ -560,7 +560,7 @@ class CurrencyController extends Controller
|
||||
$unfiltered = $repository->getAll();
|
||||
|
||||
$collection = $unfiltered->filter(
|
||||
function (Rule $rule) use ($currency) {
|
||||
static function (Rule $rule) use ($currency) {
|
||||
/** @var RuleTrigger $trigger */
|
||||
foreach ($rule->ruleTriggers as $trigger) {
|
||||
if ('currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value) {
|
||||
|
@@ -52,7 +52,7 @@ class PreferenceController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
static function ($request, $next) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
@@ -258,7 +258,8 @@ class RuleGroupController extends Controller
|
||||
$matcher->setTriggeredLimit($parameters['trigger_limit']);
|
||||
$matcher->setAccounts($parameters['accounts']);
|
||||
|
||||
$result = $matcher->findTransactionsByRule();
|
||||
$result = $matcher->findTransactionsByRule();
|
||||
/** @noinspection AdditionOperationOnArraysInspection */
|
||||
$matchingTransactions = $result + $matchingTransactions;
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,6 @@ namespace FireflyIII\Api\V1\Controllers;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Helpers\Report\NetWorthInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -41,7 +40,6 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -86,10 +84,9 @@ class SummaryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function basic(DateRequest $request): JsonResponse
|
||||
|
@@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Controllers;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Api\V1\Requests\TagRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
@@ -79,7 +78,6 @@ class TagController extends Controller
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function cloud(DateRequest $request): JsonResponse
|
||||
{
|
||||
|
@@ -105,7 +105,7 @@ class TransactionLinkController extends Controller
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
|
||||
// read type from URI
|
||||
$name = $request->get('name') ?? null;
|
||||
$name = $request->get('name');
|
||||
|
||||
// types to get, page size:
|
||||
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
|
@@ -56,7 +56,7 @@ class AccountStoreRequest extends Request
|
||||
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
|
||||
$rules = [
|
||||
'name' => 'required|min:1|uniqueAccountForUser',
|
||||
'type' => sprintf('in:%s', $types),
|
||||
'type' => 'required|' . sprintf('in:%s', $types),
|
||||
'iban' => 'iban|nullable',
|
||||
'bic' => 'bic|nullable',
|
||||
'account_number' => 'between:1,255|nullable|uniqueAccountNumberForUser',
|
||||
|
@@ -119,7 +119,7 @@ class BillRequest extends Request
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator) {
|
||||
static function (Validator $validator) {
|
||||
$data = $validator->getData();
|
||||
$min = (float)($data['amount_min'] ?? 0);
|
||||
$max = (float)($data['amount_max'] ?? 0);
|
||||
|
@@ -123,6 +123,7 @@ class RuleGroupTestRequest extends Request
|
||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||
$account = $accountRepository->findNull((int)$accountId);
|
||||
if ($this->validAccount($account)) {
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||
$accounts->push($account);
|
||||
}
|
||||
|
@@ -123,6 +123,7 @@ class RuleTestRequest extends Request
|
||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||
$account = $accountRepository->findNull((int)$accountId);
|
||||
if ($this->validAccount($account)) {
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||
$accounts->push($account);
|
||||
}
|
||||
|
@@ -98,6 +98,7 @@ class RuleTriggerRequest extends Request
|
||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||
$account = $accountRepository->findNull((int)$accountId);
|
||||
if ($this->validAccount($account)) {
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||
$accounts->push($account);
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ use Schema;
|
||||
|
||||
/**
|
||||
* Class CorrectDatabase
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CorrectDatabase extends Command
|
||||
{
|
||||
@@ -38,7 +39,7 @@ class CorrectDatabase extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Will correct the integrity of your database, of necessary.';
|
||||
protected $description = 'Will correct the integrity of your database, if necessary.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
|
@@ -22,6 +22,7 @@
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -35,7 +36,7 @@ class CreateAccessTokens extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Creates user access tokens.';
|
||||
protected $description = 'Creates user access tokens which are used for command line access to personal data.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
@@ -51,9 +52,13 @@ class CreateAccessTokens extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
// make repository:
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
|
||||
$start = microtime(true);
|
||||
$count = 0;
|
||||
$users = User::get();
|
||||
$users= $repository->all();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$pref = app('preferences')->getForUser($user, 'access_token', null);
|
||||
|
@@ -58,12 +58,13 @@ class CreateLinkTypes extends Command
|
||||
'Reimbursement' => ['(partially) reimburses', 'is (partially) reimbursed by'],
|
||||
];
|
||||
foreach ($set as $name => $values) {
|
||||
$link = LinkType::where('name', $name)->where('outward', $values[0])->where('inward', $values[1])->first();
|
||||
$link = LinkType::where('name', $name)
|
||||
->first();
|
||||
if (null === $link) {
|
||||
$link = new LinkType;
|
||||
$link->name = $name;
|
||||
$link->outward = $values[0];
|
||||
$link->inward = $values[1];
|
||||
$link->outward = $values[0];
|
||||
++$count;
|
||||
$this->line(sprintf('Created missing link type "%s"', $name));
|
||||
}
|
||||
|
@@ -59,7 +59,7 @@ class DeleteEmptyGroups extends Command
|
||||
$this->info('No empty transaction groups.');
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->info(sprintf('Deleted %d empty transaction groups.', $count));
|
||||
$this->info(sprintf('Deleted %d empty transaction group(s).', $count));
|
||||
TransactionGroup::whereNull('deleted_at')->whereNotIn('id', $groups)->delete();
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
|
@@ -72,9 +72,12 @@ class DeleteEmptyJournals extends Command
|
||||
foreach ($set as $entry) {
|
||||
try {
|
||||
TransactionJournal::find($entry->id)->delete();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $e) {
|
||||
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$this->info(sprintf('Deleted empty transaction journal #%d', $entry->id));
|
||||
++$count;
|
||||
}
|
||||
@@ -90,15 +93,6 @@ class DeleteEmptyJournals extends Command
|
||||
*/
|
||||
private function deleteUnevenJournals(): void
|
||||
{
|
||||
/**
|
||||
* select count(transactions.transaction_journal_id) as the_count, transactions.transaction_journal_id from transactions
|
||||
*
|
||||
* where transactions.deleted_at is null
|
||||
*
|
||||
* group by transactions.transaction_journal_id
|
||||
* having the_count in ()
|
||||
*/
|
||||
|
||||
$set = Transaction
|
||||
::whereNull('deleted_at')
|
||||
->having('the_count', '!=', '2')
|
||||
@@ -111,9 +105,12 @@ class DeleteEmptyJournals extends Command
|
||||
// uneven number, delete journal and transactions:
|
||||
try {
|
||||
TransactionJournal::find((int)$row->transaction_journal_id)->delete();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch(Exception $e) {
|
||||
Log::info(sprintf('Could not delete journal: %s', $e->getMessage()));
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete();
|
||||
$this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id));
|
||||
$total++;
|
||||
|
@@ -25,8 +25,8 @@ use Exception;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use stdClass;
|
||||
use Log;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Deletes transactions where the journal has been deleted.
|
||||
@@ -81,17 +81,20 @@ class DeleteOrphanedTransactions extends Command
|
||||
if ($journal) {
|
||||
try {
|
||||
$journal->delete();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $e) {
|
||||
Log::info(sprintf('Could not delete transaction %s', $e->getMessage()));
|
||||
Log::info(sprintf('Could not delete journal %s', $e->getMessage()));
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete();
|
||||
$this->line(
|
||||
sprintf('Deleted transaction journal #%d because account #%d was already deleted.', $transaction->transaction_journal_id, $transaction->account_id)
|
||||
sprintf('Deleted transaction journal #%d because account #%d was already deleted.',
|
||||
$transaction->transaction_journal_id, $transaction->account_id)
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
if(0===$count) {
|
||||
if (0 === $count) {
|
||||
$this->info('No orphaned accounts.');
|
||||
}
|
||||
}
|
||||
|
@@ -47,7 +47,6 @@ class DeleteZeroAmount extends Command
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* @throws Exception
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
@@ -60,7 +59,13 @@ class DeleteZeroAmount extends Command
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->info(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id));
|
||||
$journal->delete();
|
||||
try {
|
||||
$journal->delete();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $e) {
|
||||
$this->line($e->getMessage());
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
Transaction::where('transaction_journal_id', $journal->id)->delete();
|
||||
}
|
||||
if (0 === $journals->count()) {
|
||||
|
@@ -86,9 +86,13 @@ class EnableCurrencies extends Command
|
||||
|
||||
$found = array_unique($found);
|
||||
$this->info(sprintf('%d different currencies are currently in use.', count($found)));
|
||||
|
||||
$disabled = TransactionCurrency::whereIn('id', $found)->where('enabled', false)->count();
|
||||
if ($disabled > 0) {
|
||||
$this->info(sprintf('%d were still disabled. This has been corrected.', $disabled));
|
||||
$this->info(sprintf('%d were (was) still disabled. This has been corrected.', $disabled));
|
||||
}
|
||||
if (0 === $disabled) {
|
||||
$this->info('All currencies are correctly enabled or disabled.');
|
||||
}
|
||||
TransactionCurrency::whereIn('id', $found)->update(['enabled' => true]);
|
||||
|
||||
|
@@ -52,82 +52,16 @@ class FixAccountTypes extends Command
|
||||
private $factory;
|
||||
/** @var array */
|
||||
private $fixable;
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $type
|
||||
* @param Transaction $source
|
||||
* @param Transaction $dest
|
||||
* @throws FireflyException
|
||||
* FixAccountTypes constructor.
|
||||
*/
|
||||
public function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void
|
||||
public function __construct()
|
||||
{
|
||||
// variables:
|
||||
$combination = sprintf('%s%s%s', $type, $source->account->accountType->type, $dest->account->accountType->type);
|
||||
|
||||
switch ($combination) {
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::LOAN):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::DEBT):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::MORTGAGE):
|
||||
// from an asset to a liability should be a withdrawal:
|
||||
$withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
|
||||
$journal->transactionType()->associate($withdrawal);
|
||||
$journal->save();
|
||||
$this->info(sprintf('Converted transaction #%d from a transfer to a withdrawal', $journal->id));
|
||||
// check it again:
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::LOAN, AccountType::ASSET):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::DEBT, AccountType::ASSET):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::MORTGAGE, AccountType::ASSET):
|
||||
// from a liability to an asset should be a deposit.
|
||||
$deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first();
|
||||
$journal->transactionType()->associate($deposit);
|
||||
$journal->save();
|
||||
$this->info(sprintf('Converted transaction #%d from a transfer to a deposit', $journal->id));
|
||||
// check it again:
|
||||
$this->inspectJournal($journal);
|
||||
|
||||
break;
|
||||
case sprintf('%s%s%s', TransactionType::WITHDRAWAL, AccountType::ASSET, AccountType::REVENUE):
|
||||
// withdrawals with a revenue account as destination instead of an expense account.
|
||||
$this->factory->setUser($journal->user);
|
||||
$result = $this->factory->findOrCreate($source->account->name, AccountType::EXPENSE);
|
||||
$dest->account()->associate($result);
|
||||
$dest->save();
|
||||
$this->info(
|
||||
sprintf(
|
||||
'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id,
|
||||
$source->account->id, $source->account->name,
|
||||
$result->id, $result->name
|
||||
)
|
||||
);
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
case sprintf('%s%s%s', TransactionType::DEPOSIT, AccountType::EXPENSE, AccountType::ASSET):
|
||||
// deposits with an expense account as source instead of a revenue account.
|
||||
// find revenue account.
|
||||
$this->factory->setUser($journal->user);
|
||||
$result = $this->factory->findOrCreate($source->account->name, AccountType::REVENUE);
|
||||
$source->account()->associate($result);
|
||||
$source->save();
|
||||
$this->info(
|
||||
sprintf(
|
||||
'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id,
|
||||
$source->account->id, $source->account->name,
|
||||
$result->id, $result->name
|
||||
)
|
||||
);
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
$this->info(sprintf('The source account of %s #%d cannot be of type "%s".', $type, $journal->id, $source->account->accountType->type));
|
||||
$this->info(sprintf('The destination account of %s #%d cannot be of type "%s".', $type, $journal->id, $dest->account->accountType->type));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,19 +93,103 @@ class FixAccountTypes extends Command
|
||||
|
||||
|
||||
$this->expected = config('firefly.source_dests');
|
||||
$journals = TransactionJournal
|
||||
|
||||
::with(['TransactionType', 'transactions', 'transactions.account', 'transactions.account.accounttype'])->get();
|
||||
$journals = TransactionJournal::with(['TransactionType', 'transactions', 'transactions.account', 'transactions.account.accounttype'])->get();
|
||||
foreach ($journals as $journal) {
|
||||
$this->inspectJournal($journal);
|
||||
}
|
||||
if (0 === $this->count) {
|
||||
$this->info('All account types are OK!');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->info(sprintf('Acted on %d transaction(s)!', $this->count));
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified account types in %s seconds', $end));
|
||||
$this->info(sprintf('Verifying account types took %s seconds', $end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $type
|
||||
* @param Transaction $source
|
||||
* @param Transaction $dest
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void
|
||||
{
|
||||
$this->count++;
|
||||
// variables:
|
||||
$combination = sprintf('%s%s%s', $type, $source->account->accountType->type, $dest->account->accountType->type);
|
||||
|
||||
switch ($combination) {
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::LOAN):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::DEBT):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::MORTGAGE):
|
||||
// from an asset to a liability should be a withdrawal:
|
||||
$withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
|
||||
$journal->transactionType()->associate($withdrawal);
|
||||
$journal->save();
|
||||
$this->info(sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id));
|
||||
// check it again:
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::LOAN, AccountType::ASSET):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::DEBT, AccountType::ASSET):
|
||||
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::MORTGAGE, AccountType::ASSET):
|
||||
// from a liability to an asset should be a deposit.
|
||||
$deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first();
|
||||
$journal->transactionType()->associate($deposit);
|
||||
$journal->save();
|
||||
$this->info(sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id));
|
||||
// check it again:
|
||||
$this->inspectJournal($journal);
|
||||
|
||||
break;
|
||||
case sprintf('%s%s%s', TransactionType::WITHDRAWAL, AccountType::ASSET, AccountType::REVENUE):
|
||||
// withdrawals with a revenue account as destination instead of an expense account.
|
||||
$this->factory->setUser($journal->user);
|
||||
$oldDest = $dest->account;
|
||||
$result = $this->factory->findOrCreate($dest->account->name, AccountType::EXPENSE);
|
||||
$dest->account()->associate($result);
|
||||
$dest->save();
|
||||
$this->info(
|
||||
sprintf(
|
||||
'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id,
|
||||
$oldDest->id, $oldDest->name,
|
||||
$result->id, $result->name
|
||||
)
|
||||
);
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
case sprintf('%s%s%s', TransactionType::DEPOSIT, AccountType::EXPENSE, AccountType::ASSET):
|
||||
// deposits with an expense account as source instead of a revenue account.
|
||||
// find revenue account.
|
||||
$this->factory->setUser($journal->user);
|
||||
$result = $this->factory->findOrCreate($source->account->name, AccountType::REVENUE);
|
||||
$oldSource = $dest->account;
|
||||
$source->account()->associate($result);
|
||||
$source->save();
|
||||
$this->info(
|
||||
sprintf(
|
||||
'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id,
|
||||
$oldSource->id, $oldSource->name,
|
||||
$result->id, $result->name
|
||||
)
|
||||
);
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
default:
|
||||
$this->info(sprintf('The source account of %s #%d cannot be of type "%s".', $type, $journal->id, $source->account->accountType->type));
|
||||
$this->info(sprintf('The destination account of %s #%d cannot be of type "%s".', $type, $journal->id, $dest->account->accountType->type));
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
@@ -201,7 +219,7 @@ class FixAccountTypes extends Command
|
||||
{
|
||||
$count = $journal->transactions()->count();
|
||||
if (2 !== $count) {
|
||||
$this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transactions instead of 2.', $journal->id, $count));
|
||||
$this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $count));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -213,9 +231,11 @@ class FixAccountTypes extends Command
|
||||
$destAccount = $destTransaction->account;
|
||||
$destAccountType = $destAccount->accountType->type;
|
||||
if (!isset($this->expected[$type])) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->info(sprintf('No source/destination info for transaction type %s.', $type));
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
if (!isset($this->expected[$type][$sourceAccountType])) {
|
||||
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
|
||||
|
@@ -46,6 +46,9 @@ class FixPiggies extends Command
|
||||
*/
|
||||
protected $signature = 'firefly-iii:fix-piggies';
|
||||
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
@@ -53,29 +56,43 @@ class FixPiggies extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
|
||||
$set->each(
|
||||
function (PiggyBankEvent $event) {
|
||||
if (null === $event->transaction_journal_id) {
|
||||
return true;
|
||||
}
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $event->transactionJournal()->first();
|
||||
if (null === $journal) {
|
||||
return true;
|
||||
}
|
||||
$this->count = 0;
|
||||
$start = microtime(true);
|
||||
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
|
||||
|
||||
$type = $journal->transactionType->type;
|
||||
if (TransactionType::TRANSFER !== $type) {
|
||||
$event->transaction_journal_id = null;
|
||||
$event->save();
|
||||
$this->line(sprintf('Piggy bank #%d was referenced by an invalid event. This has been fixed.', $event->piggy_bank_id));
|
||||
}
|
||||
/** @var PiggyBankEvent $event */
|
||||
foreach ($set as $event) {
|
||||
|
||||
return true;
|
||||
if (null === $event->transaction_journal_id) {
|
||||
continue;
|
||||
}
|
||||
);
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $event->transactionJournal;
|
||||
// @codeCoverageIgnoreStart
|
||||
if (null === $journal) {
|
||||
$event->transaction_journal_id = null;
|
||||
$event->save();
|
||||
$this->count++;
|
||||
continue;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$type = $journal->transactionType->type;
|
||||
if (TransactionType::TRANSFER !== $type) {
|
||||
$event->transaction_journal_id = null;
|
||||
$event->save();
|
||||
$this->line(sprintf('Piggy bank #%d was referenced by an invalid event. This has been fixed.', $event->piggy_bank_id));
|
||||
$this->count++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (0 === $this->count) {
|
||||
$this->line('All piggy bank events are correct.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Fixed %d piggy bank event(s).', $this->count));
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->line(sprintf('Verified the content of %d piggy bank events in %s seconds.', $set->count(), $end));
|
||||
|
||||
|
@@ -22,6 +22,7 @@
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -84,7 +85,7 @@ class FixUnevenAmount extends Command
|
||||
// one of the transactions is bad.
|
||||
$journal = TransactionJournal::find($param);
|
||||
if (!$journal) {
|
||||
return;
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
/** @var Transaction $source */
|
||||
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
||||
@@ -92,11 +93,11 @@ class FixUnevenAmount extends Command
|
||||
|
||||
// fix amount of destination:
|
||||
/** @var Transaction $destination */
|
||||
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
||||
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
||||
$destination->amount = $amount;
|
||||
$destination->save();
|
||||
|
||||
$this->line(sprintf('Corrected amount in transaction journal #%d', $param));
|
||||
|
||||
$message = sprintf('Corrected amount in transaction journal #%d', $param);
|
||||
$this->line($message);
|
||||
}
|
||||
}
|
||||
|
@@ -42,6 +42,9 @@ class RenameMetaFields extends Command
|
||||
*/
|
||||
protected $signature = 'firefly-iii:rename-meta-fields';
|
||||
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
@@ -49,7 +52,8 @@ class RenameMetaFields extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
$this->count = 0;
|
||||
$start = microtime(true);
|
||||
|
||||
$changes = [
|
||||
'original-source' => 'original_source',
|
||||
@@ -67,6 +71,12 @@ class RenameMetaFields extends Command
|
||||
foreach ($changes as $original => $update) {
|
||||
$this->rename($original, $update);
|
||||
}
|
||||
if (0 === $this->count) {
|
||||
$this->line('All meta fields are correct.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Renamed %d meta field(s).', $this->count));
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Renamed meta fields in %s seconds', $end));
|
||||
@@ -80,8 +90,9 @@ class RenameMetaFields extends Command
|
||||
*/
|
||||
private function rename(string $original, string $update): void
|
||||
{
|
||||
DB::table('journal_meta')
|
||||
->where('name', '=', $original)
|
||||
->update(['name' => $update]);
|
||||
$count = DB::table('journal_meta')
|
||||
->where('name', '=', $original)
|
||||
->update(['name' => $update]);
|
||||
$this->count += $count;
|
||||
}
|
||||
}
|
||||
|
@@ -66,6 +66,9 @@ class TransferBudgets extends Command
|
||||
if (0 === $count) {
|
||||
$this->info('No invalid budget/journal entries.');
|
||||
}
|
||||
if(0 !== $count) {
|
||||
$this->line(sprintf('Corrected %d invalid budget/journal entries (entry).', $count));
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified budget/journals in %s seconds.', $end));
|
||||
|
||||
|
@@ -49,15 +49,15 @@ class DecryptDatabase extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly:decrypt-all';
|
||||
protected $signature = 'firefly-iii:decrypt-all';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle()
|
||||
public function handle(): int
|
||||
{
|
||||
$this->line('Going to decrypt the database.');
|
||||
$tables = [
|
||||
@@ -151,7 +151,7 @@ class DecryptDatabase extends Command
|
||||
$value = Crypt::decrypt($value);
|
||||
} catch (DecryptException $e) {
|
||||
if ('The MAC is invalid.' === $e->getMessage()) {
|
||||
throw new FireflyException($e->getMessage());
|
||||
throw new FireflyException($e->getMessage()); // @codeCoverageIgnore
|
||||
}
|
||||
Log::debug(sprintf('Could not decrypt. %s', $e->getMessage()));
|
||||
}
|
||||
|
@@ -28,18 +28,17 @@ namespace FireflyIII\Console\Commands\Import;
|
||||
use Exception;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
|
||||
use FireflyIII\Import\Routine\RoutineInterface;
|
||||
use FireflyIII\Import\Storage\ImportArrayStorage;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class CreateCSVImport.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CreateCSVImport extends Command
|
||||
{
|
||||
@@ -50,7 +49,6 @@ class CreateCSVImport extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Use this command to create a new CSV file import.';
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
@@ -62,169 +60,96 @@ class CreateCSVImport extends Command
|
||||
{configuration? : The configuration file to use for the import.}
|
||||
{--user=1 : The user ID that the import should import for.}
|
||||
{--token= : The user\'s access token.}';
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepository;
|
||||
/** @var ImportJobRepositoryInterface */
|
||||
private $importRepository;
|
||||
/** @var ImportJob */
|
||||
private $importJob;
|
||||
|
||||
/**
|
||||
* CreateCSVImport constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->userRepository = app(UserRepositoryInterface::class);
|
||||
$this->importRepository = app(ImportJobRepositoryInterface::class);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the command.
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->errorLine('Invalid access token.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
/** @var UserRepositoryInterface $userRepository */
|
||||
$userRepository = app(UserRepositoryInterface::class);
|
||||
$file = (string)$this->argument('file');
|
||||
$configuration = (string)$this->argument('configuration');
|
||||
$user = $userRepository->findNull((int)$this->option('user'));
|
||||
$cwd = getcwd();
|
||||
$configurationData = [];
|
||||
|
||||
if (null === $user) {
|
||||
$this->errorLine('User is NULL.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!$this->validArguments()) {
|
||||
$this->errorLine('Invalid arguments.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
if ('' !== $configuration) {
|
||||
$configurationData = json_decode(file_get_contents($configuration), true);
|
||||
if (null === $configurationData) {
|
||||
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
|
||||
// @codeCoverageIgnoreEnd
|
||||
/** @var User $user */
|
||||
$user = $this->userRepository->findNull((int)$this->option('user'));
|
||||
$file = (string)$this->argument('file');
|
||||
$configuration = (string)$this->argument('configuration');
|
||||
|
||||
return 1;
|
||||
}
|
||||
$this->importRepository->setUser($user);
|
||||
|
||||
$configurationData = json_decode(file_get_contents($configuration), true);
|
||||
$this->importJob = $this->importRepository->create('file');
|
||||
|
||||
|
||||
// inform user (and log it)
|
||||
$this->infoLine(sprintf('Import file : %s', $file));
|
||||
$this->infoLine(sprintf('Configuration file : %s', $configuration));
|
||||
$this->infoLine(sprintf('User : #%d (%s)', $user->id, $user->email));
|
||||
$this->infoLine(sprintf('Job : %s', $this->importJob->key));
|
||||
|
||||
try {
|
||||
$this->storeFile($file);
|
||||
} catch (FireflyException $e) {
|
||||
$this->errorLine($e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
$this->infoLine(sprintf('Going to create a job to import file: %s', $file));
|
||||
$this->infoLine(sprintf('Using configuration file: %s', $configuration));
|
||||
$this->infoLine(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
|
||||
|
||||
/** @var ImportJobRepositoryInterface $jobRepository */
|
||||
$jobRepository = app(ImportJobRepositoryInterface::class);
|
||||
$jobRepository->setUser($user);
|
||||
$importJob = $jobRepository->create('file');
|
||||
$this->infoLine(sprintf('Created job "%s"', $importJob->key));
|
||||
|
||||
// make sure that job has no prerequisites.
|
||||
if ((bool)config('import.has_prereq.csv')) {
|
||||
// make prerequisites thing.
|
||||
$class = (string)config('import.prerequisites.csv');
|
||||
if (!class_exists($class)) {
|
||||
throw new FireflyException('No class to handle prerequisites for CSV.'); // @codeCoverageIgnore
|
||||
}
|
||||
/** @var PrerequisitesInterface $object */
|
||||
$object = app($class);
|
||||
$object->setUser($user);
|
||||
if (!$object->isComplete()) {
|
||||
$this->errorLine('CSV Import provider has prerequisites that can only be filled in using the browser.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// store file as attachment.
|
||||
if ('' !== $file) {
|
||||
$messages = $jobRepository->storeCLIUpload($importJob, 'import_file', $file);
|
||||
if ($messages->count() > 0) {
|
||||
$this->errorLine($messages->first());
|
||||
|
||||
return 1;
|
||||
}
|
||||
$this->infoLine('File content saved.');
|
||||
}
|
||||
|
||||
$this->infoLine('Job configuration saved.');
|
||||
$jobRepository->setConfiguration($importJob, $configurationData);
|
||||
$jobRepository->setStatus($importJob, 'ready_to_run');
|
||||
|
||||
// job is ready to go
|
||||
$this->importRepository->setConfiguration($this->importJob, $configurationData);
|
||||
$this->importRepository->setStatus($this->importJob, 'ready_to_run');
|
||||
|
||||
$this->infoLine('The import routine has started. The process is not visible. Please wait.');
|
||||
Log::debug('Go for import!');
|
||||
|
||||
// run it!
|
||||
$className = config('import.routine.file');
|
||||
if (null === $className || !class_exists($className)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->errorLine('No routine for file provider.');
|
||||
|
||||
return 1;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// keep repeating this call until job lands on "provider_finished"
|
||||
$valid = ['provider_finished'];
|
||||
$count = 0;
|
||||
while (!in_array($importJob->status, $valid, true) && $count < 6) {
|
||||
Log::debug(sprintf('Now in loop #%d.', $count + 1));
|
||||
/** @var RoutineInterface $routine */
|
||||
$routine = app($className);
|
||||
$routine->setImportJob($importJob);
|
||||
try {
|
||||
$routine->run();
|
||||
} catch (FireflyException|Exception $e) {
|
||||
$message = 'The import routine crashed: ' . $e->getMessage();
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
try {
|
||||
$this->processFile();
|
||||
} catch (FireflyException $e) {
|
||||
$this->errorLine($e->getMessage());
|
||||
|
||||
// set job errored out:
|
||||
$jobRepository->setStatus($importJob, 'error');
|
||||
$this->errorLine($message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
$count++;
|
||||
return 1;
|
||||
}
|
||||
if ('provider_finished' === $importJob->status) {
|
||||
$this->infoLine('Import has finished. Please wait for storage of data.');
|
||||
// set job to be storing data:
|
||||
$jobRepository->setStatus($importJob, 'storing_data');
|
||||
|
||||
/** @var ImportArrayStorage $storage */
|
||||
$storage = app(ImportArrayStorage::class);
|
||||
$storage->setImportJob($importJob);
|
||||
// then store data:
|
||||
try {
|
||||
$this->storeData();
|
||||
} catch (FireflyException $e) {
|
||||
$this->errorLine($e->getMessage());
|
||||
|
||||
try {
|
||||
$storage->store();
|
||||
} catch (FireflyException|Exception $e) {
|
||||
$message = 'The import routine crashed: ' . $e->getMessage();
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
|
||||
// set job errored out:
|
||||
$jobRepository->setStatus($importJob, 'error');
|
||||
$this->errorLine($message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
// set storage to be finished:
|
||||
$jobRepository->setStatus($importJob, 'storage_finished');
|
||||
return 1;
|
||||
}
|
||||
|
||||
// give feedback:
|
||||
$this->infoLine('Job has finished.');
|
||||
if (null !== $importJob->tag) {
|
||||
$this->infoLine(sprintf('%d transaction(s) have been imported.', $importJob->tag->transactionJournals->count()));
|
||||
$this->infoLine(sprintf('You can find your transactions under tag "%s"', $importJob->tag->tag));
|
||||
}
|
||||
$this->giveFeedback();
|
||||
|
||||
if (null === $importJob->tag) {
|
||||
$this->errorLine('No transactions have been imported :(.');
|
||||
}
|
||||
if (count($importJob->errors) > 0) {
|
||||
$this->infoLine(sprintf('%d error(s) occurred:', count($importJob->errors)));
|
||||
foreach ($importJob->errors as $err) {
|
||||
$this->errorLine('- ' . $err);
|
||||
}
|
||||
}
|
||||
// clear cache for user:
|
||||
app('preferences')->setForUser($user, 'lastActivity', microtime());
|
||||
|
||||
@@ -234,6 +159,7 @@ class CreateCSVImport extends Command
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array|null $data
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function errorLine(string $message, array $data = null): void
|
||||
{
|
||||
@@ -245,6 +171,7 @@ class CreateCSVImport extends Command
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $data
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function infoLine(string $message, array $data = null): void
|
||||
{
|
||||
@@ -257,6 +184,7 @@ class CreateCSVImport extends Command
|
||||
*
|
||||
* @noinspection MultipleReturnStatementsInspection
|
||||
* @return bool
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function validArguments(): bool
|
||||
{
|
||||
@@ -283,6 +211,119 @@ class CreateCSVImport extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
$configurationData = json_decode(file_get_contents($configuration), true);
|
||||
if (null === $configurationData) {
|
||||
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the supplied file as an attachment to this job.
|
||||
*
|
||||
* @param string $file
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function storeFile(string $file): void
|
||||
{
|
||||
// store file as attachment.
|
||||
if ('' !== $file) {
|
||||
$messages = $this->importRepository->storeCLIUpload($this->importJob, 'import_file', $file);
|
||||
if ($messages->count() > 0) {
|
||||
throw new FireflyException($messages->first());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep repeating import call until job lands on "provider_finished".
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function processFile(): void
|
||||
{
|
||||
$className = config('import.routine.file');
|
||||
$valid = ['provider_finished'];
|
||||
$count = 0;
|
||||
|
||||
while (!in_array($this->importJob->status, $valid, true) && $count < 6) {
|
||||
Log::debug(sprintf('Now in loop #%d.', $count + 1));
|
||||
/** @var RoutineInterface $routine */
|
||||
$routine = app($className);
|
||||
$routine->setImportJob($this->importJob);
|
||||
try {
|
||||
$routine->run();
|
||||
} catch (FireflyException|Exception $e) {
|
||||
$message = 'The import routine crashed: ' . $e->getMessage();
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
|
||||
// set job errored out:
|
||||
$this->importRepository->setStatus($this->importJob, 'error');
|
||||
throw new FireflyException($message);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$this->importRepository->setStatus($this->importJob, 'provider_finished');
|
||||
$this->importJob->status = 'provider_finished';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function storeData(): void
|
||||
{
|
||||
if ('provider_finished' === $this->importJob->status) {
|
||||
$this->infoLine('Import has finished. Please wait for storage of data.');
|
||||
// set job to be storing data:
|
||||
$this->importRepository->setStatus($this->importJob, 'storing_data');
|
||||
|
||||
/** @var ImportArrayStorage $storage */
|
||||
$storage = app(ImportArrayStorage::class);
|
||||
$storage->setImportJob($this->importJob);
|
||||
|
||||
try {
|
||||
$storage->store();
|
||||
} catch (FireflyException|Exception $e) {
|
||||
$message = 'The import routine crashed: ' . $e->getMessage();
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
|
||||
// set job errored out:
|
||||
$this->importRepository->setStatus($this->importJob, 'error');
|
||||
throw new FireflyException($message);
|
||||
|
||||
}
|
||||
// set storage to be finished:
|
||||
$this->importRepository->setStatus($this->importJob, 'storage_finished');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function giveFeedback(): void
|
||||
{
|
||||
$this->infoLine('Job has finished.');
|
||||
|
||||
|
||||
if (null !== $this->importJob->tag) {
|
||||
$this->infoLine(sprintf('%d transaction(s) have been imported.', $this->importJob->tag->transactionJournals->count()));
|
||||
$this->infoLine(sprintf('You can find your transactions under tag "%s"', $this->importJob->tag->tag));
|
||||
}
|
||||
|
||||
if (null === $this->importJob->tag) {
|
||||
$this->errorLine('No transactions have been imported :(.');
|
||||
}
|
||||
if (count($this->importJob->errors) > 0) {
|
||||
$this->infoLine(sprintf('%d error(s) occurred:', count($this->importJob->errors)));
|
||||
foreach ($this->importJob->errors as $err) {
|
||||
$this->errorLine('- ' . $err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -80,9 +80,8 @@ class ReportEmptyObjects extends Command
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$name = $entry->name;
|
||||
$line = 'User #%d (%s) has account #%d ("%s") which has no transactions.';
|
||||
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $name);
|
||||
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
@@ -125,13 +124,12 @@ class ReportEmptyObjects extends Command
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$objName = $entry->name;
|
||||
$line = sprintf(
|
||||
'User #%d (%s) has budget #%d ("%s") which has no transaction journals.',
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->id,
|
||||
$objName
|
||||
$entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
@@ -151,14 +149,12 @@ class ReportEmptyObjects extends Command
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$objName = $entry->name;
|
||||
|
||||
$line = sprintf(
|
||||
'User #%d (%s) has category #%d ("%s") which has no transaction journals.',
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->id,
|
||||
$objName
|
||||
$entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
@@ -178,14 +174,13 @@ class ReportEmptyObjects extends Command
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$objName = $entry->tag;
|
||||
|
||||
$line = sprintf(
|
||||
'User #%d (%s) has tag #%d ("%s") which has no transaction journals.',
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->id,
|
||||
$objName
|
||||
$entry->tag
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ use Artisan;
|
||||
|
||||
/**
|
||||
* Class ReportIntegrity
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ReportIntegrity extends Command
|
||||
{
|
||||
|
@@ -75,8 +75,6 @@ class ApplyRules extends Command
|
||||
private $acceptedAccounts;
|
||||
/** @var Carbon */
|
||||
private $endDate;
|
||||
/** @var Collection */
|
||||
private $results;
|
||||
/** @var array */
|
||||
private $ruleGroupSelection;
|
||||
/** @var array */
|
||||
@@ -106,7 +104,6 @@ class ApplyRules extends Command
|
||||
$this->accounts = new Collection;
|
||||
$this->ruleSelection = [];
|
||||
$this->ruleGroupSelection = [];
|
||||
$this->results = new Collection;
|
||||
$this->ruleRepository = app(RuleRepositoryInterface::class);
|
||||
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->acceptedAccounts = [AccountType::DEFAULT, AccountType::DEBT, AccountType::ASSET, AccountType::LOAN, AccountType::MORTGAGE];
|
||||
@@ -121,11 +118,14 @@ class ApplyRules extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->error('Invalid access token.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
// set user:
|
||||
$this->ruleRepository->setUser($this->getUser());
|
||||
$this->ruleGroupRepository->setUser($this->getUser());
|
||||
@@ -141,24 +141,8 @@ class ApplyRules extends Command
|
||||
$this->grabAllRules();
|
||||
|
||||
// loop all groups and rules and indicate if they're included:
|
||||
$count = 0;
|
||||
$rulesToApply = [];
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($this->groups as $group) {
|
||||
/** @var Rule $rule */
|
||||
foreach ($group->rules as $rule) {
|
||||
// if in rule selection, or group in selection or all rules, it's included.
|
||||
$test = $this->includeRule($rule, $group);
|
||||
if (true === $test) {
|
||||
Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title));
|
||||
$count++;
|
||||
$rulesToApply[] = $rule->id;
|
||||
}
|
||||
if (false === $test) {
|
||||
Log::debug(sprintf('Will not include rule #%d "%s"', $rule->id, $rule->title));
|
||||
}
|
||||
}
|
||||
}
|
||||
$rulesToApply = $this->getRulesToApply();
|
||||
$count = count($rulesToApply);
|
||||
if (0 === $count) {
|
||||
$this->error('No rules or rule groups have been included.');
|
||||
$this->warn('Make a selection using:');
|
||||
@@ -167,7 +151,6 @@ class ApplyRules extends Command
|
||||
$this->warn(' --all_rules');
|
||||
}
|
||||
|
||||
// get transactions from asset accounts.
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->getUser());
|
||||
@@ -176,7 +159,7 @@ class ApplyRules extends Command
|
||||
$journals = $collector->getExtractedJournals();
|
||||
|
||||
// start running rules.
|
||||
$this->line(sprintf('Will apply %d rules to %d transactions.', $count, count($journals)));
|
||||
$this->line(sprintf('Will apply %d rule(s) to %d transaction(s).', $count, count($journals)));
|
||||
|
||||
// start looping.
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
@@ -191,6 +174,7 @@ class ApplyRules extends Command
|
||||
Log::debug('Start of new journal.');
|
||||
$ruleEngine->processJournalArray($journal);
|
||||
Log::debug('Done with all rules for this group + done with journal.');
|
||||
/** @noinspection DisconnectedForeachInstructionInspection */
|
||||
$bar->advance();
|
||||
}
|
||||
$this->line('');
|
||||
@@ -212,16 +196,10 @@ class ApplyRules extends Command
|
||||
}
|
||||
|
||||
// verify rule groups.
|
||||
$result = $this->verifyInputRuleGroups();
|
||||
if (false === $result) {
|
||||
return $result;
|
||||
}
|
||||
$this->verifyInputRuleGroups();
|
||||
|
||||
// verify rules.
|
||||
$result = $this->verifyInputRules();
|
||||
if (false === $result) {
|
||||
return $result;
|
||||
}
|
||||
$this->verifyInputRules();
|
||||
|
||||
$this->verifyInputDates();
|
||||
|
||||
@@ -243,11 +221,13 @@ class ApplyRules extends Command
|
||||
$finalList = new Collection;
|
||||
$accountList = explode(',', $accountString);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (0 === count($accountList)) {
|
||||
$this->error('Please use the --accounts option to indicate the accounts to apply rules to.');
|
||||
|
||||
return false;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
@@ -284,11 +264,12 @@ class ApplyRules extends Command
|
||||
return true;
|
||||
}
|
||||
$ruleGroupList = explode(',', $ruleGroupString);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (0 === count($ruleGroupList)) {
|
||||
// can be empty.
|
||||
return true;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
foreach ($ruleGroupList as $ruleGroupId) {
|
||||
$ruleGroup = $this->ruleGroupRepository->find((int)$ruleGroupId);
|
||||
if ($ruleGroup->active) {
|
||||
@@ -314,11 +295,14 @@ class ApplyRules extends Command
|
||||
}
|
||||
$ruleList = explode(',', $ruleString);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (0 === count($ruleList)) {
|
||||
// can be empty.
|
||||
|
||||
return true;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
foreach ($ruleList as $ruleId) {
|
||||
$rule = $this->ruleRepository->find((int)$ruleId);
|
||||
if (null !== $rule && $rule->active) {
|
||||
@@ -383,4 +367,27 @@ class ApplyRules extends Command
|
||||
in_array($rule->id, $this->ruleSelection, true) ||
|
||||
$this->allRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getRulesToApply(): array
|
||||
{
|
||||
$rulesToApply = [];
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($this->groups as $group) {
|
||||
$rules = $this->ruleGroupRepository->getActiveStoreRules($group);
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
// if in rule selection, or group in selection or all rules, it's included.
|
||||
$test = $this->includeRule($rule, $group);
|
||||
if (true === $test) {
|
||||
Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title));
|
||||
$rulesToApply[] = $rule->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rulesToApply;
|
||||
}
|
||||
}
|
||||
|
@@ -24,8 +24,10 @@ namespace FireflyIII\Console\Commands\Upgrade;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Log;
|
||||
@@ -48,9 +50,23 @@ class AccountCurrencies extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:account-currencies {--F|force : Force the execution of this command.}';
|
||||
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
private $accountRepos;
|
||||
/** @var UserRepositoryInterface */
|
||||
private $userRepos;
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* AccountCurrencies constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account.
|
||||
@@ -59,8 +75,7 @@ class AccountCurrencies extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$start = microtime(true);
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
@@ -69,11 +84,17 @@ class AccountCurrencies extends Command
|
||||
Log::debug('Now in updateAccountCurrencies()');
|
||||
$this->updateAccountCurrencies();
|
||||
|
||||
if (0 === $this->count) {
|
||||
$this->line('All accounts are OK.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Corrected %d account(s).', $this->count));
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified and fixed account currencies in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -100,41 +121,53 @@ class AccountCurrencies extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Account $account
|
||||
* @param TransactionCurrency $currency
|
||||
*/
|
||||
private function updateAccount(Account $account, TransactionCurrency $currency): void
|
||||
{
|
||||
$this->repository->setUser($account->user);
|
||||
|
||||
$accountCurrency = (int)$this->repository->getMetaValue($account, 'currency_id');
|
||||
$openingBalance = $account->getOpeningBalance();
|
||||
$obCurrency = (int)$openingBalance->transaction_currency_id;
|
||||
$this->accountRepos->setUser($account->user);
|
||||
|
||||
$accountCurrency = (int)$this->accountRepos->getMetaValue($account, 'currency_id');
|
||||
$openingBalance = $this->accountRepos->getOpeningBalance($account);
|
||||
$obCurrency = 0;
|
||||
if (null !== $openingBalance) {
|
||||
$obCurrency = (int)$openingBalance->transaction_currency_id;
|
||||
}
|
||||
|
||||
// both 0? set to default currency:
|
||||
if (0 === $accountCurrency && 0 === $obCurrency) {
|
||||
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $currency->id]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $currency->code));
|
||||
|
||||
$this->count++;
|
||||
return;
|
||||
}
|
||||
|
||||
// account is set to 0, opening balance is not?
|
||||
if (0 === $accountCurrency && $obCurrency > 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $currency->code));
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (#%d).', $account->id, $account->name, $obCurrency));
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// do not match and opening balance id is not null.
|
||||
if ($accountCurrency !== $obCurrency && $openingBalance->id > 0) {
|
||||
if ($accountCurrency !== $obCurrency && null !== $openingBalance) {
|
||||
// update opening balance:
|
||||
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||
$openingBalance->save();
|
||||
$openingBalance->transactions->each(
|
||||
static function (Transaction $transaction) use ($accountCurrency) {
|
||||
$transaction->transaction_currency_id = $accountCurrency;
|
||||
$transaction->save();
|
||||
});
|
||||
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,24 +176,21 @@ class AccountCurrencies extends Command
|
||||
*/
|
||||
private function updateAccountCurrencies(): void
|
||||
{
|
||||
|
||||
$users = $this->userRepos->all();
|
||||
$defaultCurrencyCode = (string)config('firefly.default_currency', 'EUR');
|
||||
$users = User::get();
|
||||
foreach ($users as $user) {
|
||||
$this->updateCurrenciesForUser($user, $defaultCurrencyCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param User $user
|
||||
* @param string $systemCurrencyCode
|
||||
*/
|
||||
private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void
|
||||
{
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])
|
||||
->get(['accounts.*']);
|
||||
$this->accountRepos->setUser($user);
|
||||
$accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
|
||||
// get user's currency preference:
|
||||
$defaultCurrencyCode = app('preferences')->getForUser($user, 'currencyPreference', $systemCurrencyCode)->data;
|
||||
|
@@ -54,6 +54,7 @@ class BackToJournals extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
$start = microtime(true);
|
||||
if (!$this->isMigrated()) {
|
||||
$this->error('Please run firefly-iii:migrate-to-groups first.');
|
||||
@@ -66,6 +67,7 @@ class BackToJournals extends Command
|
||||
if (true === $this->option('force')) {
|
||||
$this->warn('Forcing the command.');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$this->migrateAll();
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
@@ -82,8 +84,9 @@ class BackToJournals extends Command
|
||||
{
|
||||
$transactions = DB::table('budget_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray();
|
||||
|
||||
return DB::table('transactions')->whereIn('transactions.id', $transactions)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(
|
||||
);
|
||||
return DB::table('transactions')
|
||||
->whereIn('transactions.id', $transactions)
|
||||
->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,8 +96,9 @@ class BackToJournals extends Command
|
||||
{
|
||||
$transactions = DB::table('category_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray();
|
||||
|
||||
return DB::table('transactions')->whereIn('transactions.id', $transactions)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(
|
||||
);
|
||||
return DB::table('transactions')
|
||||
->whereIn('transactions.id', $transactions)
|
||||
->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,9 +153,10 @@ class BackToJournals extends Command
|
||||
*/
|
||||
private function migrateBudgets(): void
|
||||
{
|
||||
|
||||
$journalIds = $this->getIdsForBudgets();
|
||||
$journals = TransactionJournal::whereIn('id', $journalIds)->with(['transactions', 'budgets', 'transactions.budgets'])->get();
|
||||
$this->line(sprintf('Check %d transaction journals for budget info.',count($journals)));
|
||||
$this->line(sprintf('Check %d transaction journal(s) for budget info.', count($journals)));
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->migrateBudgetsForJournal($journal);
|
||||
@@ -163,26 +168,34 @@ class BackToJournals extends Command
|
||||
*/
|
||||
private function migrateBudgetsForJournal(TransactionJournal $journal): void
|
||||
{
|
||||
|
||||
// grab category from first transaction
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions->first();
|
||||
if (null === $transaction) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->info(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
/** @var Budget $budget */
|
||||
$budget = $transaction->budgets->first();
|
||||
/** @var Budget $journalBudget */
|
||||
$journalBudget = $journal->budgets->first();
|
||||
|
||||
// both have a budget, but they don't match.
|
||||
if (null !== $budget && null !== $journalBudget && $budget->id !== $journalBudget->id) {
|
||||
// sync to journal:
|
||||
$journal->budgets()->sync([(int)$budget->id]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// budget in transaction overrules journal.
|
||||
if (null === $budget && null !== $journalBudget) {
|
||||
$journal->budgets()->sync([]);
|
||||
// transaction has a budget, but the journal doesn't.
|
||||
if (null !== $budget && null === $journalBudget) {
|
||||
// sync to journal:
|
||||
$journal->budgets()->sync([(int)$budget->id]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +206,7 @@ class BackToJournals extends Command
|
||||
{
|
||||
$journalIds = $this->getIdsForCategories();
|
||||
$journals = TransactionJournal::whereIn('id', $journalIds)->with(['transactions', 'categories', 'transactions.categories'])->get();
|
||||
$this->line(sprintf('Check %d transaction journals for category info.', count($journals)));
|
||||
$this->line(sprintf('Check %d transaction journal(s) for category info.', count($journals)));
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->migrateCategoriesForJournal($journal);
|
||||
@@ -209,22 +222,26 @@ class BackToJournals extends Command
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions->first();
|
||||
if (null === $transaction) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->info(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
/** @var Category $category */
|
||||
$category = $transaction->categories->first();
|
||||
/** @var Category $journalCategory */
|
||||
$journalCategory = $journal->categories->first();
|
||||
|
||||
// both have a category, but they don't match.
|
||||
if (null !== $category && null !== $journalCategory && $category->id !== $journalCategory->id) {
|
||||
// sync to journal:
|
||||
$journal->categories()->sync([(int)$category->id]);
|
||||
}
|
||||
|
||||
// category in transaction overrules journal.
|
||||
if (null === $category && null !== $journalCategory) {
|
||||
$journal->categories()->sync([]);
|
||||
// transaction has a category, but the journal doesn't.
|
||||
if (null !== $category && null === $journalCategory) {
|
||||
$journal->categories()->sync([(int)$category->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -55,11 +55,14 @@ class BudgetLimitCurrency extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$count = 0;
|
||||
$budgetLimits = BudgetLimit::get();
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
|
@@ -58,14 +58,19 @@ class CCLiabilities extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$ccType = AccountType::where('type', AccountType::CREDITCARD)->first();
|
||||
$debtType = AccountType::where('type', AccountType::DEBT)->first();
|
||||
if (null === $ccType || null === $debtType) {
|
||||
$this->info('No incorrectly stored credit card liabilities.');
|
||||
return 0;
|
||||
}
|
||||
/** @var Collection $accounts */
|
||||
|
@@ -62,6 +62,20 @@ class JournalCurrencies extends Command
|
||||
private $currencyRepos;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
/** @var int */
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* JournalCurrencies constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->count = 0;
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
@@ -71,20 +85,27 @@ class JournalCurrencies extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
$this->accountCurrencies = [];
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
|
||||
|
||||
$start = microtime(true);
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$this->updateTransferCurrencies();
|
||||
$this->updateOtherJournalsCurrencies();
|
||||
$this->markAsExecuted();
|
||||
|
||||
if (0 === $this->count) {
|
||||
$this->line('All transactions are correct.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Verified %d transaction(s) and journal(s).', $this->count));
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified and fixed transaction currencies in %s seconds.', $end));
|
||||
|
||||
@@ -137,7 +158,8 @@ class JournalCurrencies extends Command
|
||||
private function getFirstAssetTransaction(TransactionJournal $journal): ?Transaction
|
||||
{
|
||||
$result = $journal->transactions->first(
|
||||
function (Transaction $transaction) {
|
||||
static function (Transaction $transaction) {
|
||||
// type can also be liability.
|
||||
return AccountType::ASSET === $transaction->account->accountType->type;
|
||||
}
|
||||
);
|
||||
@@ -180,7 +202,7 @@ class JournalCurrencies extends Command
|
||||
* This method makes sure that the transaction journal uses the currency given in the transaction.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param Transaction $transaction
|
||||
* @param Transaction $transaction
|
||||
*/
|
||||
private function updateJournalCurrency(TransactionJournal $journal, Transaction $transaction): void
|
||||
{
|
||||
@@ -192,6 +214,7 @@ class JournalCurrencies extends Command
|
||||
}
|
||||
|
||||
if (!((int)$currency->id === (int)$journal->transaction_currency_id)) {
|
||||
$this->count++;
|
||||
$this->line(
|
||||
sprintf(
|
||||
'Transfer #%d ("%s") has been updated to use %s instead of %s.',
|
||||
@@ -223,10 +246,11 @@ class JournalCurrencies extends Command
|
||||
}
|
||||
|
||||
$journal->transactions->each(
|
||||
function (Transaction $transaction) use ($currency) {
|
||||
static function (Transaction $transaction) use ($currency) {
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
$this->count++;
|
||||
}
|
||||
|
||||
// when mismatch in transaction:
|
||||
@@ -235,11 +259,13 @@ class JournalCurrencies extends Command
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
$this->count++;
|
||||
}
|
||||
}
|
||||
);
|
||||
// also update the journal, of course:
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$this->count++;
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
@@ -255,11 +281,15 @@ class JournalCurrencies extends Command
|
||||
*/
|
||||
private function updateOtherJournalsCurrencies(): void
|
||||
{
|
||||
$set = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->whereNotIn('transaction_types.type', [TransactionType::TRANSFER])
|
||||
->with(['transactions', 'transactions.account', 'transactions.account.accountType'])
|
||||
->get(['transaction_journals.*']);
|
||||
$set =
|
||||
$this->journalRepos->getAllJournals(
|
||||
[
|
||||
TransactionType::WITHDRAWAL,
|
||||
TransactionType::DEPOSIT,
|
||||
TransactionType::OPENING_BALANCE,
|
||||
TransactionType::RECONCILIATION,
|
||||
]
|
||||
);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
@@ -292,46 +322,54 @@ class JournalCurrencies extends Command
|
||||
$this->journalRepos->setUser($user);
|
||||
$this->currencyRepos->setUser($user);
|
||||
|
||||
$sourceAccountCurrency = $this->getCurrency($sourceAccount);
|
||||
$destAccountCurrency = $this->getCurrency($destAccount);
|
||||
if (null === $sourceAccountCurrency) {
|
||||
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $sourceAccount->id, $sourceAccount->name));
|
||||
$sourceCurrency = $this->getCurrency($sourceAccount);
|
||||
$destCurrency = $this->getCurrency($destAccount);
|
||||
if (null === $sourceCurrency) {
|
||||
$message = sprintf('Account #%d ("%s") must have currency preference but has none.', $sourceAccount->id, $sourceAccount->name);
|
||||
Log::error($message);
|
||||
$this->error($message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// has no currency ID? Must have, so fill in using account preference:
|
||||
if (null === $source->transaction_currency_id) {
|
||||
$source->transaction_currency_id = (int)$sourceAccountCurrency->id;
|
||||
Log::debug(sprintf('Transaction #%d has no currency setting, now set to %s', $source->id, $sourceAccountCurrency->code));
|
||||
$source->transaction_currency_id = (int)$sourceCurrency->id;
|
||||
$message = sprintf('Transaction #%d has no currency setting, now set to %s.', $source->id, $sourceCurrency->code);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$source->save();
|
||||
}
|
||||
|
||||
// does not match the source account (see above)? Can be fixed
|
||||
// when mismatch in transaction and NO foreign amount is set:
|
||||
if (!((int)$source->transaction_currency_id === (int)$sourceAccountCurrency->id) && null === $source->foreign_amount) {
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$source->id,
|
||||
$source->transaction_currency_id,
|
||||
$sourceAccountCurrency->id,
|
||||
$source->amount
|
||||
)
|
||||
if (!((int)$source->transaction_currency_id === (int)$sourceCurrency->id) && null === $source->foreign_amount) {
|
||||
$message = sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$source->id,
|
||||
$source->transaction_currency_id,
|
||||
$sourceCurrency->id,
|
||||
$source->amount
|
||||
);
|
||||
$source->transaction_currency_id = (int)$sourceAccountCurrency->id;
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$source->transaction_currency_id = (int)$sourceCurrency->id;
|
||||
$source->save();
|
||||
}
|
||||
|
||||
|
||||
if (null === $destAccountCurrency) {
|
||||
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $destAccount->id, $destAccount->name));
|
||||
if (null === $destCurrency) {
|
||||
$message = sprintf('Account #%d ("%s") must have currency preference but has none.', $destAccount->id, $destAccount->name);
|
||||
Log::error($message);
|
||||
$this->line($message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if the destination account currency is the same, both foreign_amount and foreign_currency_id must be NULL for both transactions:
|
||||
if ((int)$destAccountCurrency->id === (int)$sourceAccountCurrency->id) {
|
||||
if ((int)$destCurrency->id === (int)$sourceCurrency->id) {
|
||||
// update both transactions to match:
|
||||
$source->foreign_amount = null;
|
||||
$source->foreign_currency_id = null;
|
||||
@@ -343,22 +381,24 @@ class JournalCurrencies extends Command
|
||||
sprintf(
|
||||
'Currency for account "%s" is %s, and currency for account "%s" is also
|
||||
%s, so %s #%d (#%d and #%d) has been verified to be to %s exclusively.',
|
||||
$destAccount->name, $destAccountCurrency->code,
|
||||
$sourceAccount->name, $sourceAccountCurrency->code,
|
||||
$destAccount->name, $destCurrency->code,
|
||||
$sourceAccount->name, $sourceCurrency->code,
|
||||
$journal->transactionType->type, $journal->id,
|
||||
$source->id, $destination->id, $sourceAccountCurrency->code
|
||||
$source->id, $destination->id, $sourceCurrency->code
|
||||
)
|
||||
);
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if destination account currency is different, both transactions must have this currency as foreign currency id.
|
||||
if (!((int)$destAccountCurrency->id === (int)$sourceAccountCurrency->id)) {
|
||||
$source->foreign_currency_id = $destAccountCurrency->id;
|
||||
$destination->foreign_currency_id = $destAccountCurrency->id;
|
||||
if (!((int)$destCurrency->id === (int)$sourceCurrency->id)) {
|
||||
$source->foreign_currency_id = $destCurrency->id;
|
||||
$destination->foreign_currency_id = $destCurrency->id;
|
||||
$source->save();
|
||||
$destination->save();
|
||||
$this->count++;
|
||||
Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $source->id, $destination->id));
|
||||
}
|
||||
|
||||
@@ -366,6 +406,7 @@ class JournalCurrencies extends Command
|
||||
if (null === $source->foreign_amount && null !== $destination->foreign_amount) {
|
||||
$source->foreign_amount = bcmul((string)$destination->foreign_amount, '-1');
|
||||
$source->save();
|
||||
$this->count++;
|
||||
Log::debug(sprintf('Restored foreign amount of source transaction (1) #%d to %s', $source->id, $source->foreign_amount));
|
||||
}
|
||||
|
||||
@@ -373,6 +414,7 @@ class JournalCurrencies extends Command
|
||||
if (null === $destination->foreign_amount && null !== $destination->foreign_amount) {
|
||||
$destination->foreign_amount = bcmul((string)$destination->foreign_amount, '-1');
|
||||
$destination->save();
|
||||
$this->count++;
|
||||
Log::debug(sprintf('Restored foreign amount of destination transaction (2) #%d to %s', $destination->id, $destination->foreign_amount));
|
||||
}
|
||||
|
||||
@@ -385,6 +427,7 @@ class JournalCurrencies extends Command
|
||||
$destination->foreign_amount = $destination->amount;
|
||||
$source->save();
|
||||
$destination->save();
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -396,6 +439,7 @@ class JournalCurrencies extends Command
|
||||
$foreignAmount
|
||||
)
|
||||
);
|
||||
$this->count++;
|
||||
$source->foreign_amount = bcmul($foreignPositive, '-1');
|
||||
$destination->foreign_amount = $foreignPositive;
|
||||
$source->save();
|
||||
@@ -415,12 +459,7 @@ class JournalCurrencies extends Command
|
||||
*/
|
||||
private function updateTransferCurrencies(): void
|
||||
{
|
||||
$set = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->where('transaction_types.type', TransactionType::TRANSFER)
|
||||
->with(['user', 'transactionType', 'transactionCurrency', 'transactions', 'transactions.account'])
|
||||
->get(['transaction_journals.*']);
|
||||
|
||||
$set = $this->journalRepos->getAllJournals([TransactionType::TRANSFER]);
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateTransferCurrency($journal);
|
||||
@@ -435,6 +474,7 @@ class JournalCurrencies extends Command
|
||||
$sourceTransaction = $this->getSourceTransaction($transfer);
|
||||
$destTransaction = $this->getDestinationTransaction($transfer);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (null === $sourceTransaction) {
|
||||
$this->info(sprintf('Source transaction for journal #%d is null.', $transfer->id));
|
||||
|
||||
@@ -445,6 +485,7 @@ class JournalCurrencies extends Command
|
||||
|
||||
return;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$this->updateTransactionCurrency($transfer, $sourceTransaction, $destTransaction);
|
||||
$this->updateJournalCurrency($transfer, $sourceTransaction);
|
||||
|
@@ -67,7 +67,7 @@ class InstallController extends Controller
|
||||
$this->upgradeCommands = [
|
||||
// there are x initial commands
|
||||
'migrate' => ['--seed' => true, '--force' => true],
|
||||
'firefly:decrypt-all' => [],
|
||||
'firefly-iii:decrypt-all' => [],
|
||||
'generate-keys' => [], // an exception :(
|
||||
|
||||
// there are 10 upgrade commands.
|
||||
|
@@ -165,24 +165,6 @@ class Account extends Model
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the opening balance.
|
||||
*
|
||||
* @return TransactionJournal
|
||||
*/
|
||||
public function getOpeningBalance(): TransactionJournal
|
||||
{
|
||||
$journal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $this->id)
|
||||
->transactionTypes([TransactionType::OPENING_BALANCE])
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $journal) {
|
||||
return new TransactionJournal;
|
||||
}
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* Get all of the notes.
|
||||
|
@@ -38,10 +38,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
* @property string $amount
|
||||
* @property Carbon created_at
|
||||
* @property Carbon updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon $date
|
||||
* @property-read \FireflyIII\Models\TransactionJournal|null $transactionJournal
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\PiggyBankEvent newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\PiggyBankEvent newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\PiggyBankEvent query()
|
||||
|
61
app/Providers/ImportServiceProvider.php
Normal file
61
app/Providers/ImportServiceProvider.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* ImportServiceProvider.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepository;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* Class ImportServiceProvider.
|
||||
*/
|
||||
class ImportServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->bind(
|
||||
ImportJobRepositoryInterface::class,
|
||||
function (Application $app) {
|
||||
/** @var ImportJobRepositoryInterface $repository */
|
||||
$repository = app(ImportJobRepository::class);
|
||||
if ($app->auth->check()) {
|
||||
$repository->setUser(auth()->user());
|
||||
}
|
||||
|
||||
return $repository;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@@ -70,7 +70,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
/**
|
||||
* Moved here from account CRUD.
|
||||
*
|
||||
* @param Account $account
|
||||
* @param Account $account
|
||||
* @param Account|null $moveTo
|
||||
*
|
||||
* @return bool
|
||||
@@ -88,7 +88,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param string $number
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
@@ -115,7 +115,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param string $iban
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
@@ -141,7 +141,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
@@ -229,9 +229,10 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
if (count($accountIds) > 0) {
|
||||
$query->whereIn('accounts.id', $accountIds);
|
||||
}
|
||||
$query->orderBy('accounts.name','ASC');
|
||||
$query->orderBy('accounts.name', 'ASC');
|
||||
|
||||
$result = $query->get(['accounts.*']);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -247,7 +248,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
if (count($types) > 0) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
$query->orderBy('accounts.name','ASC');
|
||||
$query->orderBy('accounts.name', 'ASC');
|
||||
$result = $query->get(['accounts.*']);
|
||||
|
||||
|
||||
@@ -271,8 +272,8 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
$query->where('active', 1);
|
||||
$query->orderBy('accounts.account_type_id','ASC');
|
||||
$query->orderBy('accounts.name','ASC');
|
||||
$query->orderBy('accounts.account_type_id', 'ASC');
|
||||
$query->orderBy('accounts.name', 'ASC');
|
||||
$result = $query->get(['accounts.*']);
|
||||
|
||||
return $result;
|
||||
@@ -322,7 +323,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
* Return meta value for account. Null if not found.
|
||||
*
|
||||
* @param Account $account
|
||||
* @param string $field
|
||||
* @param string $field
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -546,7 +547,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -590,7 +591,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param array $data
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
@@ -605,4 +606,17 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return TransactionJournal|null
|
||||
*/
|
||||
public function getOpeningBalance(Account $account): ?TransactionJournal
|
||||
{
|
||||
return TransactionJournal
|
||||
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->transactionTypes([TransactionType::OPENING_BALANCE])
|
||||
->first(['transaction_journals.*']);
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,12 @@ use Illuminate\Support\Collection;
|
||||
interface AccountRepositoryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return TransactionJournal|null
|
||||
*/
|
||||
public function getOpeningBalance(Account $account): ?TransactionJournal;
|
||||
|
||||
/**
|
||||
* Moved here from account CRUD.
|
||||
*
|
||||
@@ -49,7 +55,7 @@ interface AccountRepositoryInterface
|
||||
/**
|
||||
* Moved here from account CRUD.
|
||||
*
|
||||
* @param Account $account
|
||||
* @param Account $account
|
||||
* @param Account|null $moveTo
|
||||
*
|
||||
* @return bool
|
||||
@@ -60,7 +66,7 @@ interface AccountRepositoryInterface
|
||||
* Find by account number. Is used.
|
||||
*
|
||||
* @param string $number
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
@@ -68,7 +74,7 @@ interface AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param string $iban
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
@@ -76,7 +82,7 @@ interface AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
@@ -149,7 +155,7 @@ interface AccountRepositoryInterface
|
||||
* Return meta value for account. Null if not found.
|
||||
*
|
||||
* @param Account $account
|
||||
* @param string $field
|
||||
* @param string $field
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -250,7 +256,7 @@ interface AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $types
|
||||
* @param array $types
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -270,7 +276,7 @@ interface AccountRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param array $data
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
|
@@ -791,4 +791,19 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
return $journal;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transaction journals with a specific type, regardless of user.
|
||||
*
|
||||
* @param array $types
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAllJournals(array $types): Collection
|
||||
{
|
||||
return TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->whereIn('transaction_types.type', $types)
|
||||
->with(['user', 'transactionType', 'transactionCurrency', 'transactions', 'transactions.account'])
|
||||
->get(['transaction_journals.*']);
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Repositories\Journal;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
@@ -41,11 +40,20 @@ use Illuminate\Support\MessageBag;
|
||||
*/
|
||||
interface JournalRepositoryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Get all transaction journals with a specific type, regardless of user.
|
||||
*
|
||||
* @param array $types
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAllJournals(array $types): Collection;
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param TransactionType $type
|
||||
* @param Account $source
|
||||
* @param Account $destination
|
||||
* @param TransactionType $type
|
||||
* @param Account $source
|
||||
* @param Account $destination
|
||||
*
|
||||
* @return MessageBag
|
||||
*/
|
||||
@@ -157,7 +165,7 @@ interface JournalRepositoryInterface
|
||||
* otherwise look for meta field and return that one.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param null|string $field
|
||||
* @param null|string $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -208,7 +216,7 @@ interface JournalRepositoryInterface
|
||||
* Return Carbon value of a meta field (or NULL).
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $field
|
||||
* @param string $field
|
||||
*
|
||||
* @return null|Carbon
|
||||
*/
|
||||
@@ -218,7 +226,7 @@ interface JournalRepositoryInterface
|
||||
* Return string value of a meta date (or NULL).
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $field
|
||||
* @param string $field
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -228,7 +236,7 @@ interface JournalRepositoryInterface
|
||||
* Return value of a meta field (or NULL).
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $field
|
||||
* @param string $field
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -301,7 +309,7 @@ interface JournalRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param int $order
|
||||
* @param int $order
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -316,7 +324,7 @@ interface JournalRepositoryInterface
|
||||
* Update budget for a journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param int $budgetId
|
||||
* @param int $budgetId
|
||||
*
|
||||
* @return TransactionJournal
|
||||
*/
|
||||
@@ -326,7 +334,7 @@ interface JournalRepositoryInterface
|
||||
* Update category for a journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $category
|
||||
* @param string $category
|
||||
*
|
||||
* @return TransactionJournal
|
||||
*/
|
||||
@@ -336,7 +344,7 @@ interface JournalRepositoryInterface
|
||||
* Update tag(s) for a journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $tags
|
||||
* @param array $tags
|
||||
*
|
||||
* @return TransactionJournal
|
||||
*/
|
||||
|
@@ -391,10 +391,10 @@ class FireflyValidator extends Validator
|
||||
public function validateUniqueAccountForUser($attribute, $value, $parameters): bool
|
||||
{
|
||||
// because a user does not have to be logged in (tests and what-not).
|
||||
|
||||
if (!auth()->check()) {
|
||||
return $this->validateAccountAnonymously();
|
||||
}
|
||||
|
||||
if (isset($this->data['what'])) {
|
||||
return $this->validateByAccountTypeString($value, $parameters, $this->data['what']);
|
||||
}
|
||||
@@ -409,7 +409,8 @@ class FireflyValidator extends Validator
|
||||
return $this->validateByAccountId($value);
|
||||
}
|
||||
|
||||
return false;
|
||||
// without type, just try to validate the name.
|
||||
return $this->validateByAccountName($value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -588,6 +589,7 @@ class FireflyValidator extends Validator
|
||||
$set = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get();
|
||||
/** @var Account $entry */
|
||||
foreach ($set as $entry) {
|
||||
// TODO no longer need to loop like this.
|
||||
if ($entry->name === $value) {
|
||||
return false;
|
||||
}
|
||||
@@ -598,7 +600,7 @@ class FireflyValidator extends Validator
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param array $parameters
|
||||
* @param array $parameters
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool
|
||||
@@ -627,4 +629,13 @@ class FireflyValidator extends Validator
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
private function validateByAccountName(string $value): bool
|
||||
{
|
||||
return auth()->user()->accounts()->where('name', $value)->count() === 0;
|
||||
}
|
||||
}
|
||||
|
@@ -19,7 +19,9 @@
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
use FireflyIII\Providers\ImportServiceProvider;
|
||||
|
||||
|
||||
return [
|
||||
@@ -96,6 +98,7 @@ return [
|
||||
FireflyIII\Providers\TagServiceProvider::class,
|
||||
FireflyIII\Providers\AdminServiceProvider::class,
|
||||
FireflyIII\Providers\RecurringServiceProvider::class,
|
||||
ImportServiceProvider::class,
|
||||
|
||||
|
||||
],
|
||||
|
@@ -46,10 +46,11 @@ class LinkTypeSeeder extends Seeder
|
||||
'outward' => '(partially) refunds',
|
||||
'editable' => false,
|
||||
],
|
||||
['name' => 'Paid',
|
||||
'inward' => 'is (partially) paid for by',
|
||||
'outward' => '(partially) pays for',
|
||||
'editable' => false,
|
||||
[
|
||||
'name' => 'Paid',
|
||||
'inward' => 'is (partially) paid for by',
|
||||
'outward' => '(partially) pays for',
|
||||
'editable' => false,
|
||||
],
|
||||
[
|
||||
'name' => 'Reimbursement',
|
||||
|
@@ -32,16 +32,24 @@
|
||||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
|
||||
</listeners>
|
||||
<testsuites>
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
<!-- test suites -->
|
||||
<testsuite name="Api">
|
||||
<directory suffix="Test.php">./tests/Api</directory>
|
||||
</testsuite>
|
||||
<!-- unit tests, splitted: -->
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit/Console</directory>
|
||||
</testsuite>
|
||||
|
||||
|
||||
<!--
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
-->
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
|
@@ -32,16 +32,24 @@
|
||||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
|
||||
</listeners>
|
||||
<testsuites>
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
<!-- test suites -->
|
||||
<testsuite name="Api">
|
||||
<directory suffix="Test.php">./tests/Api</directory>
|
||||
</testsuite>
|
||||
<!-- unit tests, splitted: -->
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit/Console</directory>
|
||||
</testsuite>
|
||||
|
||||
|
||||
<!--
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
-->
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
|
22
phpunit.xml
22
phpunit.xml
@@ -32,16 +32,24 @@
|
||||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
|
||||
</listeners>
|
||||
<testsuites>
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
<!-- test suites -->
|
||||
<testsuite name="Api">
|
||||
<directory suffix="Test.php">./tests/Api</directory>
|
||||
</testsuite>
|
||||
<!-- unit tests, splitted: -->
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit/Console</directory>
|
||||
</testsuite>
|
||||
|
||||
|
||||
<!--
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
-->
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
|
@@ -333,6 +333,9 @@ abstract class TestCase extends BaseTestCase
|
||||
DB::raw('COUNT(transaction_journal_id) as ct'),
|
||||
]
|
||||
)->first();
|
||||
if(null === $result) {
|
||||
throw new FireflyException(sprintf('Cannot find suitable %s to use.', $type));
|
||||
}
|
||||
|
||||
return TransactionJournal::find((int)$result->transaction_journal_id);
|
||||
}
|
||||
|
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* CorrectDatabaseTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Mockery;
|
||||
use Preferences;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class CreateAccessTokensTest
|
||||
*/
|
||||
class CreateAccessTokensTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\CreateAccessTokens
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$users = new Collection([$this->user()]);
|
||||
$repository = $this->mock(UserRepositoryInterface::class);
|
||||
|
||||
// mock calls:
|
||||
$repository->shouldReceive('all')->atLeast()->once()->andReturn($users);
|
||||
|
||||
// mock preferences thing:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'access_token', null])
|
||||
->once()->andReturn(null);
|
||||
|
||||
// null means user object will generate one and store it.
|
||||
Preferences::shouldReceive('setForUser')->withArgs([Mockery::any(), 'access_token', Mockery::any()])
|
||||
->once();
|
||||
|
||||
|
||||
$this->artisan('firefly-iii:create-access-tokens')
|
||||
->expectsOutput(sprintf('Generated access token for user %s', $this->user()->email))
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\CreateAccessTokens
|
||||
*/
|
||||
public function testHandlePrefExists(): void
|
||||
{
|
||||
$users = new Collection([$this->user()]);
|
||||
$repository = $this->mock(UserRepositoryInterface::class);
|
||||
|
||||
// mock calls:
|
||||
$repository->shouldReceive('all')->atLeast()->once()->andReturn($users);
|
||||
|
||||
// mock preferences thing:
|
||||
$preference = new Preference;
|
||||
$preference->data = '123';
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'access_token', null])
|
||||
->once()->andReturn($preference);
|
||||
|
||||
// null means user object will generate one and store it.
|
||||
Preferences::shouldNotReceive('setForUser');
|
||||
|
||||
$this->artisan('firefly-iii:create-access-tokens')
|
||||
->expectsOutput('All access tokens OK!')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* CreateLinkTypesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\LinkType;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class CreateLinkTypesTest
|
||||
*/
|
||||
class CreateLinkTypesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\CreateLinkTypes
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// delete all other link types:
|
||||
LinkType::whereNotIn('name', ['Related', 'Refund', 'Paid', 'Reimbursement'])->forceDelete();
|
||||
|
||||
// delete link type:
|
||||
LinkType::where('name', 'Reimbursement')->forceDelete();
|
||||
$this->assertCount(3, LinkType::get());
|
||||
|
||||
// run command, expect output:
|
||||
$this->artisan('firefly-iii:create-link-types')
|
||||
->expectsOutput('Created missing link type "Reimbursement"')
|
||||
->assertExitCode(0);
|
||||
|
||||
$this->assertCount(4, LinkType::get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\CreateLinkTypes
|
||||
*/
|
||||
public function testHandleNothing(): void
|
||||
{
|
||||
$this->assertCount(4, LinkType::get());
|
||||
|
||||
// run command, expect output:
|
||||
$this->artisan('firefly-iii:create-link-types')
|
||||
->expectsOutput('All link types OK!')
|
||||
->assertExitCode(0);
|
||||
|
||||
$this->assertCount(4, LinkType::get());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* DeleteEmptyGroupsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class DeleteEmptyGroupsTest
|
||||
*/
|
||||
class DeleteEmptyGroupsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteEmptyGroups
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// assume there are no empty groups..
|
||||
$this->artisan('firefly-iii:delete-empty-groups')
|
||||
->expectsOutput('No empty transaction groups.')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteEmptyGroups
|
||||
*/
|
||||
public function testHandleWithGroup(): void
|
||||
{
|
||||
// create new group:
|
||||
$group = TransactionGroup::create(['user_id' => 1]);
|
||||
|
||||
// command should delete it.
|
||||
$this->artisan('firefly-iii:delete-empty-groups')
|
||||
->expectsOutput('Deleted 1 empty transaction group(s).')
|
||||
->assertExitCode(0);
|
||||
|
||||
// should not be able to find it:
|
||||
$this->assertCount(0, TransactionGroup::where('id', $group->id)->whereNull('deleted_at')->get());
|
||||
}
|
||||
}
|
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* DeleteEmptyJournalsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
class DeleteEmptyJournalsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteEmptyJournals
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// assume there are no empty journals or uneven journals
|
||||
$this->artisan('firefly-iii:delete-empty-journals')
|
||||
->expectsOutput('No uneven transaction journals.')
|
||||
->expectsOutput('No empty transaction journals.')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteEmptyJournals
|
||||
*/
|
||||
public function testHandleEmptyJournals(): void
|
||||
{
|
||||
// create empty journal:
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => 1,
|
||||
'description' => 'Hello',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$this->artisan('firefly-iii:delete-empty-journals')
|
||||
->expectsOutput('No uneven transaction journals.')
|
||||
->expectsOutput(sprintf('Deleted empty transaction journal #%d', $journal->id))
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify its indeed gone
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->whereNull('deleted_at')->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteEmptyJournals
|
||||
*/
|
||||
public function testHandleUnevenJournals(): void
|
||||
{
|
||||
// create empty journal:
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => 1,
|
||||
'description' => 'Hello',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
|
||||
// link empty transaction
|
||||
$transaction = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => 1,
|
||||
'amount' => '5',
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
$this->artisan('firefly-iii:delete-empty-journals')
|
||||
->expectsOutput(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $journal->id))
|
||||
->expectsOutput('No empty transaction journals.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify both are gone
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->whereNull('deleted_at')->get());
|
||||
$this->assertCount(0, Transaction::where('id', $transaction->id)->whereNull('deleted_at')->get());
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* DeleteOrphanedTransactionsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class DeleteOrphanedTransactionsTest
|
||||
*/
|
||||
class DeleteOrphanedTransactionsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteOrphanedTransactions
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// assume there are no orphaned transactions.
|
||||
$this->artisan('firefly-iii:delete-orphaned-transactions')
|
||||
->expectsOutput('No orphaned transactions.')
|
||||
->expectsOutput('No orphaned accounts.')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testHandleOrphanedAccounts(): void
|
||||
{
|
||||
|
||||
// create deleted account:
|
||||
$account = Account::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'name' => 'Some account',
|
||||
'account_type_id' => 1,
|
||||
|
||||
]
|
||||
);
|
||||
$account->delete();
|
||||
|
||||
// create NOT deleted journal + transaction.
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => 1,
|
||||
'description' => 'Hello',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
|
||||
$transaction = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $account->id,
|
||||
'amount' => '5',
|
||||
]
|
||||
);
|
||||
|
||||
$this->artisan('firefly-iii:delete-orphaned-transactions')
|
||||
->expectsOutput('No orphaned transactions.')
|
||||
->expectsOutput(sprintf('Deleted transaction journal #%d because account #%d was already deleted.',
|
||||
$journal->id, $account->id))
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify bad objects are gone.
|
||||
$this->assertCount(0, Transaction::where('id', $transaction->id)->whereNull('deleted_at')->get());
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->whereNull('deleted_at')->get());
|
||||
$this->assertCount(0, Account::where('id', $account->id)->whereNull('deleted_at')->get());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteOrphanedTransactions
|
||||
*/
|
||||
public function testHandleOrphanedTransactions(): void
|
||||
{
|
||||
// create deleted journal:
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => 1,
|
||||
'description' => 'Hello',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$journal->delete();
|
||||
|
||||
// create NOT deleted transaction.
|
||||
$transaction = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => 1,
|
||||
'amount' => '5',
|
||||
]
|
||||
);
|
||||
|
||||
$this->artisan('firefly-iii:delete-orphaned-transactions')
|
||||
->expectsOutput(sprintf('Transaction #%d (part of deleted transaction journal #%d) has been deleted as well.',
|
||||
$transaction->id, $journal->id))
|
||||
->expectsOutput('No orphaned accounts.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify objects are gone.
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->whereNull('deleted_at')->get());
|
||||
$this->assertCount(0, Transaction::where('id', $transaction->id)->whereNull('deleted_at')->get());
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* DeleteZeroAmountTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class DeleteZeroAmountTest
|
||||
*/
|
||||
class DeleteZeroAmountTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteZeroAmount
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// assume there are no transactions with a zero amount.
|
||||
$this->artisan('firefly-iii:delete-zero-amount')
|
||||
->expectsOutput('No zero-amount transaction journals.')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\DeleteZeroAmount
|
||||
*/
|
||||
public function testHandleTransactions(): void
|
||||
{
|
||||
$account = $this->getRandomAsset();
|
||||
// create NOT deleted journal + transaction.
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => 1,
|
||||
'description' => 'Hello',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
|
||||
$transaction = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $account->id,
|
||||
'amount' => '0',
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
// assume there are no transactions with a zero amount.
|
||||
$this->artisan('firefly-iii:delete-zero-amount')
|
||||
->expectsOutput(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id))
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify objects are gone.
|
||||
$this->assertCount(0, Transaction::where('id', $transaction->id)->whereNull('deleted_at')->get());
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->whereNull('deleted_at')->get());
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* EnableCurrenciesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class EnableCurrenciesTest
|
||||
*/
|
||||
class EnableCurrenciesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\EnableCurrencies
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// assume the current database is intact.
|
||||
$count = TransactionCurrency::where('enabled', 1)->count();
|
||||
|
||||
$this->artisan('firefly-iii:enable-currencies')
|
||||
->expectsOutput('All currencies are correctly enabled or disabled.')
|
||||
->assertExitCode(0);
|
||||
|
||||
|
||||
$this->assertCount($count, TransactionCurrency::where('enabled', 1)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\EnableCurrencies
|
||||
*/
|
||||
public function testHandleDisabled(): void
|
||||
{
|
||||
// find a disabled currency, update a budget limit with it.
|
||||
$currency = TransactionCurrency::where('enabled', 0)->first();
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
$budgetLimit = BudgetLimit::inRandomOrder()->first();
|
||||
$budgetLimit->transaction_currency_id = $currency->id;
|
||||
$budgetLimit->save();
|
||||
|
||||
// assume the current database is intact.
|
||||
$count = TransactionCurrency::where('enabled', 1)->count();
|
||||
$this->artisan('firefly-iii:enable-currencies')
|
||||
->expectsOutput(sprintf('%d were (was) still disabled. This has been corrected.', 1))
|
||||
->assertExitCode(0);
|
||||
|
||||
// assume its been enabled.
|
||||
$this->assertCount($count + 1, TransactionCurrency::where('enabled', 1)->get());
|
||||
}
|
||||
|
||||
}
|
371
tests/Unit/Console/Commands/Correction/FixAccountTypesTest.php
Normal file
371
tests/Unit/Console/Commands/Correction/FixAccountTypesTest.php
Normal file
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
/**
|
||||
* FixAccountTypesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Factory\AccountFactory;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class FixAccountTypesTest
|
||||
*/
|
||||
class FixAccountTypesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixAccountTypes
|
||||
*/
|
||||
public function testHandleUneven(): void
|
||||
{
|
||||
$this->mock(AccountFactory::class);
|
||||
$source = $this->user()->accounts()->where('name', 'Another DUO Student loans')->first();
|
||||
$type = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => $type->id,
|
||||
'description' => 'Test',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$one = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $source->id,
|
||||
'amount' => '-10',
|
||||
]
|
||||
);
|
||||
|
||||
// assume there's nothing to fix.
|
||||
$this->artisan('firefly-iii:fix-account-types')
|
||||
->expectsOutput(sprintf('Cannot inspect transaction journal #%d because it has 1 transaction(s) instead of 2.', $journal->id))
|
||||
->assertExitCode(0);
|
||||
$one->forceDelete();
|
||||
$journal->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixAccountTypes
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$this->mock(AccountFactory::class);
|
||||
|
||||
// assume there's nothing to fix.
|
||||
$this->artisan('firefly-iii:fix-account-types')
|
||||
->expectsOutput('All account types are OK!')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to fix a withdrawal that goes from a loan to another loan.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixAccountTypes
|
||||
*/
|
||||
public function testHandleWithdrawalLoanLoan(): void
|
||||
{
|
||||
$this->mock(AccountFactory::class);
|
||||
$source = $this->user()->accounts()->where('name', 'Another DUO Student loans')->first();
|
||||
$destination = $this->user()->accounts()->where('name', 'DUO Student loans')->first();
|
||||
$type = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => $type->id,
|
||||
'description' => 'Test',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$one = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $source->id,
|
||||
'amount' => '-10',
|
||||
]
|
||||
);
|
||||
$two = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $destination->id,
|
||||
'amount' => '10',
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
$this->artisan('firefly-iii:fix-account-types')
|
||||
->expectsOutput(sprintf('The source account of %s #%d cannot be of type "%s".', $type->type, $journal->id, 'Loan'))
|
||||
->expectsOutput(sprintf('The destination account of %s #%d cannot be of type "%s".', $type->type, $journal->id, 'Loan'))
|
||||
->expectsOutput('Acted on 1 transaction(s)!')
|
||||
->assertExitCode(0);
|
||||
|
||||
// since system cant handle this problem, dont look for changed transactions.
|
||||
|
||||
|
||||
$one->forceDelete();
|
||||
$two->forceDelete();
|
||||
$journal->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transferring from an asset to a loan should be a withdrawal, not a transfer
|
||||
*/
|
||||
public function testHandleTransferAssetLoan(): void
|
||||
{
|
||||
$this->mock(AccountFactory::class);
|
||||
$source = $this->getRandomAsset();
|
||||
$destination = $this->user()->accounts()->where('name', 'DUO Student loans')->first();
|
||||
$type = TransactionType::where('type', TransactionType::TRANSFER)->first();
|
||||
$withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => $type->id,
|
||||
'description' => 'Test',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$one = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $source->id,
|
||||
'amount' => '-10',
|
||||
]
|
||||
);
|
||||
$two = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $destination->id,
|
||||
'amount' => '10',
|
||||
]
|
||||
);
|
||||
|
||||
$this->artisan('firefly-iii:fix-account-types')
|
||||
->expectsOutput(sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id))
|
||||
->expectsOutput('Acted on 1 transaction(s)!')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify the change has been made.
|
||||
$this->assertCount(1, TransactionJournal::where('id', $journal->id)->where('transaction_type_id', $withdrawal->id)->get());
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->where('transaction_type_id', $type->id)->get());
|
||||
|
||||
$one->forceDelete();
|
||||
$two->forceDelete();
|
||||
$journal->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transferring from a loan to an asset should be a deposit, not a transfer
|
||||
*/
|
||||
public function testHandleTransferLoanAsset(): void
|
||||
{
|
||||
$this->mock(AccountFactory::class);
|
||||
$source = $this->user()->accounts()->where('name', 'DUO Student loans')->first();
|
||||
$destination = $this->getRandomAsset();
|
||||
$type = TransactionType::where('type', TransactionType::TRANSFER)->first();
|
||||
$deposit = TransactionType::where('type', TransactionType::DEPOSIT)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => $type->id,
|
||||
'description' => 'Test',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$one = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $source->id,
|
||||
'amount' => '-10',
|
||||
]
|
||||
);
|
||||
$two = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $destination->id,
|
||||
'amount' => '10',
|
||||
]
|
||||
);
|
||||
|
||||
$this->artisan('firefly-iii:fix-account-types')
|
||||
->expectsOutput(sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id))
|
||||
->expectsOutput('Acted on 1 transaction(s)!')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify the change has been made.
|
||||
$this->assertCount(1, TransactionJournal::where('id', $journal->id)->where('transaction_type_id', $deposit->id)->get());
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->where('transaction_type_id', $type->id)->get());
|
||||
|
||||
$one->forceDelete();
|
||||
$two->forceDelete();
|
||||
$journal->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Withdrawal with a revenue account as a destination must be converted.
|
||||
*/
|
||||
public function testHandleWithdrawalAssetRevenue(): void
|
||||
{
|
||||
$source = $this->getRandomAsset();
|
||||
$destination = $this->getRandomRevenue();
|
||||
$newDestination = $this->getRandomExpense();
|
||||
$withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => $withdrawal->id,
|
||||
'description' => 'Test',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$one = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $source->id,
|
||||
'amount' => '-10',
|
||||
]
|
||||
);
|
||||
$two = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $destination->id,
|
||||
'amount' => '10',
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertCount(0, Transaction::where('id', $two->id)->where('account_id', $newDestination->id)->get());
|
||||
$this->assertCount(1, Transaction::where('id', $two->id)->where('account_id', $destination->id)->get());
|
||||
|
||||
// mock stuff
|
||||
$factory = $this->mock(AccountFactory::class);
|
||||
$factory->shouldReceive('setUser')->atLeast()->once();
|
||||
$factory->shouldReceive('findOrCreate')
|
||||
->withArgs([$destination->name, AccountType::EXPENSE])
|
||||
->atLeast()->once()->andReturn($newDestination);
|
||||
|
||||
// Transaction journal #137, destination account changed from #1 ("Checking Account") to #29 ("Land lord").
|
||||
$this->artisan('firefly-iii:fix-account-types')
|
||||
->expectsOutput(
|
||||
sprintf('Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").',
|
||||
$journal->id,
|
||||
$destination->id, $destination->name,
|
||||
$newDestination->id, $newDestination->name
|
||||
))
|
||||
->expectsOutput('Acted on 1 transaction(s)!')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify the change has been made
|
||||
$this->assertCount(1, Transaction::where('id', $two->id)->where('account_id', $newDestination->id)->get());
|
||||
$this->assertCount(0, Transaction::where('id', $two->id)->where('account_id', $destination->id)->get());
|
||||
|
||||
$one->forceDelete();
|
||||
$two->forceDelete();
|
||||
$journal->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposit with an expense account as a source instead of a revenue account must be converted.
|
||||
*/
|
||||
public function testHandleDepositAssetExpense(): void
|
||||
{
|
||||
$source = $this->getRandomExpense();
|
||||
$newSource = $this->getRandomRevenue();
|
||||
$destination = $this->getRandomAsset();
|
||||
|
||||
$withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
$deposit = TransactionType::where('type', TransactionType::DEPOSIT)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => $deposit->id,
|
||||
'description' => 'Test',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$one = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $source->id,
|
||||
'amount' => '-10',
|
||||
]
|
||||
);
|
||||
$two = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $destination->id,
|
||||
'amount' => '10',
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertCount(0, Transaction::where('id', $one->id)->where('account_id', $newSource->id)->get());
|
||||
$this->assertCount(1, Transaction::where('id', $one->id)->where('account_id', $source->id)->get());
|
||||
|
||||
// mock stuff
|
||||
$factory = $this->mock(AccountFactory::class);
|
||||
$factory->shouldReceive('setUser')->atLeast()->once();
|
||||
$factory->shouldReceive('findOrCreate')
|
||||
->withArgs([$source->name, AccountType::REVENUE])
|
||||
->atLeast()->once()->andReturn($newSource);
|
||||
|
||||
// Transaction journal #137, destination account changed from #1 ("Checking Account") to #29 ("Land lord").
|
||||
$this->artisan('firefly-iii:fix-account-types')
|
||||
->expectsOutput(
|
||||
sprintf('Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").',
|
||||
$journal->id,
|
||||
$destination->id, $destination->name,
|
||||
$newSource->id, $newSource->name
|
||||
))
|
||||
->expectsOutput('Acted on 1 transaction(s)!')
|
||||
->assertExitCode(0);
|
||||
|
||||
$this->assertCount(1, Transaction::where('id', $one->id)->where('account_id', $newSource->id)->get());
|
||||
$this->assertCount(0, Transaction::where('id', $one->id)->where('account_id', $source->id)->get());
|
||||
|
||||
$one->forceDelete();
|
||||
$two->forceDelete();
|
||||
$journal->forceDelete();
|
||||
}
|
||||
}
|
130
tests/Unit/Console/Commands/Correction/FixPiggiesTest.php
Normal file
130
tests/Unit/Console/Commands/Correction/FixPiggiesTest.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* FixPiggiesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class FixPiggiesTest
|
||||
*/
|
||||
class FixPiggiesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null event.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixPiggies
|
||||
*/
|
||||
public function testHandleNull(): void
|
||||
{
|
||||
/** @var PiggyBank $piggy */
|
||||
$piggy = $this->user()->piggyBanks()->inRandomOrder()->first();
|
||||
|
||||
// create event to trigger console commands.
|
||||
$event = PiggyBankEvent::create(
|
||||
[
|
||||
'piggy_bank_id' => $piggy->id,
|
||||
'date' => '2019-01-01',
|
||||
'amount' => 5,
|
||||
]
|
||||
);
|
||||
|
||||
// assume there's nothing to fix.
|
||||
$this->artisan('firefly-iii:fix-piggies')
|
||||
->expectsOutput('All piggy bank events are correct.')
|
||||
->assertExitCode(0);
|
||||
$event->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Withdrawal instead of transfer
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixPiggies
|
||||
*/
|
||||
public function testHandleBadJournal(): void
|
||||
{
|
||||
/** @var PiggyBank $piggy */
|
||||
$piggy = $this->user()->piggyBanks()->inRandomOrder()->first();
|
||||
$withdrawal = $this->getRandomWithdrawal();
|
||||
// create event to trigger console commands.
|
||||
$event = PiggyBankEvent::create(
|
||||
[
|
||||
'piggy_bank_id' => $piggy->id,
|
||||
'date' => '2019-01-01',
|
||||
'amount' => 5,
|
||||
'transaction_journal_id' => $withdrawal->id,
|
||||
]
|
||||
);
|
||||
|
||||
// assume there's nothing to fix.
|
||||
$this->artisan('firefly-iii:fix-piggies')
|
||||
->expectsOutput(sprintf('Piggy bank #%d was referenced by an invalid event. This has been fixed.', $piggy->id))
|
||||
->expectsOutput('Fixed 1 piggy bank event(s).')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify update
|
||||
$this->assertCount(0, PiggyBankEvent::where('id', $event->id)->where('transaction_journal_id', $withdrawal->id)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Withdrawal instead of transfer
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixPiggies
|
||||
*/
|
||||
public function testHandleDeletedJournal(): void
|
||||
{
|
||||
/** @var PiggyBank $piggy */
|
||||
$piggy = $this->user()->piggyBanks()->inRandomOrder()->first();
|
||||
$transfer = $this->getRandomTransfer();
|
||||
$event = PiggyBankEvent::create(
|
||||
[
|
||||
'piggy_bank_id' => $piggy->id,
|
||||
'date' => '2019-01-01',
|
||||
'amount' => 5,
|
||||
'transaction_journal_id' => $transfer->id,
|
||||
]
|
||||
);
|
||||
$transfer->deleted_at = '2019-01-01 12:00:00';
|
||||
$transfer->save();
|
||||
$transfer->refresh();
|
||||
|
||||
$this->artisan('firefly-iii:fix-piggies')
|
||||
->expectsOutput('Fixed 1 piggy bank event(s).')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify update
|
||||
$this->assertCount(0, PiggyBankEvent::where('id', $event->id)->where('transaction_journal_id', $transfer->id)->get());
|
||||
$event->forceDelete();
|
||||
}
|
||||
}
|
101
tests/Unit/Console/Commands/Correction/FixUnevenAmountTest.php
Normal file
101
tests/Unit/Console/Commands/Correction/FixUnevenAmountTest.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* FixUnevenAmountTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class FixUnevenAmountTest
|
||||
*/
|
||||
class FixUnevenAmountTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixUnevenAmount
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// assume there's nothing to fix.
|
||||
$this->artisan('firefly-iii:fix-uneven-amount')
|
||||
->expectsOutput('Amount integrity OK!')
|
||||
->assertExitCode(0);
|
||||
|
||||
// dont verify anything
|
||||
}
|
||||
|
||||
/**
|
||||
* Create uneven journal
|
||||
* @covers \FireflyIII\Console\Commands\Correction\FixUnevenAmount
|
||||
*/
|
||||
public function testHandleUneven(): void
|
||||
{
|
||||
$asset = $this->getRandomAsset();
|
||||
$expense = $this->getRandomExpense();
|
||||
$withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'transaction_currency_id' => 1,
|
||||
'transaction_type_id' => $withdrawal->id,
|
||||
'description' => 'Test',
|
||||
'tag_count' => 0,
|
||||
'date' => '2019-01-01',
|
||||
]
|
||||
);
|
||||
$one = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $asset->id,
|
||||
'amount' => '-10',
|
||||
]
|
||||
);
|
||||
$two = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'account_id' => $expense->id,
|
||||
'amount' => '12',
|
||||
]
|
||||
);
|
||||
|
||||
$this->artisan('firefly-iii:fix-uneven-amount')
|
||||
->expectsOutput(sprintf('Corrected amount in transaction journal #%d', $journal->id))
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify change.
|
||||
$this->assertCount(1, Transaction::where('id', $one->id)->where('amount', '-10')->get());
|
||||
$this->assertCount(1, Transaction::where('id', $two->id)->where('amount', '10')->get());
|
||||
}
|
||||
|
||||
}
|
75
tests/Unit/Console/Commands/Correction/RemoveBillsTest.php
Normal file
75
tests/Unit/Console/Commands/Correction/RemoveBillsTest.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* RemoveBillsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class RemoveBillsTest
|
||||
*/
|
||||
class RemoveBillsTest extends TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\RemoveBills
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// assume there's nothing to fix.
|
||||
$this->artisan('firefly-iii:remove-bills')
|
||||
->expectsOutput('All transaction journals have correct bill information.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// dont verify anything
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\RemoveBills
|
||||
*/
|
||||
public function testHandleWithdrawal(): void
|
||||
{
|
||||
$bill = $this->user()->bills()->first();
|
||||
$journal = $this->getRandomDeposit();
|
||||
|
||||
$journal->bill_id = $bill->id;
|
||||
$journal->save();
|
||||
|
||||
$this->artisan('firefly-iii:remove-bills')
|
||||
->expectsOutput(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $bill->id))
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify change
|
||||
$this->assertCount(0, TransactionJournal::where('id', $journal->id)->whereNotNull('bill_id')->get());
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* RenameMetaFieldsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class RenameMetaFieldsTest
|
||||
*/
|
||||
class RenameMetaFieldsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\RenameMetaFields
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$this->artisan('firefly-iii:rename-meta-fields')
|
||||
->expectsOutput('All meta fields are correct.')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\RenameMetaFields
|
||||
*/
|
||||
public function testHandleFixed(): void
|
||||
{
|
||||
$withdrawal = $this->getRandomWithdrawal();
|
||||
$entry = TransactionJournalMeta::create(
|
||||
[
|
||||
'transaction_journal_id' => $withdrawal->id,
|
||||
'name' => 'importHashV2',
|
||||
'data' => 'Fake data',
|
||||
|
||||
]
|
||||
);
|
||||
|
||||
$this->artisan('firefly-iii:rename-meta-fields')
|
||||
->expectsOutput('Renamed 1 meta field(s).')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify update
|
||||
$this->assertCount(1, TransactionJournalMeta::where('id', $entry->id)->where('name', 'import_hash_v2')->get());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* TransferBudgetsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Correction;
|
||||
|
||||
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class TransferBudgetsTest
|
||||
*/
|
||||
class TransferBudgetsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\TransferBudgets
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$this->artisan('firefly-iii:fix-transfer-budgets')
|
||||
->expectsOutput('No invalid budget/journal entries.')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Correction\TransferBudgets
|
||||
*/
|
||||
public function testHandleBudget(): void
|
||||
{
|
||||
$deposit = $this->getRandomDeposit();
|
||||
$budget = $this->user()->budgets()->inRandomOrder()->first();
|
||||
|
||||
$deposit->budgets()->save($budget);
|
||||
|
||||
$this->artisan('firefly-iii:fix-transfer-budgets')
|
||||
->expectsOutput(sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $deposit->id, $deposit->transactionType->type))
|
||||
->expectsOutput('Corrected 1 invalid budget/journal entries (entry).')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify change
|
||||
$this->assertCount(0, $deposit->budgets()->get());
|
||||
}
|
||||
|
||||
}
|
140
tests/Unit/Console/Commands/DecryptDatabaseTest.php
Normal file
140
tests/Unit/Console/Commands/DecryptDatabaseTest.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* DecryptDatabaseTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands;
|
||||
|
||||
|
||||
use Crypt;
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Configuration;
|
||||
use Log;
|
||||
use Mockery;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class DecryptDatabaseTest
|
||||
*/
|
||||
class DecryptDatabaseTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\DecryptDatabase
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// create encrypted account:
|
||||
$name = 'Encrypted name';
|
||||
$iban = 'HR1723600001101234565';
|
||||
$account = Account::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'account_type_id' => 1,
|
||||
'name' => Crypt::encrypt($name),
|
||||
'iban' => Crypt::encrypt($iban),
|
||||
]);
|
||||
|
||||
|
||||
|
||||
FireflyConfig::shouldReceive('get')->withArgs([Mockery::any(), false])->atLeast()->once()->andReturn(null);
|
||||
FireflyConfig::shouldReceive('set')->withArgs([Mockery::any(), true])->atLeast()->once();
|
||||
|
||||
$this->artisan('firefly-iii:decrypt-all')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
|
||||
$this->assertCount(1, Account::where('id', $account->id)->where('name', $name)->get());
|
||||
$this->assertCount(1, Account::where('id', $account->id)->where('iban', $iban)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\DecryptDatabase
|
||||
*/
|
||||
public function testHandleDecrypted(): void
|
||||
{
|
||||
// create encrypted account:
|
||||
$name = 'Encrypted name';
|
||||
$iban = 'HR1723600001101234565';
|
||||
$encryptedName = Crypt::encrypt($name);
|
||||
$encryptedIban = Crypt::encrypt($iban);
|
||||
$account = Account::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'account_type_id' => 1,
|
||||
'name' => $encryptedName,
|
||||
'iban' => $encryptedIban,
|
||||
]);
|
||||
|
||||
// pretend its not yet decrypted.
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
|
||||
FireflyConfig::shouldReceive('get')->withArgs([Mockery::any(), false])->atLeast()->once()->andReturn($true);
|
||||
|
||||
$this->artisan('firefly-iii:decrypt-all')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
|
||||
$this->assertCount(1, Account::where('id', $account->id)->where('name', $encryptedName)->get());
|
||||
$this->assertCount(1, Account::where('id', $account->id)->where('iban', $encryptedIban)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to decrypt data that isn't actually encrypted.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\DecryptDatabase
|
||||
*/
|
||||
public function testHandleNotEncrypted(): void
|
||||
{
|
||||
// create encrypted account:
|
||||
$name = 'Encrypted name';
|
||||
$iban = 'HR1723600001101234565';
|
||||
$account = Account::create(
|
||||
[
|
||||
'user_id' => 1,
|
||||
'account_type_id' => 1,
|
||||
'name' => $name,
|
||||
'iban' => $iban,
|
||||
]);
|
||||
|
||||
// pretend its not yet decrypted.
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
|
||||
FireflyConfig::shouldReceive('get')->withArgs([Mockery::any(), false])->atLeast()->once()->andReturn($true);
|
||||
|
||||
$this->artisan('firefly-iii:decrypt-all')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
|
||||
$this->assertCount(1, Account::where('id', $account->id)->where('name', $name)->get());
|
||||
$this->assertCount(1, Account::where('id', $account->id)->where('iban', $iban)->get());
|
||||
}
|
||||
|
||||
}
|
433
tests/Unit/Console/Commands/Import/CreateCSVImportTest.php
Normal file
433
tests/Unit/Console/Commands/Import/CreateCSVImportTest.php
Normal file
@@ -0,0 +1,433 @@
|
||||
<?php
|
||||
/**
|
||||
* CreateCSVImportTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Import;
|
||||
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Routine\FileRoutine;
|
||||
use FireflyIII\Import\Storage\ImportArrayStorage;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Log;
|
||||
use Mockery;
|
||||
use Preferences;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class CreateCSVImportTest
|
||||
*/
|
||||
class CreateCSVImportTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Covers a default run with perfect arguments.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Import\CreateCSVImport
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$jobRepos = $this->mock(ImportJobRepositoryInterface::class);
|
||||
$fileRoutine = $this->mock(FileRoutine::class);
|
||||
$storage = $this->mock(ImportArrayStorage::class);
|
||||
$user = $this->user();
|
||||
$token = new Preference;
|
||||
$importJob = $this->user()->importJobs()->first();
|
||||
$file = storage_path('build/test-upload.csv');
|
||||
$config = storage_path('build/configuration.json');
|
||||
|
||||
// set preferences:
|
||||
$token->data = 'token';
|
||||
|
||||
// mock calls to repository:
|
||||
$userRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($user);
|
||||
$jobRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$jobRepos->shouldReceive('create')->atLeast()->once()->andReturn($importJob);
|
||||
$jobRepos->shouldReceive('storeCLIupload')->atLeast()->once()->andReturn(new MessageBag);
|
||||
$jobRepos->shouldReceive('setConfiguration')->atLeast()->once();
|
||||
|
||||
// job is ready to run.
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'ready_to_run'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'provider_finished'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'storing_data'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'storage_finished'])->atLeast()->once();
|
||||
|
||||
// file routine gets called.
|
||||
$fileRoutine->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$fileRoutine->shouldReceive('run')->atLeast()->once();
|
||||
|
||||
// store data thing gets called.
|
||||
$storage->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$storage->shouldReceive('store')->atLeast()->once();
|
||||
|
||||
// mock Preferences.
|
||||
Preferences::shouldReceive('setForUser')->atLeast()->once()->withArgs([Mockery::any(), 'lastActivity', Mockery::any()]);
|
||||
Preferences::shouldReceive('getForUser')->atLeast()->once()->withArgs([Mockery::any(), 'access_token', null])->andReturn($token);
|
||||
|
||||
|
||||
$parameters = [
|
||||
$file,
|
||||
$config,
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:csv-import ' . implode(' ', $parameters))
|
||||
->expectsOutput(sprintf('Import file : %s', $file))
|
||||
->expectsOutput(sprintf('Configuration file : %s', $config))
|
||||
->expectsOutput('User : #1 (thegrumpydictator@gmail.com)')
|
||||
->expectsOutput(sprintf('Job : %s', $importJob->key))
|
||||
->assertExitCode(0);
|
||||
|
||||
// this method imports nothing so there is nothing to verify.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Covers a default run with perfect arguments, but no import tag
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Import\CreateCSVImport
|
||||
*/
|
||||
public function testHandleNoTag(): void
|
||||
{
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$jobRepos = $this->mock(ImportJobRepositoryInterface::class);
|
||||
$fileRoutine = $this->mock(FileRoutine::class);
|
||||
$storage = $this->mock(ImportArrayStorage::class);
|
||||
$user = $this->user();
|
||||
$token = new Preference;
|
||||
|
||||
$importJob = ImportJob::create(
|
||||
[
|
||||
'key' => 'key-' . random_int(1, 100000),
|
||||
'user_id' => 1,
|
||||
'file_type' => 'csv',
|
||||
'status' => 'new',
|
||||
'errors' => [],
|
||||
]
|
||||
);
|
||||
|
||||
$file = storage_path('build/test-upload.csv');
|
||||
$config = storage_path('build/configuration.json');
|
||||
|
||||
// set preferences:
|
||||
$token->data = 'token';
|
||||
|
||||
// mock calls to repository:
|
||||
$userRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($user);
|
||||
$jobRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$jobRepos->shouldReceive('create')->atLeast()->once()->andReturn($importJob);
|
||||
$jobRepos->shouldReceive('storeCLIupload')->atLeast()->once()->andReturn(new MessageBag);
|
||||
$jobRepos->shouldReceive('setConfiguration')->atLeast()->once();
|
||||
|
||||
// job is ready to run.
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'ready_to_run'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'provider_finished'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'storing_data'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'storage_finished'])->atLeast()->once();
|
||||
|
||||
// file routine gets called.
|
||||
$fileRoutine->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$fileRoutine->shouldReceive('run')->atLeast()->once();
|
||||
|
||||
// store data thing gets called.
|
||||
$storage->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$storage->shouldReceive('store')->atLeast()->once();
|
||||
|
||||
// mock Preferences.
|
||||
Preferences::shouldReceive('setForUser')->atLeast()->once()->withArgs([Mockery::any(), 'lastActivity', Mockery::any()]);
|
||||
Preferences::shouldReceive('getForUser')->atLeast()->once()->withArgs([Mockery::any(), 'access_token', null])->andReturn($token);
|
||||
|
||||
|
||||
$parameters = [
|
||||
$file,
|
||||
$config,
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:csv-import ' . implode(' ', $parameters))
|
||||
->expectsOutput(sprintf('Import file : %s', $file))
|
||||
->expectsOutput(sprintf('Configuration file : %s', $config))
|
||||
->expectsOutput('User : #1 (thegrumpydictator@gmail.com)')
|
||||
->expectsOutput(sprintf('Job : %s', $importJob->key))
|
||||
->expectsOutput('No transactions have been imported :(.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// this method imports nothing so there is nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Covers a default run with perfect arguments, but errors after importing.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Import\CreateCSVImport
|
||||
*/
|
||||
public function testHandleErrors(): void
|
||||
{
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$jobRepos = $this->mock(ImportJobRepositoryInterface::class);
|
||||
$fileRoutine = $this->mock(FileRoutine::class);
|
||||
$storage = $this->mock(ImportArrayStorage::class);
|
||||
$user = $this->user();
|
||||
$token = new Preference;
|
||||
|
||||
$importJob = ImportJob::create(
|
||||
[
|
||||
'key' => 'key-' . random_int(1, 100000),
|
||||
'user_id' => 1,
|
||||
'file_type' => 'csv',
|
||||
'status' => 'new',
|
||||
'errors' => ['I am an error'],
|
||||
]
|
||||
);
|
||||
|
||||
$file = storage_path('build/test-upload.csv');
|
||||
$config = storage_path('build/configuration.json');
|
||||
|
||||
// set preferences:
|
||||
$token->data = 'token';
|
||||
|
||||
// mock calls to repository:
|
||||
$userRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($user);
|
||||
$jobRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$jobRepos->shouldReceive('create')->atLeast()->once()->andReturn($importJob);
|
||||
$jobRepos->shouldReceive('storeCLIupload')->atLeast()->once()->andReturn(new MessageBag);
|
||||
$jobRepos->shouldReceive('setConfiguration')->atLeast()->once();
|
||||
|
||||
// job is ready to run.
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'ready_to_run'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'provider_finished'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'storing_data'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'storage_finished'])->atLeast()->once();
|
||||
|
||||
// file routine gets called.
|
||||
$fileRoutine->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$fileRoutine->shouldReceive('run')->atLeast()->once();
|
||||
|
||||
// store data thing gets called.
|
||||
$storage->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$storage->shouldReceive('store')->atLeast()->once();
|
||||
|
||||
// mock Preferences.
|
||||
Preferences::shouldReceive('setForUser')->atLeast()->once()->withArgs([Mockery::any(), 'lastActivity', Mockery::any()]);
|
||||
Preferences::shouldReceive('getForUser')->atLeast()->once()->withArgs([Mockery::any(), 'access_token', null])->andReturn($token);
|
||||
|
||||
|
||||
$parameters = [
|
||||
$file,
|
||||
$config,
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:csv-import ' . implode(' ', $parameters))
|
||||
->expectsOutput(sprintf('Import file : %s', $file))
|
||||
->expectsOutput(sprintf('Configuration file : %s', $config))
|
||||
->expectsOutput('User : #1 (thegrumpydictator@gmail.com)')
|
||||
->expectsOutput(sprintf('Job : %s', $importJob->key))
|
||||
->expectsOutput('- I am an error')
|
||||
->assertExitCode(0);
|
||||
|
||||
// this method imports nothing so there is nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Crash while storing data.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Import\CreateCSVImport
|
||||
*/
|
||||
public function testHandleCrashStorage(): void
|
||||
{
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$jobRepos = $this->mock(ImportJobRepositoryInterface::class);
|
||||
$fileRoutine = $this->mock(FileRoutine::class);
|
||||
$storage = $this->mock(ImportArrayStorage::class);
|
||||
$user = $this->user();
|
||||
$token = new Preference;
|
||||
$importJob = $this->user()->importJobs()->first();
|
||||
$file = storage_path('build/test-upload.csv');
|
||||
$config = storage_path('build/configuration.json');
|
||||
|
||||
// set preferences:
|
||||
$token->data = 'token';
|
||||
|
||||
// mock calls to repository:
|
||||
$userRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($user);
|
||||
$jobRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$jobRepos->shouldReceive('create')->atLeast()->once()->andReturn($importJob);
|
||||
$jobRepos->shouldReceive('storeCLIupload')->atLeast()->once()->andReturn(new MessageBag);
|
||||
$jobRepos->shouldReceive('setConfiguration')->atLeast()->once();
|
||||
|
||||
// job is ready to run.
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'ready_to_run'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'provider_finished'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'storing_data'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'error'])->atLeast()->once();
|
||||
|
||||
// file routine gets called.
|
||||
$fileRoutine->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$fileRoutine->shouldReceive('run')->atLeast()->once();
|
||||
|
||||
// store data thing gets called.
|
||||
$storage->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$storage->shouldReceive('store')->atLeast()->once()->andThrow(new FireflyException('I am storage error.'));
|
||||
|
||||
// mock Preferences.
|
||||
Preferences::shouldReceive('getForUser')->atLeast()->once()->withArgs([Mockery::any(), 'access_token', null])->andReturn($token);
|
||||
|
||||
|
||||
$parameters = [
|
||||
$file,
|
||||
$config,
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:csv-import ' . implode(' ', $parameters))
|
||||
->expectsOutput(sprintf('Import file : %s', $file))
|
||||
->expectsOutput(sprintf('Configuration file : %s', $config))
|
||||
->expectsOutput('User : #1 (thegrumpydictator@gmail.com)')
|
||||
->expectsOutput(sprintf('Job : %s', $importJob->key))
|
||||
->expectsOutput('The import routine crashed: I am storage error.')
|
||||
->assertExitCode(1);
|
||||
|
||||
// this method imports nothing so there is nothing to verify.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The file processor crashes for some reason.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Import\CreateCSVImport
|
||||
*/
|
||||
public function testHandleCrashProcess(): void
|
||||
{
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$jobRepos = $this->mock(ImportJobRepositoryInterface::class);
|
||||
$fileRoutine = $this->mock(FileRoutine::class);
|
||||
$storage = $this->mock(ImportArrayStorage::class);
|
||||
$user = $this->user();
|
||||
$token = new Preference;
|
||||
$importJob = $this->user()->importJobs()->first();
|
||||
$file = storage_path('build/test-upload.csv');
|
||||
$config = storage_path('build/configuration.json');
|
||||
|
||||
// set preferences:
|
||||
$token->data = 'token';
|
||||
|
||||
// mock calls to repository:
|
||||
$userRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($user);
|
||||
$jobRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$jobRepos->shouldReceive('create')->atLeast()->once()->andReturn($importJob);
|
||||
$jobRepos->shouldReceive('storeCLIupload')->atLeast()->once()->andReturn(new MessageBag);
|
||||
$jobRepos->shouldReceive('setConfiguration')->atLeast()->once();
|
||||
|
||||
// job is ready to run.
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'ready_to_run'])->atLeast()->once();
|
||||
$jobRepos->shouldReceive('setStatus')->withArgs([Mockery::any(), 'error'])->atLeast()->once();
|
||||
|
||||
// file routine gets called.
|
||||
$fileRoutine->shouldReceive('setImportJob')->atLeast()->once();
|
||||
$fileRoutine->shouldReceive('run')->atLeast()->once()->andThrows(new FireflyException('I am big bad exception.'));
|
||||
|
||||
// mock Preferences.
|
||||
Preferences::shouldReceive('getForUser')->atLeast()->once()->withArgs([Mockery::any(), 'access_token', null])->andReturn($token);
|
||||
|
||||
$parameters = [
|
||||
$file,
|
||||
$config,
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:csv-import ' . implode(' ', $parameters))
|
||||
->expectsOutput(sprintf('Import file : %s', $file))
|
||||
->expectsOutput(sprintf('Configuration file : %s', $config))
|
||||
->expectsOutput('User : #1 (thegrumpydictator@gmail.com)')
|
||||
->expectsOutput(sprintf('Job : %s', $importJob->key))
|
||||
->expectsOutput('The import routine crashed: I am big bad exception.')
|
||||
->assertExitCode(1);
|
||||
|
||||
// this method imports nothing so there is nothing to verify.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw error when storing data.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Import\CreateCSVImport
|
||||
*/
|
||||
public function testHandleFileStoreError(): void
|
||||
{
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$jobRepos = $this->mock(ImportJobRepositoryInterface::class);
|
||||
$this->mock(FileRoutine::class);
|
||||
$this->mock(ImportArrayStorage::class);
|
||||
$user = $this->user();
|
||||
$token = new Preference;
|
||||
$importJob = $this->user()->importJobs()->first();
|
||||
$file = storage_path('build/test-upload.csv');
|
||||
$config = storage_path('build/configuration.json');
|
||||
$messages = new MessageBag;
|
||||
$messages->add('file', 'Some file error.');
|
||||
|
||||
// set preferences:
|
||||
$token->data = 'token';
|
||||
|
||||
// mock calls to repository:
|
||||
$userRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($user);
|
||||
$jobRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$jobRepos->shouldReceive('create')->atLeast()->once()->andReturn($importJob);
|
||||
$jobRepos->shouldReceive('storeCLIupload')->atLeast()->once()->andReturn($messages);
|
||||
|
||||
// mock Preferences.
|
||||
Preferences::shouldReceive('getForUser')->atLeast()->once()->withArgs([Mockery::any(), 'access_token', null])->andReturn($token);
|
||||
|
||||
|
||||
$parameters = [
|
||||
$file,
|
||||
$config,
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
];
|
||||
$this->artisan('firefly-iii:csv-import ' . implode(' ', $parameters))
|
||||
->expectsOutput(sprintf('Import file : %s', $file))
|
||||
->expectsOutput(sprintf('Configuration file : %s', $config))
|
||||
->expectsOutput('User : #1 (thegrumpydictator@gmail.com)')
|
||||
->expectsOutput(sprintf('Job : %s', $importJob->key))
|
||||
->expectsOutput('Some file error.')
|
||||
->assertExitCode(1);
|
||||
|
||||
// this method imports nothing so there is nothing to verify.
|
||||
}
|
||||
}
|
148
tests/Unit/Console/Commands/Integrity/ReportEmptyObjectsTest.php
Normal file
148
tests/Unit/Console/Commands/Integrity/ReportEmptyObjectsTest.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportEmptyObjectsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Integrity;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ReportEmptyObjectsTest
|
||||
*/
|
||||
class ReportEmptyObjectsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run basic test routine.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Integrity\ReportEmptyObjects
|
||||
*/
|
||||
public function testHandleBudget(): void
|
||||
{
|
||||
$user = $this->user();
|
||||
$budget = Budget::create(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Some budget',
|
||||
]);
|
||||
$budgetLine = sprintf('User #%d (%s) has budget #%d ("%s") which has no transaction journals.',
|
||||
$user->id, $user->email, $budget->id, $budget->name);
|
||||
$budgetLimitLine = sprintf('User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$user->id, $user->email, $budget->id, $budget->name);
|
||||
|
||||
$this->artisan('firefly-iii:report-empty-objects')
|
||||
->expectsOutput($budgetLine)
|
||||
->expectsOutput($budgetLimitLine)
|
||||
->assertExitCode(0);
|
||||
$budget->forceDelete();
|
||||
|
||||
// this method changes no objects so there is nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Run basic test routine.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Integrity\ReportEmptyObjects
|
||||
*/
|
||||
public function testHandleCategory(): void
|
||||
{
|
||||
$user = $this->user();
|
||||
$category = Category::create(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Some category',
|
||||
]);
|
||||
$categoryLine = sprintf('User #%d (%s) has category #%d ("%s") which has no transaction journals.',
|
||||
$user->id, $user->email, $category->id, $category->name);
|
||||
|
||||
$this->artisan('firefly-iii:report-empty-objects')
|
||||
->expectsOutput($categoryLine)
|
||||
->assertExitCode(0);
|
||||
$category->forceDelete();
|
||||
|
||||
// this method changes no objects so there is nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Run basic test routine.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Integrity\ReportEmptyObjects
|
||||
*/
|
||||
public function testHandleTag(): void
|
||||
{
|
||||
$user = $this->user();
|
||||
$tag = Tag::create(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'tag' => 'Some tag',
|
||||
'tagMode' => 'nothing',
|
||||
]);
|
||||
$tagLine = sprintf('User #%d (%s) has tag #%d ("%s") which has no transaction journals.',
|
||||
$user->id, $user->email, $tag->id, $tag->tag);
|
||||
|
||||
$this->artisan('firefly-iii:report-empty-objects')
|
||||
->expectsOutput($tagLine)
|
||||
->assertExitCode(0);
|
||||
$tag->forceDelete();
|
||||
|
||||
// this method changes no objects so there is nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Run basic test routine.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Integrity\ReportEmptyObjects
|
||||
*/
|
||||
public function testHandleAccount(): void
|
||||
{
|
||||
$user = $this->user();
|
||||
$account = Account::create(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Some account',
|
||||
'account_type_id' => 1,
|
||||
]);
|
||||
$tagLine = sprintf('User #%d (%s) has account #%d ("%s") which has no transactions.',
|
||||
$user->id, $user->email, $account->id, $account->name);
|
||||
|
||||
$this->artisan('firefly-iii:report-empty-objects')
|
||||
->expectsOutput($tagLine)
|
||||
->assertExitCode(0);
|
||||
$account->forceDelete();
|
||||
|
||||
// this method changes no objects so there is nothing to verify.
|
||||
}
|
||||
|
||||
|
||||
}
|
85
tests/Unit/Console/Commands/Integrity/ReportSumTest.php
Normal file
85
tests/Unit/Console/Commands/Integrity/ReportSumTest.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportSumTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Integrity;
|
||||
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ReportSumTest
|
||||
*/
|
||||
class ReportSumTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Integrity\ReportSum
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$repository = $this->mock(UserRepositoryInterface::class);
|
||||
$repository->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
|
||||
$this->artisan('firefly-iii:report-sum')
|
||||
->expectsOutput(sprintf('Amount integrity OK for user #%d', $this->user()->id))
|
||||
->assertExitCode(0);
|
||||
|
||||
// this method changes no objects so there is nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Create transaction to make balance uneven.
|
||||
* @covers \FireflyIII\Console\Commands\Integrity\ReportSum
|
||||
*/
|
||||
public function testHandleUneven(): void
|
||||
{
|
||||
$transaction = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $this->getRandomWithdrawal()->id,
|
||||
'user_id' => 1,
|
||||
'account_id' => $this->getRandomAsset()->id,
|
||||
'amount' => 10,
|
||||
]
|
||||
);
|
||||
|
||||
$repository = $this->mock(UserRepositoryInterface::class);
|
||||
$repository->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
|
||||
$this->artisan('firefly-iii:report-sum')
|
||||
->expectsOutput(sprintf('Error: Transactions for user #%d (%s) are off by %s!', $this->user()->id, $this->user()->email, '10.0'))
|
||||
->assertExitCode(0);
|
||||
$transaction->forceDelete();
|
||||
|
||||
// this method changes no objects so there is nothing to verify.
|
||||
}
|
||||
}
|
404
tests/Unit/Console/Commands/Tools/ApplyRulesTest.php
Normal file
404
tests/Unit/Console/Commands/Tools/ApplyRulesTest.php
Normal file
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
/**
|
||||
* ApplyRulesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Tools;
|
||||
|
||||
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class ApplyRulesTest
|
||||
*/
|
||||
class ApplyRulesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic call with everything perfect (and ALL rules).
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Tools\ApplyRules
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$ruleRepos = $this->mock(RuleRepositoryInterface::class);
|
||||
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$collector = $this->mock(GroupCollectorInterface::class);
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$ruleEngine = $this->mock(RuleEngine::class);
|
||||
|
||||
// data
|
||||
$asset = $this->getRandomAsset();
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
$group = $this->user()->ruleGroups()->first();
|
||||
$rule = $this->user()->rules()->first();
|
||||
$groups = new Collection([$group]);
|
||||
$rules = new Collection([$rule]);
|
||||
|
||||
// expected calls:
|
||||
$ruleRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleGroupRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([1])->andReturn($asset);
|
||||
$journalRepos->shouldReceive('firstNull')->atLeast()->once()->andReturn($journal);
|
||||
|
||||
$ruleGroupRepos->shouldReceive('getActiveGroups')->atLeast()->once()->andReturn($groups);
|
||||
$ruleGroupRepos->shouldReceive('getActiveStoreRules')->atLeast()->once()->andReturn($rules);
|
||||
|
||||
|
||||
$collector->shouldReceive('setUser')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setAccounts')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setRange')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('getExtractedJournals')->atLeast()->once()->andReturn([[], [], []]);
|
||||
|
||||
$ruleEngine->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('setRulesToApply')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('processJournalArray')->times(3);
|
||||
|
||||
$parameters = [
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
'--accounts=1',
|
||||
'--all_rules',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:apply-rules ' . implode(' ', $parameters))
|
||||
->expectsOutput('Will apply 1 rule(s) to 3 transaction(s).')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
|
||||
// this method changes no objects so there is nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic call with everything perfect (and ALL rules), but no rules will be selected.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Tools\ApplyRules
|
||||
*/
|
||||
public function testHandEmptye(): void
|
||||
{
|
||||
$ruleRepos = $this->mock(RuleRepositoryInterface::class);
|
||||
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$collector = $this->mock(GroupCollectorInterface::class);
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$ruleEngine = $this->mock(RuleEngine::class);
|
||||
|
||||
// data
|
||||
$asset = $this->getRandomAsset();
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
$group = $this->user()->ruleGroups()->first();
|
||||
$groups = new Collection([$group]);
|
||||
|
||||
// expected calls:
|
||||
$ruleRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleGroupRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([1])->andReturn($asset);
|
||||
$journalRepos->shouldReceive('firstNull')->atLeast()->once()->andReturn($journal);
|
||||
|
||||
$ruleGroupRepos->shouldReceive('getActiveGroups')->atLeast()->once()->andReturn($groups);
|
||||
$ruleGroupRepos->shouldReceive('getActiveStoreRules')->atLeast()->once()->andReturn(new Collection);
|
||||
|
||||
|
||||
$collector->shouldReceive('setUser')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setAccounts')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setRange')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('getExtractedJournals')->atLeast()->once()->andReturn([[], [], []]);
|
||||
|
||||
$ruleEngine->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('setRulesToApply')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('processJournalArray')->times(3);
|
||||
|
||||
$parameters = [
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
'--accounts=1',
|
||||
'--all_rules',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:apply-rules ' . implode(' ', $parameters))
|
||||
->expectsOutput('No rules or rule groups have been included.')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic call with everything perfect (and ALL rules) and dates.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Tools\ApplyRules
|
||||
*/
|
||||
public function testHandleDate(): void
|
||||
{
|
||||
$ruleRepos = $this->mock(RuleRepositoryInterface::class);
|
||||
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
|
||||
$this->mock(JournalRepositoryInterface::class);
|
||||
$collector = $this->mock(GroupCollectorInterface::class);
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$ruleEngine = $this->mock(RuleEngine::class);
|
||||
|
||||
// data
|
||||
$asset = $this->getRandomAsset();
|
||||
$group = $this->user()->ruleGroups()->first();
|
||||
$rule = $this->user()->rules()->first();
|
||||
$groups = new Collection([$group]);
|
||||
$rules = new Collection([$rule]);
|
||||
|
||||
// expected calls:
|
||||
$ruleRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleGroupRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([1])->andReturn($asset);
|
||||
|
||||
$ruleGroupRepos->shouldReceive('getActiveGroups')->atLeast()->once()->andReturn($groups);
|
||||
$ruleGroupRepos->shouldReceive('getActiveStoreRules')->atLeast()->once()->andReturn($rules);
|
||||
|
||||
|
||||
$collector->shouldReceive('setUser')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setAccounts')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setRange')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('getExtractedJournals')->atLeast()->once()->andReturn([[], [], []]);
|
||||
|
||||
$ruleEngine->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('setRulesToApply')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('processJournalArray')->times(3);
|
||||
|
||||
$parameters = [
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
'--accounts=1',
|
||||
'--all_rules',
|
||||
'--start_date=2019-01-31',
|
||||
'--end_date=2019-01-01',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:apply-rules ' . implode(' ', $parameters))
|
||||
->expectsOutput('Will apply 1 rule(s) to 3 transaction(s).')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will submit some rules to apply.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Tools\ApplyRules
|
||||
*/
|
||||
public function testHandleRules(): void
|
||||
{
|
||||
$ruleRepos = $this->mock(RuleRepositoryInterface::class);
|
||||
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$collector = $this->mock(GroupCollectorInterface::class);
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$ruleEngine = $this->mock(RuleEngine::class);
|
||||
|
||||
// data
|
||||
$asset = $this->getRandomAsset();
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
$group = $this->user()->ruleGroups()->first();
|
||||
$groups = new Collection([$group]);
|
||||
$activeRule = $this->user()->rules()->where('active', 1)->inRandomOrder()->first();
|
||||
$inactiveRule = $this->user()->rules()->where('active', 0)->inRandomOrder()->first();
|
||||
$rules = new Collection([$activeRule]);
|
||||
|
||||
// expected calls:
|
||||
$ruleRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleGroupRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([1])->andReturn($asset);
|
||||
$journalRepos->shouldReceive('firstNull')->atLeast()->once()->andReturn($journal);
|
||||
|
||||
$ruleRepos->shouldReceive('find')->atLeast()->once()->withArgs([$activeRule->id])->andReturn($activeRule);
|
||||
$ruleRepos->shouldReceive('find')->atLeast()->once()->withArgs([$inactiveRule->id])->andReturn($inactiveRule);
|
||||
|
||||
$ruleGroupRepos->shouldReceive('getActiveGroups')->atLeast()->once()->andReturn($groups);
|
||||
$ruleGroupRepos->shouldReceive('getActiveStoreRules')->atLeast()->once()->andReturn($rules);
|
||||
|
||||
|
||||
$collector->shouldReceive('setUser')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setAccounts')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setRange')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('getExtractedJournals')->atLeast()->once()->andReturn([[], [], []]);
|
||||
|
||||
$ruleEngine->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('setRulesToApply')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('processJournalArray')->times(3);
|
||||
|
||||
$parameters = [
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
'--accounts=1',
|
||||
sprintf('--rules=%d,%d', $activeRule->id, $inactiveRule->id),
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:apply-rules ' . implode(' ', $parameters))
|
||||
->expectsOutput('Will apply 1 rule(s) to 3 transaction(s).')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic call with two rule groups. One active, one inactive.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Tools\ApplyRules
|
||||
*/
|
||||
public function testHandleRuleGroups(): void
|
||||
{
|
||||
$ruleRepos = $this->mock(RuleRepositoryInterface::class);
|
||||
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$collector = $this->mock(GroupCollectorInterface::class);
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$ruleEngine = $this->mock(RuleEngine::class);
|
||||
|
||||
$activeGroup = $this->user()->ruleGroups()->where('active', 1)->inRandomOrder()->first();
|
||||
$inactiveGroup = $this->user()->ruleGroups()->where('active', 0)->inRandomOrder()->first();
|
||||
|
||||
// data
|
||||
$asset = $this->getRandomAsset();
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
$rule = $this->user()->rules()->first();
|
||||
$groups = new Collection([$activeGroup]);
|
||||
$rules = new Collection([$rule]);
|
||||
|
||||
// expected calls:
|
||||
$ruleRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleGroupRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([1])->andReturn($asset);
|
||||
$journalRepos->shouldReceive('firstNull')->atLeast()->once()->andReturn($journal);
|
||||
|
||||
$ruleGroupRepos->shouldReceive('getActiveGroups')->atLeast()->once()->andReturn($groups);
|
||||
$ruleGroupRepos->shouldReceive('getActiveStoreRules')->atLeast()->once()->andReturn($rules);
|
||||
$ruleGroupRepos->shouldReceive('find')->atLeast()->once()->withArgs([$activeGroup->id])->andReturn($activeGroup);
|
||||
$ruleGroupRepos->shouldReceive('find')->atLeast()->once()->withArgs([$inactiveGroup->id])->andReturn($inactiveGroup);
|
||||
|
||||
$collector->shouldReceive('setUser')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setAccounts')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('setRange')->atLeast()->once()->andReturnSelf();
|
||||
$collector->shouldReceive('getExtractedJournals')->atLeast()->once()->andReturn([[], [], []]);
|
||||
|
||||
$ruleEngine->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('setRulesToApply')->atLeast()->once();
|
||||
$ruleEngine->shouldReceive('processJournalArray')->times(3);
|
||||
|
||||
$parameters = [
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
'--accounts=1',
|
||||
sprintf('--rule_groups=%d,%d', $activeGroup->id, $inactiveGroup->id),
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:apply-rules ' . implode(' ', $parameters))
|
||||
->expectsOutput(sprintf('Will ignore inactive rule group #%d ("%s")', $inactiveGroup->id, $inactiveGroup->title))
|
||||
// one rule out of 2 groups:
|
||||
->expectsOutput('Will apply 1 rule(s) to 3 transaction(s).')
|
||||
->expectsOutput('Done!')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic call but no accounts submitted.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Tools\ApplyRules
|
||||
*/
|
||||
public function testHandleNoAccounts(): void
|
||||
{
|
||||
$ruleRepos = $this->mock(RuleRepositoryInterface::class);
|
||||
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
|
||||
$this->mock(JournalRepositoryInterface::class);
|
||||
$this->mock(GroupCollectorInterface::class);
|
||||
$this->mock(AccountRepositoryInterface::class);
|
||||
$this->mock(RuleEngine::class);
|
||||
|
||||
// expected calls:
|
||||
$ruleRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleGroupRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
|
||||
$parameters = [
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
'--accounts=',
|
||||
'--all_rules',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:apply-rules ' . implode(' ', $parameters))
|
||||
->expectsOutput('Please use the --accounts option to indicate the accounts to apply rules to.')
|
||||
->assertExitCode(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic call but only one expense account submitted
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Tools\ApplyRules
|
||||
*/
|
||||
public function testHandleExpenseAccounts(): void
|
||||
{
|
||||
$ruleRepos = $this->mock(RuleRepositoryInterface::class);
|
||||
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
|
||||
$this->mock(JournalRepositoryInterface::class);
|
||||
$this->mock(GroupCollectorInterface::class);
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$this->mock(RuleEngine::class);
|
||||
|
||||
// data
|
||||
$expense = $this->getRandomExpense();
|
||||
|
||||
// expected calls:
|
||||
$ruleRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$ruleGroupRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([$expense->id])->andReturn($expense);
|
||||
|
||||
|
||||
$parameters = [
|
||||
'--user=1',
|
||||
'--token=token',
|
||||
'--accounts=' . $expense->id,
|
||||
'--all_rules',
|
||||
];
|
||||
|
||||
$this->artisan('firefly-iii:apply-rules ' . implode(' ', $parameters))
|
||||
->expectsOutput('Please make sure all accounts in --accounts are asset accounts or liabilities.')
|
||||
->assertExitCode(1);
|
||||
}
|
||||
|
||||
}
|
341
tests/Unit/Console/Commands/Upgrade/AccountCurrenciesTest.php
Normal file
341
tests/Unit/Console/Commands/Upgrade/AccountCurrenciesTest.php
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountCurrenciesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Upgrade;
|
||||
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\Configuration;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Mockery;
|
||||
use Preferences;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class AccountCurrenciesTest
|
||||
*/
|
||||
class AccountCurrenciesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perfect run without opening balance.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$pref = new Preference;
|
||||
$pref->data = 'EUR';
|
||||
$account = $this->getRandomAsset();
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
|
||||
// mock calls
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturn('1');
|
||||
$accountRepos->shouldReceive('getOpeningBalance')->atLeast()->once()->andReturn(null);
|
||||
$accountRepos->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$account]));
|
||||
$userRepos->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput('All accounts are OK.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// nothing changed, so nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Perfect run without opening balance.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandleNotNull(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$pref = new Preference;
|
||||
$pref->data = 'EUR';
|
||||
$account = $this->getRandomAsset();
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
|
||||
// mock calls
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
|
||||
// account reports USD
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturn('2');
|
||||
// journal is EUR.
|
||||
$accountRepos->shouldReceive('getOpeningBalance')->atLeast()->once()->andReturn($journal);
|
||||
$accountRepos->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$account]));
|
||||
$userRepos->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name))
|
||||
->assertExitCode(0);
|
||||
|
||||
// check if currency has been changed for the journal + transactions.
|
||||
$this->assertCount(1, TransactionJournal::where('id', $journal->id)->where('transaction_currency_id', 2)->get());
|
||||
$this->assertCount(2, Transaction::where('transaction_journal_id', $journal->id)->where('transaction_currency_id', 2)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Perfect run with opening balance.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandleOpeningBalance(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$pref = new Preference;
|
||||
$pref->data = 'EUR';
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
$account = $this->getRandomAsset();
|
||||
// mock calls
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturn('1');
|
||||
$accountRepos->shouldReceive('getOpeningBalance')->atLeast()->once()->andReturn($journal);
|
||||
$userRepos->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
$accountRepos->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$account]));
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput('All accounts are OK.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// nothing changed, dont check output.
|
||||
}
|
||||
|
||||
/**
|
||||
* Perfect run with opening balance with different currencies
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandleDifferent(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$pref = new Preference;
|
||||
$pref->data = 'USD';
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
$account = $this->getRandomAsset();
|
||||
$euro = TransactionCurrency::where('code', 'EUR')->first();
|
||||
|
||||
// delete meta data of account just in case:
|
||||
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();
|
||||
// mock calls
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturn('0');
|
||||
$accountRepos->shouldReceive('getOpeningBalance')->atLeast()->once()->andReturn($journal);
|
||||
$userRepos->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
$accountRepos->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$account]));
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput(sprintf('Account #%d ("%s") now has a currency setting (#%d).', $account->id, $account->name, $euro->id))
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify account meta data change.
|
||||
$this->assertCount(1,
|
||||
AccountMeta::where('account_id', $account->id)
|
||||
->where('name', 'currency_id')
|
||||
->where('data', $euro->id)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* No known currency preferences.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandleZeroPreference(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$pref = new Preference;
|
||||
$pref->data = 'EUR';
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$account = $this->getRandomAsset();
|
||||
$euro = TransactionCurrency::where('code', 'EUR')->first();
|
||||
// mock calls
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturn('0');
|
||||
$accountRepos->shouldReceive('getOpeningBalance')->atLeast()->once()->andReturn(null);
|
||||
$userRepos->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
$accountRepos->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$account]));
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput(sprintf('Account #%d ("%s") now has a currency setting (%s).',
|
||||
$account->id, $account->name, $euro->code
|
||||
))
|
||||
->expectsOutput('Corrected 1 account(s).')
|
||||
->assertExitCode(0);
|
||||
|
||||
$this->assertCount(1, AccountMeta::where('account_id', $account->id)
|
||||
->where('name', 'currency_id')
|
||||
->where('data', $euro->id)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandleNoPreference(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$pref = new Preference;
|
||||
$pref->data = false;
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$account = $this->getRandomAsset();
|
||||
// mock calls
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturn('1');
|
||||
$accountRepos->shouldReceive('getOpeningBalance')->atLeast()->once()->andReturn(null);
|
||||
$userRepos->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
$accountRepos->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$account]));
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput('All accounts are OK.')
|
||||
->assertExitCode(0);
|
||||
// nothing changed, so nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandleInvalidPreference(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$pref = new Preference;
|
||||
$pref->data = 'ABC';
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$userRepos = $this->mock(UserRepositoryInterface::class);
|
||||
$account = $this->getRandomAsset();
|
||||
|
||||
// mock calls
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$accountRepos->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$account]));
|
||||
$userRepos->shouldReceive('all')->atLeast()->once()->andReturn(new Collection([$this->user()]));
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput('User has a preference for "ABC", but this currency does not exist.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// nothing changed, so nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\AccountCurrencies
|
||||
*/
|
||||
public function testHandleAlreadyExecuted(): void
|
||||
{
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
$pref = new Preference;
|
||||
$pref->data = 'EUR';
|
||||
$this->mock(AccountRepositoryInterface::class);
|
||||
$this->mock(UserRepositoryInterface::class);
|
||||
|
||||
// check config
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_account_currencies', false])->andReturn($true);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_account_currencies', true]);
|
||||
|
||||
// check preferences:
|
||||
Preferences::shouldReceive('getForUser')->withArgs([Mockery::any(), 'currencyPreference', 'EUR'])->andReturn($pref);
|
||||
|
||||
$this->artisan('firefly-iii:account-currencies')
|
||||
->expectsOutput('This command has already been executed.')
|
||||
->assertExitCode(0);
|
||||
// nothing changed, so nothing to verify.
|
||||
}
|
||||
}
|
244
tests/Unit/Console/Commands/Upgrade/BackToJournalsTest.php
Normal file
244
tests/Unit/Console/Commands/Upgrade/BackToJournalsTest.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
/**
|
||||
* BackToJournalsTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Upgrade;
|
||||
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Configuration;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class BackToJournalsTest
|
||||
*/
|
||||
class BackToJournalsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perfect run. Will report on nothing.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\BackToJournals
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// verify preference:
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_back_to_journals', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_migrated_to_groups', false])->andReturn($true);
|
||||
|
||||
// set new preference after running:
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_back_to_journals', true]);
|
||||
|
||||
$this->artisan('firefly-iii:back-to-journals')
|
||||
->expectsOutput('Check 0 transaction journal(s) for budget info.')
|
||||
->expectsOutput('Check 0 transaction journal(s) for category info.')
|
||||
->assertExitCode(0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction has a budget, journal doesn't.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\BackToJournals
|
||||
*/
|
||||
public function testHandleBudget(): void
|
||||
{
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->first();
|
||||
/** @var Budget $budget */
|
||||
$budget = $this->user()->budgets()->first();
|
||||
$transaction->budgets()->sync([$budget->id]);
|
||||
$journal->budgets()->sync([]);
|
||||
$journal->save();
|
||||
$transaction->save();
|
||||
|
||||
|
||||
// verify preference:
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_back_to_journals', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_migrated_to_groups', false])->andReturn($true);
|
||||
|
||||
// set new preference after running:
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_back_to_journals', true]);
|
||||
|
||||
$this->artisan('firefly-iii:back-to-journals')
|
||||
->expectsOutput('Check 1 transaction journal(s) for budget info.')
|
||||
->expectsOutput('Check 0 transaction journal(s) for category info.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// transaction should have no budget:
|
||||
$this->assertEquals(0, $transaction->budgets()->count());
|
||||
// journal should have one.
|
||||
$this->assertEquals(1, $journal->budgets()->count());
|
||||
// should be $budget:
|
||||
$this->assertEquals($budget->id, $journal->budgets()->first()->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction has a category, journal doesn't.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\BackToJournals
|
||||
*/
|
||||
public function testHandleCategory(): void
|
||||
{
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->first();
|
||||
/** @var Category $category */
|
||||
$category = $this->user()->categories()->first();
|
||||
$transaction->categories()->sync([$category->id]);
|
||||
$journal->categories()->sync([]);
|
||||
$journal->save();
|
||||
$transaction->save();
|
||||
|
||||
// verify preference:
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_back_to_journals', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_migrated_to_groups', false])->andReturn($true);
|
||||
|
||||
// set new preference after running:
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_back_to_journals', true]);
|
||||
|
||||
$this->artisan('firefly-iii:back-to-journals')
|
||||
->expectsOutput('Check 0 transaction journal(s) for budget info.')
|
||||
->expectsOutput('Check 1 transaction journal(s) for category info.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// transaction should have no category:
|
||||
$this->assertEquals(0, $transaction->categories()->count());
|
||||
// journal should have one.
|
||||
$this->assertEquals(1, $journal->categories()->count());
|
||||
// should be $category:
|
||||
$this->assertEquals($category->id, $journal->categories()->first()->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction has a budget, journal has another
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\BackToJournals
|
||||
*/
|
||||
public function testHandleDifferentBudget(): void
|
||||
{
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->first();
|
||||
/** @var Budget $budget */
|
||||
$budget = $this->user()->budgets()->first();
|
||||
$otherBudget = $this->user()->budgets()->where('id', '!=', $budget->id)->first();
|
||||
$transaction->budgets()->sync([$budget->id]);
|
||||
$journal->budgets()->sync([$otherBudget->id]);
|
||||
$journal->save();
|
||||
$transaction->save();
|
||||
|
||||
|
||||
// verify preference:
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_back_to_journals', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_migrated_to_groups', false])->andReturn($true);
|
||||
|
||||
// set new preference after running:
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_back_to_journals', true]);
|
||||
|
||||
$this->artisan('firefly-iii:back-to-journals')
|
||||
->expectsOutput('Check 1 transaction journal(s) for budget info.')
|
||||
->expectsOutput('Check 0 transaction journal(s) for category info.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// transaction should have no budget:
|
||||
$this->assertEquals(0, $transaction->budgets()->count());
|
||||
// journal should have one.
|
||||
$this->assertEquals(1, $journal->budgets()->count());
|
||||
// should be $budget:
|
||||
$this->assertEquals($budget->id, $journal->budgets()->first()->id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transaction has a category, journal has another
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\BackToJournals
|
||||
*/
|
||||
public function testHandleDifferentCategory(): void
|
||||
{
|
||||
$journal = $this->getRandomWithdrawal();
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->first();
|
||||
/** @var Category $category */
|
||||
$category = $this->user()->categories()->first();
|
||||
$otherCategory = $this->user()->categories()->where('id', '!=', $category->id)->first();
|
||||
$transaction->categories()->sync([$category->id]);
|
||||
$journal->categories()->sync([$otherCategory->id]);
|
||||
$journal->save();
|
||||
$transaction->save();
|
||||
|
||||
// verify preference:
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$true = new Configuration;
|
||||
$true->data = true;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_back_to_journals', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_migrated_to_groups', false])->andReturn($true);
|
||||
|
||||
// set new preference after running:
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_back_to_journals', true]);
|
||||
|
||||
$this->artisan('firefly-iii:back-to-journals')
|
||||
->expectsOutput('Check 0 transaction journal(s) for budget info.')
|
||||
->expectsOutput('Check 1 transaction journal(s) for category info.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// transaction should have no category:
|
||||
$this->assertEquals(0, $transaction->categories()->count());
|
||||
// journal should have one.
|
||||
$this->assertEquals(1, $journal->categories()->count());
|
||||
// should be $category:
|
||||
$this->assertEquals($category->id, $journal->categories()->first()->id);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetLimitCurrencyTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Upgrade;
|
||||
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Configuration;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class BudgetLimitCurrencyTest
|
||||
*/
|
||||
class BudgetLimitCurrencyTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\BudgetLimitCurrency
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$this->artisan('firefly-iii:bl-currency')
|
||||
->assertExitCode(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bad budget limit.
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\BudgetLimitCurrency
|
||||
*/
|
||||
public function testHandleBadLimit(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
$budget = $this->user()->budgets()->first();
|
||||
$limit = BudgetLimit::create(
|
||||
[
|
||||
'budget_id' => $budget->id,
|
||||
'amount' => '10',
|
||||
'start_date' => '2019-01-01',
|
||||
'end_date' => '2019-01-31',
|
||||
]);
|
||||
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_bl_currency', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_bl_currency', true]);
|
||||
|
||||
$this->artisan('firefly-iii:bl-currency')
|
||||
->expectsOutput(
|
||||
sprintf('Budget limit #%d (part of budget "%s") now has a currency setting (%s).',
|
||||
$limit->id, $budget->name, 'Euro'
|
||||
)
|
||||
)
|
||||
->assertExitCode(0);
|
||||
|
||||
// assume currency is filled in.
|
||||
$this->assertCount(1, BudgetLimit::where('id', $limit->id)->where('transaction_currency_id', 1)->get());
|
||||
}
|
||||
}
|
129
tests/Unit/Console/Commands/Upgrade/CCLiabilitiesTest.php
Normal file
129
tests/Unit/Console/Commands/Upgrade/CCLiabilitiesTest.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* CCLiabilitiesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Upgrade;
|
||||
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Configuration;
|
||||
use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class CCLiabilitiesTest
|
||||
*/
|
||||
class CCLiabilitiesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\CCLiabilities
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_cc_liabilities', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_cc_liabilities', true]);
|
||||
|
||||
|
||||
$this->artisan('firefly-iii:cc-liabilities')
|
||||
->expectsOutput('No incorrectly stored credit card liabilities.')
|
||||
->assertExitCode(0);
|
||||
|
||||
// nothing changed, so nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add type to make the script run.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\CCLiabilities
|
||||
*/
|
||||
public function testHandleEmpty(): void
|
||||
{
|
||||
$type = AccountType::create(
|
||||
[
|
||||
'type' => AccountType::CREDITCARD,
|
||||
]
|
||||
);
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_cc_liabilities', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_cc_liabilities', true]);
|
||||
|
||||
|
||||
$this->artisan('firefly-iii:cc-liabilities')
|
||||
->expectsOutput('No incorrectly stored credit card liabilities.')
|
||||
->assertExitCode(0);
|
||||
$type->forceDelete();
|
||||
// nothing changed, so nothing to verify.
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some things to make it trigger.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\CCLiabilities
|
||||
*/
|
||||
public function testHandleCase(): void
|
||||
{
|
||||
$type = AccountType::create(
|
||||
[
|
||||
'type' => AccountType::CREDITCARD,
|
||||
]
|
||||
);
|
||||
|
||||
$account = Account::create(
|
||||
[
|
||||
'name' => 'CC',
|
||||
'user_id' => 1,
|
||||
'account_type_id' => $type->id,
|
||||
|
||||
]
|
||||
);
|
||||
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_cc_liabilities', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_cc_liabilities', true]);
|
||||
|
||||
|
||||
$this->artisan('firefly-iii:cc-liabilities')
|
||||
->expectsOutput(sprintf('Converted credit card liability account "%s" (#%d) to generic debt liability.', $account->name, $account->id))
|
||||
->expectsOutput('Credit card liability types are no longer supported and have been converted to generic debts. See: http://bit.ly/FF3-credit-cards')
|
||||
->assertExitCode(0);
|
||||
|
||||
// verify new type.
|
||||
$this->assertCount(0, Account::where('id', $account->id)->where('account_type_id', $type->id)->get());
|
||||
|
||||
$account->forceDelete();
|
||||
$type->forceDelete();
|
||||
|
||||
}
|
||||
}
|
354
tests/Unit/Console/Commands/Upgrade/JournalCurrenciesTest.php
Normal file
354
tests/Unit/Console/Commands/Upgrade/JournalCurrenciesTest.php
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
/**
|
||||
* JournalCurrenciesTest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Unit\Console\Commands\Upgrade;
|
||||
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Configuration;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Mockery;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class JournalCurrenciesTest
|
||||
*/
|
||||
class JournalCurrenciesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic run. Would not change anything.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\JournalCurrencies
|
||||
*/
|
||||
public function testHandle(): void
|
||||
{
|
||||
// mock classes
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$euro = TransactionCurrency::find(1);
|
||||
|
||||
// update transfer if necessary for the test:
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_journal_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_journal_currencies', true]);
|
||||
|
||||
// mock stuff
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::TRANSFER]])
|
||||
->andReturn(new Collection);
|
||||
|
||||
// for the "other journals" check, return nothing.
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]])
|
||||
->andReturn(new Collection);
|
||||
|
||||
// transaction would be verified, nothing more.
|
||||
$this->artisan('firefly-iii:journal-currencies')
|
||||
->expectsOutput('All transactions are correct.')
|
||||
->assertExitCode(0);
|
||||
// nothing changed, so no verification.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a single transfer which has no issues.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\JournalCurrencies
|
||||
*/
|
||||
public function testHandleTransfer(): void
|
||||
{
|
||||
// mock classes
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$euro = TransactionCurrency::find(1);
|
||||
$transfer = $this->getRandomTransfer();
|
||||
|
||||
// update transfer if necessary for the test:
|
||||
$collection = new Collection([$transfer]);
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_journal_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_journal_currencies', true]);
|
||||
|
||||
// mock stuff
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$currencyRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
|
||||
// return single tranfer
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::TRANSFER]])
|
||||
->andReturn($collection);
|
||||
|
||||
// for the "other journals" check, return nothing.
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]])
|
||||
->andReturn(new Collection);
|
||||
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->withArgs([Mockery::any(), 'currency_id'])->andReturn($euro->id);
|
||||
$currencyRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($euro);
|
||||
|
||||
// transaction would be verified, nothing more.
|
||||
$this->artisan('firefly-iii:journal-currencies')
|
||||
->expectsOutput('Verified 1 transaction(s) and journal(s).')
|
||||
->assertExitCode(0);
|
||||
// nothing changed, so no verification.
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a single transfer where the source account has no currency preference.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\JournalCurrencies
|
||||
*/
|
||||
public function testHandleTransferSourceNoPref(): void
|
||||
{
|
||||
// mock classes
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$euro = TransactionCurrency::find(1);
|
||||
$transfer = $this->getRandomTransfer();
|
||||
|
||||
// edit source to remove currency preference:
|
||||
/** @var Account $source */
|
||||
$source = $transfer->transactions()->where('amount', '<', 0)->first()->account;
|
||||
// AccountMeta::where('account_id', $source->id)->where('name', 'currency_id')->delete();
|
||||
|
||||
// update transfer if necessary for the test:
|
||||
$collection = new Collection([$transfer]);
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_journal_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_journal_currencies', true]);
|
||||
|
||||
// mock stuff
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$currencyRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
|
||||
// return single transfer
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::TRANSFER]])
|
||||
->andReturn($collection);
|
||||
|
||||
// for the "other journals" check, return nothing.
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]])
|
||||
->andReturn(new Collection);
|
||||
|
||||
// return NULL for first currency ID and currency.
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->withArgs([Mockery::any(), 'currency_id'])->andReturnNull();
|
||||
$currencyRepos->shouldReceive('findNull')->atLeast()->once()->andReturnNull();
|
||||
|
||||
// transaction would be verified, nothing more.
|
||||
$this->artisan('firefly-iii:journal-currencies')
|
||||
->expectsOutput(sprintf('Account #%d ("%s") must have currency preference but has none.', $source->id, $source->name))
|
||||
->assertExitCode(0);
|
||||
// nothing changed, so no verification.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a single transfer where the source transaction has no currency set.
|
||||
* Because this is not done over repositories, we must edit the DB.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\JournalCurrencies
|
||||
*/
|
||||
public function testHandleTransferSourceNoCurrency(): void
|
||||
{
|
||||
// mock classes
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$euro = TransactionCurrency::find(1);
|
||||
$transfer = $this->getRandomTransfer();
|
||||
/** @var Transaction $source */
|
||||
$source = $transfer->transactions()->where('amount', '<', 0)->first();
|
||||
$source->transaction_currency_id = null;
|
||||
$source->save();
|
||||
|
||||
// update transfer if necessary for the test:
|
||||
$collection = new Collection([$transfer]);
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_journal_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_journal_currencies', true]);
|
||||
|
||||
// mock stuff
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$currencyRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
|
||||
// return single tranfer
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::TRANSFER]])
|
||||
->andReturn($collection);
|
||||
|
||||
// for the "other journals" check, return nothing.
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]])
|
||||
->andReturn(new Collection);
|
||||
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->withArgs([Mockery::any(), 'currency_id'])->andReturn($euro->id);
|
||||
$currencyRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($euro);
|
||||
|
||||
// transaction would be verified, nothing more.
|
||||
$this->artisan('firefly-iii:journal-currencies')
|
||||
->expectsOutput(sprintf('Transaction #%d has no currency setting, now set to %s.', $source->id, $euro->code))
|
||||
->expectsOutput('Verified 2 transaction(s) and journal(s).')
|
||||
->assertExitCode(0);
|
||||
|
||||
// check transaction
|
||||
$this->assertCount(1, Transaction::where('id', $source->id)->where('transaction_currency_id', $euro->id)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a single transfer where the source transaction has a different currency than the source account does.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\JournalCurrencies
|
||||
*/
|
||||
public function testHandleMismatchedTransfer(): void
|
||||
{
|
||||
// mock classes
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$euro = TransactionCurrency::find(1);
|
||||
$usd = TransactionCurrency::where('code', 'USD')->first();
|
||||
$transfer = $this->getRandomTransfer();
|
||||
|
||||
/** @var Transaction $source */
|
||||
$source = $transfer->transactions()->where('amount', '<', 0)->first();
|
||||
$source->transaction_currency_id = $usd->id;
|
||||
$source->save();
|
||||
|
||||
// update transfer if necessary for the test:
|
||||
$collection = new Collection([$transfer]);
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_journal_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_journal_currencies', true]);
|
||||
|
||||
// mock stuff
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$currencyRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
|
||||
// return single tranfer
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::TRANSFER]])
|
||||
->andReturn($collection);
|
||||
|
||||
// for the "other journals" check, return nothing.
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]])
|
||||
->andReturn(new Collection);
|
||||
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->withArgs([Mockery::any(), 'currency_id'])->andReturn($euro->id);
|
||||
$currencyRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($euro);
|
||||
|
||||
// transaction would be verified, nothing more.
|
||||
$this->artisan('firefly-iii:journal-currencies')
|
||||
->expectsOutput(
|
||||
sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$source->id,
|
||||
$source->transaction_currency_id,
|
||||
$euro->id,
|
||||
$source->amount
|
||||
)
|
||||
)
|
||||
->expectsOutput('Verified 2 transaction(s) and journal(s).')
|
||||
->assertExitCode(0);
|
||||
// nothing changed, so no verification.
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a single transfer where the destination account has no currency preference.
|
||||
*
|
||||
* @covers \FireflyIII\Console\Commands\Upgrade\JournalCurrencies
|
||||
*/
|
||||
public function testHandleTransferNoDestinationCurrency(): void
|
||||
{
|
||||
// mock classes
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class);
|
||||
$euro = TransactionCurrency::find(1);
|
||||
$transfer = $this->getRandomTransfer();
|
||||
|
||||
/** @var Account $destination */
|
||||
$destination = $transfer->transactions()->where('amount', '>', 0)->first()->account;
|
||||
|
||||
// update transfer if necessary for the test:
|
||||
$collection = new Collection([$transfer]);
|
||||
$false = new Configuration;
|
||||
$false->data = false;
|
||||
FireflyConfig::shouldReceive('get')->withArgs(['4780_journal_currencies', false])->andReturn($false);
|
||||
FireflyConfig::shouldReceive('set')->withArgs(['4780_journal_currencies', true]);
|
||||
|
||||
// mock stuff
|
||||
$accountRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$journalRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
$currencyRepos->shouldReceive('setUser')->atLeast()->once();
|
||||
|
||||
// return single tranfer
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::TRANSFER]])
|
||||
->andReturn($collection);
|
||||
|
||||
// for the "other journals" check, return nothing.
|
||||
$journalRepos->shouldReceive('getAllJournals')->atLeast()->once()
|
||||
->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]])
|
||||
->andReturn(new Collection);
|
||||
|
||||
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->withArgs([Mockery::any(), 'currency_id'])->andReturn($euro->id, 0);
|
||||
$currencyRepos->shouldReceive('findNull')->atLeast()->once()->andReturn($euro, null);
|
||||
|
||||
// transaction would be verified, nothing more.
|
||||
$this->artisan('firefly-iii:journal-currencies')
|
||||
->expectsOutput(sprintf('Account #%d ("%s") must have currency preference but has none.', $destination->id, $destination->name))
|
||||
->assertExitCode(0);
|
||||
// nothing changed, so no verification.
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user