mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-22 12:11:19 +00:00
Refactor upgrade and verify commands.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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.');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user