Refactor upgrade and verify commands.

This commit is contained in:
James Cole
2019-03-23 18:58:06 +01:00
parent 1b0be2a47e
commit ce30375341
31 changed files with 909 additions and 513 deletions

View File

@@ -51,6 +51,7 @@ class CreateAccessTokens extends Command
*/
public function handle(): int
{
$start = microtime(true);
$count = 0;
$users = User::get();
/** @var User $user */
@@ -66,6 +67,8 @@ class CreateAccessTokens extends Command
if (0 === $count) {
$this->info('All access tokens OK!');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verify access tokens in %s seconds.', $end));
return 0;
}

View File

@@ -49,7 +49,7 @@ class CreateLinkTypes extends Command
*/
public function handle(): int
{
//
$start = microtime(true);
$count = 0;
$set = [
'Related' => ['relates to', 'relates to'],
@@ -73,6 +73,8 @@ class CreateLinkTypes extends Command
if (0 === $count) {
$this->info('All link types OK!');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified link types in %s seconds', $end));
return 0;
}

View File

@@ -52,16 +52,18 @@ class DeleteEmptyGroups extends Command
*/
public function handle(): int
{
//
$start = microtime(true);
$groups = array_unique(TransactionJournal::get(['transaction_group_id'])->pluck('transaction_group_id')->toArray());
$count = TransactionGroup::whereNull('deleted_at')->whereNotIn('id', $groups)->count();
if (0 === $count) {
$this->info('No empty groups.');
$this->info('No empty transaction groups.');
}
if ($count > 0) {
$this->info(sprintf('Deleted %d empty groups.', $count));
$this->info(sprintf('Deleted %d empty transaction groups.', $count));
TransactionGroup::whereNull('deleted_at')->whereNotIn('id', $groups)->delete();
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified empty groups in %s seconds', $end));
return 0;
}

View File

@@ -60,7 +60,7 @@ class DeleteEmptyJournals extends Command
private function deleteEmptyJournals(): void
{
$start = microtime(true);
$count = 0;
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id')
@@ -69,12 +69,14 @@ class DeleteEmptyJournals extends Command
foreach ($set as $entry) {
TransactionJournal::find($entry->id)->delete();
$this->info(sprintf('Deleted empty transaction #%d', $entry->id));
$this->info(sprintf('Deleted empty transaction journal #%d', $entry->id));
++$count;
}
if (0 === $count) {
$this->info('No empty transactions.');
$this->info('No empty transaction journals.');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified empty journals in %s seconds', $end));
}
/**
@@ -103,12 +105,12 @@ class DeleteEmptyJournals extends Command
// uneven number, delete journal and transactions:
TransactionJournal::find((int)$row->transaction_journal_id)->delete();
Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete();
$this->info(sprintf('Deleted transaction #%d because it had an uneven number of transactions.', $row->transaction_journal_id));
$this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id));
$total++;
}
}
if (0 === $total) {
$this->info('No uneven transactions.');
$this->info('No uneven transaction journals.');
}
}

View File

@@ -53,9 +53,11 @@ class DeleteOrphanedTransactions extends Command
*/
public function handle(): int
{
$start = microtime(true);
$this->deleteOrphanedTransactions();
$this->deleteFromOrphanedAccounts();
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified orphans in %s seconds', $end));
return 0;
}
@@ -80,7 +82,7 @@ class DeleteOrphanedTransactions extends Command
}
Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete();
$this->line(
sprintf('Deleted transaction #%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++;
}
@@ -112,7 +114,7 @@ class DeleteOrphanedTransactions extends Command
$transaction->delete();
$this->info(
sprintf(
'Transaction #%d (part of deleted journal #%d) has been deleted as well.',
'Transaction #%d (part of deleted transaction journal #%d) has been deleted as well.',
$entry->transaction_id,
$entry->journal_id
)
@@ -122,5 +124,6 @@ class DeleteOrphanedTransactions extends Command
if (0 === $count) {
$this->info('No orphaned transactions.');
}
}
}

View File

@@ -52,20 +52,23 @@ class DeleteZeroAmount extends Command
*/
public function handle(): int
{
$start = microtime(true);
$set = Transaction::where('amount', 0)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
$set = array_unique($set);
/** @var Collection $journals */
$journals = TransactionJournal::whereIn('id', $set)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$this->info(sprintf('Deleted transaction #%d because the amount is zero (0.00).', $journal->id));
$this->info(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id));
$journal->delete();
Transaction::where('transaction_journal_id', $journal->id)->delete();
}
if (0 === $journals->count()) {
$this->info('No zero-amount transactions.');
$this->info('No zero-amount transaction journals.');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified zero-amount integrity in %s seconds', $end));
return 0;
}
}

View File

@@ -54,6 +54,7 @@ class EnableCurrencies extends Command
*/
public function handle(): int
{
$start = microtime(true);
$found = [];
// get all meta entries
/** @var Collection $meta */
@@ -91,6 +92,9 @@ class EnableCurrencies extends Command
}
TransactionCurrency::whereIn('id', $found)->update(['enabled' => true]);
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified currencies in %s seconds.', $end));
return 0;
}
}

View File

@@ -21,8 +21,11 @@
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Account;
use FireflyIII\Factory\AccountFactory;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command;
/**
@@ -44,70 +47,180 @@ class FixAccountTypes extends Command
protected $signature = 'firefly-iii:fix-account-types';
/** @var array */
private $expected;
/** @var AccountFactory */
private $factory;
/** @var array */
private $fixable;
/**
* @param TransactionJournal $journal
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void
{
// 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;
}
}
/**
* Execute the console command.
*
* @return int
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function handle(): int
{
$start = microtime(true);
$this->factory = app(AccountFactory::class);
// some combinations can be fixed by this script:
$this->fixable = [
// transfers from asset to liability and vice versa
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::LOAN),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::DEBT),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::MORTGAGE),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::LOAN, AccountType::ASSET),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::DEBT, AccountType::ASSET),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::MORTGAGE, AccountType::ASSET),
// withdrawals with a revenue account as destination instead of an expense account.
sprintf('%s%s%s', TransactionType::WITHDRAWAL, AccountType::ASSET, AccountType::REVENUE),
// deposits with an expense account as source instead of a revenue account.
sprintf('%s%s%s', TransactionType::DEPOSIT, AccountType::EXPENSE, AccountType::ASSET),
];
$this->expected = config('firefly.source_dests');
$journals = TransactionJournal::get();
$journals = TransactionJournal::with(['TransactionType', 'transactions', 'transactions.account', 'transactions.account.accounttype'])->get();
foreach ($journals as $journal) {
$this->inspectJournal($journal);
}
return 0;
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified account types in %s seconds', $end));
private function getDestinationAccount(TransactionJournal $journal): Account
{
return $journal->transactions()->where('amount', '>', 0)->first()->account;
return 0;
}
/**
* @param TransactionJournal $journal
*
* @return Account
* @return Transaction
*/
private function getSourceAccount(TransactionJournal $journal): Account
private function getDestinationTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions()->where('amount', '<', 0)->first()->account;
return $journal->transactions->firstWhere('amount', '>', 0);
}
/**
* @param TransactionJournal $journal
*
* @return Transaction
*/
private function getSourceTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions->firstWhere('amount', '<', 0);
}
/**
* @param TransactionJournal $journal
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function inspectJournal(TransactionJournal $journal): void
{
$count = $journal->transactions()->count();
if (2 !== $count) {
$this->info(sprintf('Cannot inspect journal #%d because it does not have 2 transactions, but %d', $journal->id, $count));
$this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transactions instead of 2.', $journal->id, $count));
return;
}
$type = $journal->transactionType->type;
$sourceAccount = $this->getSourceAccount($journal);
$sourceAccountType = $sourceAccount->accountType->type;
$destinationAccount = $this->getDestinationAccount($journal);
$destinationAccountType = $destinationAccount->accountType->type;
$type = $journal->transactionType->type;
$sourceTransaction = $this->getSourceTransaction($journal);
$sourceAccount = $sourceTransaction->account;
$sourceAccountType = $sourceAccount->accountType->type;
$destTransaction = $this->getDestinationTransaction($journal);
$destAccount = $destTransaction->account;
$destAccountType = $destAccount->accountType->type;
if (!isset($this->expected[$type])) {
$this->info(sprintf('No source/destination info for transaction type %s.', $type));
return;
}
if (!isset($this->expected[$type][$sourceAccountType])) {
$this->info(sprintf('The source of %s #%d cannot be of type "%s".', $type, $journal->id, $sourceAccountType));
$this->info(sprintf('The destination of %s #%d probably cannot be of type "%s".', $type, $journal->id, $destinationAccountType));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
// TODO think of a way to fix the problem.
return;
}
$expectedTypes = $this->expected[$type][$sourceAccountType];
if (!\in_array($destinationAccountType, $expectedTypes, true)) {
$this->info(sprintf('The destination of %s #%d cannot be of type "%s".', $type, $journal->id, $destinationAccountType));
// TODO think of a way to fix the problem.
if (!\in_array($destAccountType, $expectedTypes, true)) {
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
}
}
}

View File

@@ -53,7 +53,8 @@ class FixPiggies extends Command
*/
public function handle(): int
{
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
$start = microtime(true);
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
$set->each(
function (PiggyBankEvent $event) {
if (null === $event->transaction_journal_id) {
@@ -75,7 +76,8 @@ class FixPiggies extends Command
return true;
}
);
$this->line(sprintf('Verified the content of %d piggy bank events.', $set->count()));
$end = round(microtime(true) - $start, 2);
$this->line(sprintf('Verified the content of %d piggy bank events in %s seconds.', $set->count(), $end));
return 0;
}

View File

@@ -52,7 +52,7 @@ class FixUnevenAmount extends Command
*/
public function handle(): int
{
$start = microtime(true);
$count = 0;
// get invalid journals
$journals = DB::table('transactions')
@@ -70,6 +70,9 @@ class FixUnevenAmount extends Command
$this->info('Amount integrity OK!');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified amount integrity in %s seconds', $end));
return 0;
}
@@ -93,7 +96,7 @@ class FixUnevenAmount extends Command
$destination->amount = $amount;
$destination->save();
$this->line(sprintf('Corrected amount in transaction #%d', $param));
$this->line(sprintf('Corrected amount in transaction journal #%d', $param));
}
}

View File

@@ -50,6 +50,7 @@ class RemoveBills extends Command
*/
public function handle(): int
{
$start = microtime(true);
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
$journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get();
@@ -60,11 +61,13 @@ class RemoveBills extends Command
$journal->save();
}
if (0 === $journals->count()) {
$this->info('All transactions have correct bill information.');
$this->info('All transaction journals have correct bill information.');
}
if ($journals->count() > 0) {
$this->info('Fixed all transactions so they have correct bill information.');
$this->info('Fixed all transaction journals so they have correct bill information.');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified bills / journals in %s seconds', $end));
return 0;
}

View File

@@ -50,6 +50,7 @@ class TransferBudgets extends Command
*/
public function handle(): int
{
$start = microtime(true);
$set = TransactionJournal::distinct()
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
@@ -58,13 +59,15 @@ class TransferBudgets extends Command
$count = 0;
/** @var TransactionJournal $entry */
foreach ($set as $entry) {
$this->info(sprintf('Transaction #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type));
$this->info(sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type));
$entry->budgets()->sync([]);
$count++;
}
if (0 === $count) {
$this->info('No invalid budget/journal entries.');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified budget/journals in %s seconds.', $end));
return 0;
}