diff --git a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php index 29cdbdf516..4e44b49307 100644 --- a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php +++ b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php @@ -89,6 +89,7 @@ class CorrectOpeningBalanceCurrencies extends Command } Log::debug(sprintf('Done with %s', __METHOD__)); + return 0; } @@ -133,6 +134,7 @@ class CorrectOpeningBalanceCurrencies extends Command $account = $transaction->account()->first(); if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) { Log::debug(sprintf('Account of transaction #%d is opposite of IB account (%s).', $transaction->id, $account->accountType()->first()->type)); + return $account; } } @@ -143,6 +145,7 @@ class CorrectOpeningBalanceCurrencies extends Command /** * @param Account $account + * * @return TransactionCurrency * @throws JsonException */ @@ -156,15 +159,16 @@ class CorrectOpeningBalanceCurrencies extends Command } /** - * @param TransactionJournal $journal + * @param TransactionJournal $journal * @param TransactionCurrency $currency + * * @return int */ private function setCurrency(TransactionJournal $journal, TransactionCurrency $currency): int { Log::debug('Now in setCurrency'); $count = 0; - if ((int) $journal->transaction_currency_id !== (int) $currency->id) { + if ((int)$journal->transaction_currency_id !== (int)$currency->id) { Log::debug(sprintf('Currency ID of journal #%d was #%d, now set to #%d', $journal->id, $journal->transaction_currency_id, $currency->id)); $journal->transaction_currency_id = $currency->id; $journal->save(); @@ -173,8 +177,10 @@ class CorrectOpeningBalanceCurrencies extends Command /** @var Transaction $transaction */ foreach ($journal->transactions as $transaction) { - if ((int) $transaction->transaction_currency_id !== (int) $currency->id) { - Log::debug(sprintf('Currency ID of transaction #%d was #%d, now set to #%d', $transaction->id, $transaction->transaction_currency_id, $currency->id)); + if ((int)$transaction->transaction_currency_id !== (int)$currency->id) { + Log::debug( + sprintf('Currency ID of transaction #%d was #%d, now set to #%d', $transaction->id, $transaction->transaction_currency_id, $currency->id) + ); $transaction->transaction_currency_id = $currency->id; $transaction->save(); $count = 1; diff --git a/app/Console/Commands/Correction/CreateAccessTokens.php b/app/Console/Commands/Correction/CreateAccessTokens.php index 7314645cf8..f98c76274e 100644 --- a/app/Console/Commands/Correction/CreateAccessTokens.php +++ b/app/Console/Commands/Correction/CreateAccessTokens.php @@ -50,8 +50,8 @@ class CreateAccessTokens extends Command /** * Execute the console command. * - * @throws Exception * @return int + * @throws Exception */ public function handle(): int { diff --git a/app/Console/Commands/Correction/DeleteEmptyGroups.php b/app/Console/Commands/Correction/DeleteEmptyGroups.php index f8c4d49c9f..7daec17572 100644 --- a/app/Console/Commands/Correction/DeleteEmptyGroups.php +++ b/app/Console/Commands/Correction/DeleteEmptyGroups.php @@ -50,9 +50,9 @@ class DeleteEmptyGroups extends Command /** * Execute the console command. * + * @return int * @throws Exception; * - * @return int */ public function handle(): int { diff --git a/app/Console/Commands/Correction/DeleteEmptyJournals.php b/app/Console/Commands/Correction/DeleteEmptyJournals.php index 5cdef1cc89..a61250f692 100644 --- a/app/Console/Commands/Correction/DeleteEmptyJournals.php +++ b/app/Console/Commands/Correction/DeleteEmptyJournals.php @@ -62,6 +62,38 @@ class DeleteEmptyJournals extends Command return 0; } + /** + * Delete transactions and their journals if they have an uneven number of transactions. + */ + private function deleteUnevenJournals(): void + { + $set = Transaction + ::whereNull('deleted_at') + ->groupBy('transactions.transaction_journal_id') + ->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); + $total = 0; + foreach ($set as $row) { + $count = (int)$row->the_count; + if (1 === $count % 2) { + // 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++; + } + } + if (0 === $total) { + $this->info('No uneven transaction journals.'); + } + } + private function deleteEmptyJournals(): void { $start = microtime(true); @@ -90,36 +122,4 @@ class DeleteEmptyJournals extends Command $this->info(sprintf('Verified empty journals in %s seconds', $end)); } - /** - * Delete transactions and their journals if they have an uneven number of transactions. - */ - private function deleteUnevenJournals(): void - { - $set = Transaction - ::whereNull('deleted_at') - ->groupBy('transactions.transaction_journal_id') - ->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); - $total = 0; - foreach ($set as $row) { - $count = (int) $row->the_count; - if (1 === $count % 2) { - // 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++; - } - } - if (0 === $total) { - $this->info('No uneven transaction journals.'); - } - } - } diff --git a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php index 8752694e12..d0414029c1 100644 --- a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php +++ b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php @@ -52,8 +52,8 @@ class DeleteOrphanedTransactions extends Command /** * Execute the console command. * - * @throws Exception * @return int + * @throws Exception */ public function handle(): int { @@ -66,45 +66,6 @@ class DeleteOrphanedTransactions extends Command return 0; } - /** - * - */ - private function deleteFromOrphanedAccounts(): void - { - $set - = Transaction - ::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id') - ->whereNotNull('accounts.deleted_at') - ->get(['transactions.*']); - $count = 0; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - // delete journals - $journal = TransactionJournal::find((int) $transaction->transaction_journal_id); - if ($journal) { - try { - $journal->delete(); - // @codeCoverageIgnoreStart - } catch (Exception $e) { - 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 - ) - ); - $count++; - } - if (0 === $count) { - $this->info('No orphaned accounts.'); - } - } - /** * @throws Exception */ @@ -124,7 +85,7 @@ class DeleteOrphanedTransactions extends Command ); /** @var stdClass $entry */ foreach ($set as $entry) { - $transaction = Transaction::find((int) $entry->transaction_id); + $transaction = Transaction::find((int)$entry->transaction_id); $transaction->delete(); $this->info( sprintf( @@ -139,4 +100,43 @@ class DeleteOrphanedTransactions extends Command $this->info('No orphaned transactions.'); } } + + /** + * + */ + private function deleteFromOrphanedAccounts(): void + { + $set + = Transaction + ::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id') + ->whereNotNull('accounts.deleted_at') + ->get(['transactions.*']); + $count = 0; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + // delete journals + $journal = TransactionJournal::find((int)$transaction->transaction_journal_id); + if ($journal) { + try { + $journal->delete(); + // @codeCoverageIgnoreStart + } catch (Exception $e) { + 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 + ) + ); + $count++; + } + if (0 === $count) { + $this->info('No orphaned accounts.'); + } + } } diff --git a/app/Console/Commands/Correction/EnableCurrencies.php b/app/Console/Commands/Correction/EnableCurrencies.php index 9ac763d49d..3901b7036b 100644 --- a/app/Console/Commands/Correction/EnableCurrencies.php +++ b/app/Console/Commands/Correction/EnableCurrencies.php @@ -64,35 +64,39 @@ class EnableCurrencies extends Command /** @var Collection $meta */ $meta = AccountMeta::where('name', 'currency_id')->groupBy('data')->get(['data']); foreach ($meta as $entry) { - $found[] = (int) $entry->data; + $found[] = (int)$entry->data; } // get all from journals: /** @var Collection $journals */ $journals = TransactionJournal::groupBy('transaction_currency_id')->get(['transaction_currency_id']); foreach ($journals as $entry) { - $found[] = (int) $entry->transaction_currency_id; + $found[] = (int)$entry->transaction_currency_id; } // get all from transactions /** @var Collection $transactions */ - $transactions = Transaction::groupBy('transaction_currency_id', 'foreign_currency_id')->get(['transaction_currency_id','foreign_currency_id']); + $transactions = Transaction::groupBy('transaction_currency_id', 'foreign_currency_id')->get(['transaction_currency_id', 'foreign_currency_id']); foreach ($transactions as $entry) { - $found[] = (int) $entry->transaction_currency_id; - $found[] = (int) $entry->foreign_currency_id; + $found[] = (int)$entry->transaction_currency_id; + $found[] = (int)$entry->foreign_currency_id; } // get all from budget limits /** @var Collection $limits */ $limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']); foreach ($limits as $entry) { - $found[] = (int) $entry->transaction_currency_id; + $found[] = (int)$entry->transaction_currency_id; } $found = array_values(array_unique($found)); - $found = array_values(array_filter($found, function (int $currencyId) { - return $currencyId !== 0; - })); + $found = array_values( + array_filter( + $found, function (int $currencyId) { + return $currencyId !== 0; + } + ) + ); $message = sprintf('%d different currencies are currently in use.', count($found)); $this->info($message); Log::debug($message, $found); diff --git a/app/Console/Commands/Correction/FixAccountTypes.php b/app/Console/Commands/Correction/FixAccountTypes.php index f4638c9f4e..80d5ab9bcb 100644 --- a/app/Console/Commands/Correction/FixAccountTypes.php +++ b/app/Console/Commands/Correction/FixAccountTypes.php @@ -39,11 +39,13 @@ class FixAccountTypes extends Command { /** * The console command description. + * * @var string */ protected $description = 'Make sure all journals have the correct from/to account types.'; /** * The name and signature of the console command. + * * @var string */ protected $signature = 'firefly-iii:fix-account-types'; @@ -54,6 +56,7 @@ class FixAccountTypes extends Command /** * Execute the console command. + * * @return int * @throws FireflyException */ @@ -84,11 +87,87 @@ class FixAccountTypes extends Command return 0; } + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->count = 0; + } + + /** + * @param TransactionJournal $journal + * + * @throws FireflyException + */ + private function inspectJournal(TransactionJournal $journal): void + { + $transactions = $journal->transactions()->count(); + if (2 !== $transactions) { + Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions)); + $this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions)); + + return; + } + $type = $journal->transactionType->type; + $sourceTransaction = $this->getSourceTransaction($journal); + $destTransaction = $this->getDestinationTransaction($journal); + $sourceAccount = $sourceTransaction->account; + $sourceAccountType = $sourceAccount->accountType->type; + $destAccount = $destTransaction->account; + $destAccountType = $destAccount->accountType->type; + + if (!array_key_exists($type, $this->expected)) { + // @codeCoverageIgnoreStart + Log::info(sprintf('No source/destination info for transaction type %s.', $type)); + $this->info(sprintf('No source/destination info for transaction type %s.', $type)); + + return; + // @codeCoverageIgnoreEnd + } + if (!array_key_exists($sourceAccountType, $this->expected[$type])) { + Log::debug(sprintf('Going to fix journal #%d', $journal->id)); + $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); + + return; + } + $expectedTypes = $this->expected[$type][$sourceAccountType]; + if (!in_array($destAccountType, $expectedTypes, true)) { + Log::debug(sprintf('Going to fix journal #%d', $journal->id)); + $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); + } + } + + /** + * @param TransactionJournal $journal + * + * @return Transaction + */ + private function getSourceTransaction(TransactionJournal $journal): Transaction + { + return $journal->transactions->firstWhere('amount', '<', 0); + } + + /** + * @param TransactionJournal $journal + * + * @return Transaction + */ + private function getDestinationTransaction(TransactionJournal $journal): Transaction + { + return $journal->transactions->firstWhere('amount', '>', 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 @@ -132,7 +211,10 @@ class FixAccountTypes extends Command $result = $this->factory->findOrCreate($dest->account->name, AccountType::EXPENSE); $dest->account()->associate($result); $dest->save(); - $message = sprintf('Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldDest->id, $oldDest->name, $result->id, $result->name); + $message = sprintf( + 'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldDest->id, $oldDest->name, + $result->id, $result->name + ); $this->info($message); Log::debug($message); $this->inspectJournal($journal); @@ -145,7 +227,10 @@ class FixAccountTypes extends Command $oldSource = $dest->account; $source->account()->associate($result); $source->save(); - $message = sprintf('Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldSource->id, $oldSource->name, $result->id, $result->name); + $message = sprintf( + 'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldSource->id, $oldSource->name, + $result->id, $result->name + ); $this->info($message); Log::debug($message); $this->inspectJournal($journal); @@ -163,75 +248,4 @@ class FixAccountTypes extends Command } } - - /** - * @param TransactionJournal $journal - * @return Transaction - */ - private function getDestinationTransaction(TransactionJournal $journal): Transaction - { - 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 FireflyException - */ - private function inspectJournal(TransactionJournal $journal): void - { - $transactions = $journal->transactions()->count(); - if (2 !== $transactions) { - Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions)); - $this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions)); - - return; - } - $type = $journal->transactionType->type; - $sourceTransaction = $this->getSourceTransaction($journal); - $destTransaction = $this->getDestinationTransaction($journal); - $sourceAccount = $sourceTransaction->account; - $sourceAccountType = $sourceAccount->accountType->type; - $destAccount = $destTransaction->account; - $destAccountType = $destAccount->accountType->type; - - if (!array_key_exists($type, $this->expected)) { - // @codeCoverageIgnoreStart - Log::info(sprintf('No source/destination info for transaction type %s.', $type)); - $this->info(sprintf('No source/destination info for transaction type %s.', $type)); - - return; - // @codeCoverageIgnoreEnd - } - if (!array_key_exists($sourceAccountType, $this->expected[$type])) { - Log::debug(sprintf('Going to fix journal #%d', $journal->id)); - $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); - - return; - } - $expectedTypes = $this->expected[$type][$sourceAccountType]; - if (!in_array($destAccountType, $expectedTypes, true)) { - Log::debug(sprintf('Going to fix journal #%d', $journal->id)); - $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); - } - } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - } } diff --git a/app/Console/Commands/Correction/FixGroupAccounts.php b/app/Console/Commands/Correction/FixGroupAccounts.php index 43239e09e6..37bb4986dc 100644 --- a/app/Console/Commands/Correction/FixGroupAccounts.php +++ b/app/Console/Commands/Correction/FixGroupAccounts.php @@ -62,12 +62,12 @@ class FixGroupAccounts extends Command ::groupBy('transaction_group_id') ->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]); foreach ($res as $journal) { - if ((int) $journal->the_count > 1) { - $groups[] = (int) $journal->transaction_group_id; + if ((int)$journal->the_count > 1) { + $groups[] = (int)$journal->transaction_group_id; } } $handler = new UpdatedGroupEventHandler; - foreach($groups as $groupId) { + foreach ($groups as $groupId) { $group = TransactionGroup::find($groupId); $event = new UpdatedTransactionGroup($group); $handler->unifyAccounts($event); diff --git a/app/Console/Commands/Correction/FixLongDescriptions.php b/app/Console/Commands/Correction/FixLongDescriptions.php index bcc5288ff1..12ade346d2 100644 --- a/app/Console/Commands/Correction/FixLongDescriptions.php +++ b/app/Console/Commands/Correction/FixLongDescriptions.php @@ -69,7 +69,7 @@ class FixLongDescriptions extends Command $groups = TransactionGroup::get(['id', 'title']); /** @var TransactionGroup $group */ foreach ($groups as $group) { - if (strlen((string) $group->title) > self::MAX_LENGTH) { + if (strlen((string)$group->title) > self::MAX_LENGTH) { $group->title = substr($group->title, 0, self::MAX_LENGTH); $group->save(); $this->line(sprintf('Truncated description of transaction group #%d', $group->id)); diff --git a/app/Console/Commands/Correction/FixRecurringTransactions.php b/app/Console/Commands/Correction/FixRecurringTransactions.php index ce51e3a10e..c34bb68a31 100644 --- a/app/Console/Commands/Correction/FixRecurringTransactions.php +++ b/app/Console/Commands/Correction/FixRecurringTransactions.php @@ -73,18 +73,6 @@ class FixRecurringTransactions extends Command return 0; } - /** - * - */ - private function correctTransactions(): void - { - $users = $this->userRepos->all(); - /** @var User $user */ - foreach ($users as $user) { - $this->processUser($user); - } - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -98,6 +86,18 @@ class FixRecurringTransactions extends Command $this->userRepos = app(UserRepositoryInterface::class); } + /** + * + */ + private function correctTransactions(): void + { + $users = $this->userRepos->all(); + /** @var User $user */ + foreach ($users as $user) { + $this->processUser($user); + } + } + /** * @param User $user */ diff --git a/app/Console/Commands/Correction/FixTransactionTypes.php b/app/Console/Commands/Correction/FixTransactionTypes.php index d3045de798..3c51059e1f 100644 --- a/app/Console/Commands/Correction/FixTransactionTypes.php +++ b/app/Console/Commands/Correction/FixTransactionTypes.php @@ -79,19 +79,6 @@ class FixTransactionTypes extends Command return 0; } - /** - * @param TransactionJournal $journal - * @param string $expectedType - */ - private function changeJournal(TransactionJournal $journal, string $expectedType): void - { - $type = TransactionType::whereType($expectedType)->first(); - if (null !== $type) { - $journal->transaction_type_id = $type->id; - $journal->save(); - } - } - /** * Collect all transaction journals. * @@ -120,7 +107,7 @@ class FixTransactionTypes extends Command return false; } - $expectedType = (string) config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type)); + $expectedType = (string)config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type)); if ($expectedType !== $type) { $this->line(sprintf('Transaction journal #%d was of type "%s" but is corrected to "%s"', $journal->id, $type, $expectedType)); $this->changeJournal($journal, $expectedType); @@ -134,8 +121,37 @@ class FixTransactionTypes extends Command /** * @param TransactionJournal $journal * - * @throws FireflyException * @return Account + * @throws FireflyException + */ + private function getSourceAccount(TransactionJournal $journal): Account + { + $collection = $journal->transactions->filter( + static function (Transaction $transaction) { + return $transaction->amount < 0; + } + ); + if (0 === $collection->count()) { + throw new FireflyException(sprintf('Journal #%d has no source transaction.', $journal->id)); + } + if (1 !== $collection->count()) { + throw new FireflyException(sprintf('Journal #%d has multiple source transactions.', $journal->id)); + } + /** @var Transaction $transaction */ + $transaction = $collection->first(); + $account = $transaction->account; + if (null === $account) { + throw new FireflyException(sprintf('Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id)); + } + + return $account; + } + + /** + * @param TransactionJournal $journal + * + * @return Account + * @throws FireflyException */ private function getDestinationAccount(TransactionJournal $journal): Account { @@ -162,30 +178,14 @@ class FixTransactionTypes extends Command /** * @param TransactionJournal $journal - * - * @throws FireflyException - * @return Account + * @param string $expectedType */ - private function getSourceAccount(TransactionJournal $journal): Account + private function changeJournal(TransactionJournal $journal, string $expectedType): void { - $collection = $journal->transactions->filter( - static function (Transaction $transaction) { - return $transaction->amount < 0; - } - ); - if (0 === $collection->count()) { - throw new FireflyException(sprintf('Journal #%d has no source transaction.', $journal->id)); + $type = TransactionType::whereType($expectedType)->first(); + if (null !== $type) { + $journal->transaction_type_id = $type->id; + $journal->save(); } - if (1 !== $collection->count()) { - throw new FireflyException(sprintf('Journal #%d has multiple source transactions.', $journal->id)); - } - /** @var Transaction $transaction */ - $transaction = $collection->first(); - $account = $transaction->account; - if (null === $account) { - throw new FireflyException(sprintf('Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id)); - } - - return $account; } } diff --git a/app/Console/Commands/Correction/RemoveBills.php b/app/Console/Commands/Correction/RemoveBills.php index fb85b507af..53f7a2d126 100644 --- a/app/Console/Commands/Correction/RemoveBills.php +++ b/app/Console/Commands/Correction/RemoveBills.php @@ -56,10 +56,10 @@ class RemoveBills extends Command $start = microtime(true); /** @var TransactionType $withdrawal */ $withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); - if(null === $withdrawal) { + if (null === $withdrawal) { return 0; } - $journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get(); + $journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get(); /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id)); diff --git a/app/Console/Commands/Export/ExportData.php b/app/Console/Commands/Export/ExportData.php index 3634c3c4ff..e06f2ff9b8 100644 --- a/app/Console/Commands/Export/ExportData.php +++ b/app/Console/Commands/Export/ExportData.php @@ -147,108 +147,16 @@ class ExportData extends Command } /** - * @param array $options - * @param array $data + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. * - * @throws FireflyException + * @codeCoverageIgnore */ - private function exportData(array $options, array $data): void + private function stupidLaravel(): void { - $date = date('Y_m_d'); - foreach ($data as $key => $content) { - $file = sprintf('%s%s_%s.csv', $options['directory'], $date, $key); - if (false === $options['force'] && file_exists($file)) { - throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file)); - } - if (true === $options['force'] && file_exists($file)) { - $this->warn(sprintf('File "%s" exists already but will be replaced.', $file)); - } - // continue to write to file. - file_put_contents($file, $content); - $this->info(sprintf('Wrote %s-export to file "%s".', $key, $file)); - } - } - - /** - * @return Collection - * @throws FireflyException - */ - private function getAccountsParameter(): Collection - { - $final = new Collection; - $accounts = new Collection; - $accountList = $this->option('accounts'); - $types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; - if (null !== $accountList && '' !== (string)$accountList) { - $accountIds = explode(',', $accountList); - $accounts = $this->accountRepository->getAccountsById($accountIds); - } - if (null === $accountList) { - $accounts = $this->accountRepository->getAccountsByType($types); - } - // filter accounts, - /** @var AccountType $account */ - foreach ($accounts as $account) { - if (in_array($account->accountType->type, $types, true)) { - $final->push($account); - } - } - if (0 === $final->count()) { - throw new FireflyException('Ended up with zero valid accounts to export from.'); - } - - return $final; - } - - /** - * @param string $field - * - * @return Carbon - * @throws Exception - */ - private function getDateParameter(string $field): Carbon - { - $date = Carbon::now()->subYear(); - $error = false; - if (null !== $this->option($field)) { - try { - $date = Carbon::createFromFormat('Y-m-d', $this->option($field)); - } catch (InvalidArgumentException $e) { - Log::error($e->getMessage()); - $this->error(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); - $error = true; - } - } - if (false === $error && 'start' === $field) { - $journal = $this->journalRepository->firstNull(); - $date = null === $journal ? Carbon::now()->subYear() : $journal->date; - $date->startOfDay(); - } - if (false === $error && 'end' === $field) { - $date = today(config('app.timezone')); - $date->endOfDay(); - } - - // fallback - return $date; - } - - /** - * @return string - * @throws FireflyException - * - */ - private function getExportDirectory(): string - { - $directory = (string)$this->option('export_directory'); - if (null === $directory) { - $directory = './'; - } - if (!is_writable($directory)) { - throw new FireflyException(sprintf('Directory "%s" isn\'t writeable.', $directory)); - } - - return $directory; + $this->journalRepository = app(JournalRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); } /** @@ -283,16 +191,108 @@ class ExportData extends Command } /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. + * @param string $field * - * @codeCoverageIgnore + * @return Carbon + * @throws Exception */ - private function stupidLaravel(): void + private function getDateParameter(string $field): Carbon { - $this->journalRepository = app(JournalRepositoryInterface::class); - $this->accountRepository = app(AccountRepositoryInterface::class); + $date = Carbon::now()->subYear(); + $error = false; + if (null !== $this->option($field)) { + try { + $date = Carbon::createFromFormat('Y-m-d', $this->option($field)); + } catch (InvalidArgumentException $e) { + Log::error($e->getMessage()); + $this->error(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); + $error = true; + } + } + if (false === $error && 'start' === $field) { + $journal = $this->journalRepository->firstNull(); + $date = null === $journal ? Carbon::now()->subYear() : $journal->date; + $date->startOfDay(); + } + if (false === $error && 'end' === $field) { + $date = today(config('app.timezone')); + $date->endOfDay(); + } + + // fallback + return $date; + } + + /** + * @return Collection + * @throws FireflyException + */ + private function getAccountsParameter(): Collection + { + $final = new Collection; + $accounts = new Collection; + $accountList = $this->option('accounts'); + $types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; + if (null !== $accountList && '' !== (string)$accountList) { + $accountIds = explode(',', $accountList); + $accounts = $this->accountRepository->getAccountsById($accountIds); + } + if (null === $accountList) { + $accounts = $this->accountRepository->getAccountsByType($types); + } + // filter accounts, + /** @var AccountType $account */ + foreach ($accounts as $account) { + if (in_array($account->accountType->type, $types, true)) { + $final->push($account); + } + } + if (0 === $final->count()) { + throw new FireflyException('Ended up with zero valid accounts to export from.'); + } + + return $final; + } + + /** + * @return string + * @throws FireflyException + * + */ + private function getExportDirectory(): string + { + $directory = (string)$this->option('export_directory'); + if (null === $directory) { + $directory = './'; + } + if (!is_writable($directory)) { + throw new FireflyException(sprintf('Directory "%s" isn\'t writeable.', $directory)); + } + + return $directory; + } + + /** + * @param array $options + * @param array $data + * + * @throws FireflyException + */ + private function exportData(array $options, array $data): void + { + $date = date('Y_m_d'); + foreach ($data as $key => $content) { + $file = sprintf('%s%s_%s.csv', $options['directory'], $date, $key); + if (false === $options['force'] && file_exists($file)) { + throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file)); + } + if (true === $options['force'] && file_exists($file)) { + $this->warn(sprintf('File "%s" exists already but will be replaced.', $file)); + } + // continue to write to file. + file_put_contents($file, $content); + $this->info(sprintf('Wrote %s-export to file "%s".', $key, $file)); + } } diff --git a/app/Console/Commands/Integrity/ReportEmptyObjects.php b/app/Console/Commands/Integrity/ReportEmptyObjects.php index 0e11400d30..fe8f33d061 100644 --- a/app/Console/Commands/Integrity/ReportEmptyObjects.php +++ b/app/Console/Commands/Integrity/ReportEmptyObjects.php @@ -68,51 +68,6 @@ class ReportEmptyObjects extends Command return 0; } - /** - * Reports on accounts with no transactions. - */ - private function reportAccounts(): void - { - $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') - ->leftJoin('users', 'accounts.user_id', '=', 'users.id') - ->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']) - ->whereNull('transactions.account_id') - ->get( - ['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'] - ); - - /** @var stdClass $entry */ - foreach ($set as $entry) { - $line = 'User #%d (%s) has account #%d ("%s") which has no transactions.'; - $line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name); - $this->line($line); - } - } - - /** - * Reports on budgets with no budget limits (which makes them pointless). - */ - private function reportBudgetLimits(): void - { - $set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') - ->leftJoin('users', 'budgets.user_id', '=', 'users.id') - ->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email']) - ->whereNull('budget_limits.id') - ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']); - - /** @var Budget $entry */ - foreach ($set as $entry) { - $line = sprintf( - 'User #%d (%s) has budget #%d ("%s") which has no budget limits.', - $entry->user_id, - $entry->email, - $entry->id, - $entry->name - ); - $this->line($line); - } - } - /** * Report on budgets with no transactions or journals. */ @@ -188,4 +143,49 @@ class ReportEmptyObjects extends Command $this->line($line); } } + + /** + * Reports on accounts with no transactions. + */ + private function reportAccounts(): void + { + $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') + ->leftJoin('users', 'accounts.user_id', '=', 'users.id') + ->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']) + ->whereNull('transactions.account_id') + ->get( + ['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'] + ); + + /** @var stdClass $entry */ + foreach ($set as $entry) { + $line = 'User #%d (%s) has account #%d ("%s") which has no transactions.'; + $line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name); + $this->line($line); + } + } + + /** + * Reports on budgets with no budget limits (which makes them pointless). + */ + private function reportBudgetLimits(): void + { + $set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') + ->leftJoin('users', 'budgets.user_id', '=', 'users.id') + ->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email']) + ->whereNull('budget_limits.id') + ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']); + + /** @var Budget $entry */ + foreach ($set as $entry) { + $line = sprintf( + 'User #%d (%s) has budget #%d ("%s") which has no budget limits.', + $entry->user_id, + $entry->email, + $entry->id, + $entry->name + ); + $this->line($line); + } + } } diff --git a/app/Console/Commands/Integrity/ReportSum.php b/app/Console/Commands/Integrity/ReportSum.php index 8b6f7f6cff..f11366c8f4 100644 --- a/app/Console/Commands/Integrity/ReportSum.php +++ b/app/Console/Commands/Integrity/ReportSum.php @@ -70,7 +70,7 @@ class ReportSum extends Command /** @var User $user */ foreach ($userRepository->all() as $user) { - $sum = (string) $user->transactions()->sum('amount'); + $sum = (string)$user->transactions()->sum('amount'); if (0 !== bccomp($sum, '0')) { $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum); $this->error($message); diff --git a/app/Console/Commands/Integrity/RestoreOAuthKeys.php b/app/Console/Commands/Integrity/RestoreOAuthKeys.php index 1d2913c108..9f8c66e76c 100644 --- a/app/Console/Commands/Integrity/RestoreOAuthKeys.php +++ b/app/Console/Commands/Integrity/RestoreOAuthKeys.php @@ -59,38 +59,6 @@ class RestoreOAuthKeys extends Command return 0; } - /** - * - */ - private function generateKeys(): void - { - OAuthKeys::generateKeys(); - } - - /** - * @return bool - */ - private function keysInDatabase(): bool - { - return OAuthKeys::keysInDatabase(); - } - - /** - * @return bool - */ - private function keysOnDrive(): bool - { - return OAuthKeys::hasKeyFiles(); - } - - /** - * - */ - private function restoreKeysFromDB(): void - { - OAuthKeys::restoreKeysFromDB(); - } - /** * */ @@ -122,6 +90,30 @@ class RestoreOAuthKeys extends Command $this->line('OAuth keys are OK'); } + /** + * @return bool + */ + private function keysInDatabase(): bool + { + return OAuthKeys::keysInDatabase(); + } + + /** + * @return bool + */ + private function keysOnDrive(): bool + { + return OAuthKeys::hasKeyFiles(); + } + + /** + * + */ + private function generateKeys(): void + { + OAuthKeys::generateKeys(); + } + /** * */ @@ -129,4 +121,12 @@ class RestoreOAuthKeys extends Command { OAuthKeys::storeKeysInDB(); } + + /** + * + */ + private function restoreKeysFromDB(): void + { + OAuthKeys::restoreKeysFromDB(); + } } diff --git a/app/Console/Commands/Tools/ApplyRules.php b/app/Console/Commands/Tools/ApplyRules.php index 553358e918..04295f3dc5 100644 --- a/app/Console/Commands/Tools/ApplyRules.php +++ b/app/Console/Commands/Tools/ApplyRules.php @@ -102,6 +102,7 @@ class ApplyRules extends Command $result = $this->verifyInput(); if (false === $result) { app('telemetry')->feature('system.command.errored', $this->signature); + return 1; } @@ -121,6 +122,7 @@ class ApplyRules extends Command $this->warn(' --all_rules'); app('telemetry')->feature('system.command.errored', $this->signature); + return 1; } @@ -132,7 +134,7 @@ class ApplyRules extends Command // add the accounts as filter: $filterAccountList = []; - foreach($this->accounts as $account) { + foreach ($this->accounts as $account) { $filterAccountList[] = $account->id; } $list = implode(',', $filterAccountList); @@ -157,49 +159,6 @@ class ApplyRules extends Command return 0; } - /** - * @return Collection - */ - private function getRulesToApply(): Collection - { - $rulesToApply = new Collection; - /** @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->push($rule); - } - } - } - - return $rulesToApply; - } - - /** - */ - private function grabAllRules(): void - { - $this->groups = $this->ruleGroupRepository->getActiveGroups(); - } - - /** - * @param Rule $rule - * @param RuleGroup $group - * - * @return bool - */ - private function includeRule(Rule $rule, RuleGroup $group): bool - { - return in_array($group->id, $this->ruleGroupSelection, true) - || in_array($rule->id, $this->ruleSelection, true) - || $this->allRules; - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -271,7 +230,7 @@ class ApplyRules extends Command foreach ($accountList as $accountId) { - $accountId = (int) $accountId; + $accountId = (int)$accountId; $account = $accountRepository->findNull($accountId); if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) { $finalList->push($account); @@ -289,42 +248,6 @@ class ApplyRules extends Command } - /** - * @throws FireflyException - */ - private function verifyInputDates(): void - { - // parse start date. - $inputStart = Carbon::now()->startOfMonth(); - $startString = $this->option('start_date'); - if (null === $startString) { - /** @var JournalRepositoryInterface $repository */ - $repository = app(JournalRepositoryInterface::class); - $repository->setUser($this->getUser()); - $first = $repository->firstNull(); - if (null !== $first) { - $inputStart = $first->date; - } - } - if (null !== $startString && '' !== $startString) { - $inputStart = Carbon::createFromFormat('Y-m-d', $startString); - } - - // parse end date - $inputEnd = Carbon::now(); - $endString = $this->option('end_date'); - if (null !== $endString && '' !== $endString) { - $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); - } - - if ($inputStart > $inputEnd) { - [$inputEnd, $inputStart] = [$inputStart, $inputEnd]; - } - - $this->startDate = $inputStart; - $this->endDate = $inputEnd; - } - /** * @return bool */ @@ -343,7 +266,7 @@ class ApplyRules extends Command } // @codeCoverageIgnoreEnd foreach ($ruleGroupList as $ruleGroupId) { - $ruleGroup = $this->ruleGroupRepository->find((int) $ruleGroupId); + $ruleGroup = $this->ruleGroupRepository->find((int)$ruleGroupId); if ($ruleGroup->active) { $this->ruleGroupSelection[] = $ruleGroup->id; } @@ -376,7 +299,7 @@ class ApplyRules extends Command // @codeCoverageIgnoreEnd foreach ($ruleList as $ruleId) { - $rule = $this->ruleRepository->find((int) $ruleId); + $rule = $this->ruleRepository->find((int)$ruleId); if (null !== $rule && $rule->active) { $this->ruleSelection[] = $rule->id; } @@ -384,4 +307,83 @@ class ApplyRules extends Command return true; } + + /** + * @throws FireflyException + */ + private function verifyInputDates(): void + { + // parse start date. + $inputStart = Carbon::now()->startOfMonth(); + $startString = $this->option('start_date'); + if (null === $startString) { + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepositoryInterface::class); + $repository->setUser($this->getUser()); + $first = $repository->firstNull(); + if (null !== $first) { + $inputStart = $first->date; + } + } + if (null !== $startString && '' !== $startString) { + $inputStart = Carbon::createFromFormat('Y-m-d', $startString); + } + + // parse end date + $inputEnd = Carbon::now(); + $endString = $this->option('end_date'); + if (null !== $endString && '' !== $endString) { + $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); + } + + if ($inputStart > $inputEnd) { + [$inputEnd, $inputStart] = [$inputStart, $inputEnd]; + } + + $this->startDate = $inputStart; + $this->endDate = $inputEnd; + } + + /** + */ + private function grabAllRules(): void + { + $this->groups = $this->ruleGroupRepository->getActiveGroups(); + } + + /** + * @return Collection + */ + private function getRulesToApply(): Collection + { + $rulesToApply = new Collection; + /** @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->push($rule); + } + } + } + + return $rulesToApply; + } + + /** + * @param Rule $rule + * @param RuleGroup $group + * + * @return bool + */ + private function includeRule(Rule $rule, RuleGroup $group): bool + { + return in_array($group->id, $this->ruleGroupSelection, true) + || in_array($rule->id, $this->ruleSelection, true) + || $this->allRules; + } } diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index e5d2a89b5e..0012981ff5 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -66,11 +66,11 @@ class Cron extends Command $date = null; try { $date = new Carbon($this->option('date')); - } catch (InvalidArgumentException|Exception $e) { + } catch (InvalidArgumentException | Exception $e) { $this->error(sprintf('"%s" is not a valid date', $this->option('date'))); $e->getMessage(); } - $force = (bool) $this->option('force'); + $force = (bool)$this->option('force'); /* * Fire recurring transaction cron job. @@ -108,9 +108,36 @@ class Cron extends Command $this->info('More feedback on the cron jobs can be found in the log files.'); app('telemetry')->feature('system.command.executed', $this->signature); + return 0; } + /** + * @param bool $force + * @param Carbon|null $date + * + * @throws FireflyException + */ + private function recurringCronJob(bool $force, ?Carbon $date): void + { + $recurring = new RecurringCronjob; + $recurring->setForce($force); + + // set date in cron job: + if (null !== $date) { + $recurring->setDate($date); + } + + $result = $recurring->fire(); + + if (false === $result) { + $this->line('The recurring transaction cron job did not fire.'); + } + if (true === $result) { + $this->line('The recurring transaction cron job fired successfully.'); + } + } + /** * @param bool $force * @param Carbon|null $date @@ -138,32 +165,6 @@ class Cron extends Command } - /** - * @param bool $force - * @param Carbon|null $date - * - * @throws FireflyException - */ - private function recurringCronJob(bool $force, ?Carbon $date): void - { - $recurring = new RecurringCronjob; - $recurring->setForce($force); - - // set date in cron job: - if (null !== $date) { - $recurring->setDate($date); - } - - $result = $recurring->fire(); - - if (false === $result) { - $this->line('The recurring transaction cron job did not fire.'); - } - if (true === $result) { - $this->line('The recurring transaction cron job fired successfully.'); - } - } - /** * @param bool $force * @param Carbon|null $date diff --git a/app/Console/Commands/Upgrade/AccountCurrencies.php b/app/Console/Commands/Upgrade/AccountCurrencies.php index 27a14030a6..ae331fa8e4 100644 --- a/app/Console/Commands/Upgrade/AccountCurrencies.php +++ b/app/Console/Commands/Upgrade/AccountCurrencies.php @@ -92,27 +92,6 @@ class AccountCurrencies extends Command return 0; } - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -127,6 +106,66 @@ class AccountCurrencies extends Command $this->count = 0; } + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + /** + * + */ + private function updateAccountCurrencies(): void + { + Log::debug('Now in updateAccountCurrencies()'); + $users = $this->userRepos->all(); + $defaultCurrencyCode = (string)config('firefly.default_currency', 'EUR'); + Log::debug(sprintf('Default currency is %s', $defaultCurrencyCode)); + foreach ($users as $user) { + $this->updateCurrenciesForUser($user, $defaultCurrencyCode); + } + } + + /** + * @param User $user + * @param string $systemCurrencyCode + */ + private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void + { + Log::debug(sprintf('Now in updateCurrenciesForUser(%s, %s)', $user->email, $systemCurrencyCode)); + $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; + if (!is_string($defaultCurrencyCode)) { + $defaultCurrencyCode = $systemCurrencyCode; + } + Log::debug(sprintf('Users currency pref is %s', $defaultCurrencyCode)); + + /** @var TransactionCurrency $defaultCurrency */ + $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); + + if (null === $defaultCurrency) { + Log::error(sprintf('Users currency pref "%s" does not exist!', $defaultCurrencyCode)); + $this->error(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode)); + + return; + } + + /** @var Account $account */ + foreach ($accounts as $account) { + $this->updateAccount($account, $defaultCurrency); + } + } + /** * @param Account $account * @param TransactionCurrency $currency @@ -136,13 +175,13 @@ class AccountCurrencies extends Command Log::debug(sprintf('Now in updateAccount(%d, %s)', $account->id, $currency->code)); $this->accountRepos->setUser($account->user); - $accountCurrency = (int) $this->accountRepos->getMetaValue($account, 'currency_id'); + $accountCurrency = (int)$this->accountRepos->getMetaValue($account, 'currency_id'); Log::debug(sprintf('Account currency is #%d', $accountCurrency)); $openingBalance = $this->accountRepos->getOpeningBalance($account); $obCurrency = 0; if (null !== $openingBalance) { - $obCurrency = (int) $openingBalance->transaction_currency_id; + $obCurrency = (int)$openingBalance->transaction_currency_id; Log::debug('Account has opening balance.'); } Log::debug(sprintf('Account OB currency is #%d.', $obCurrency)); @@ -192,47 +231,8 @@ class AccountCurrencies extends Command /** * */ - private function updateAccountCurrencies(): void + private function markAsExecuted(): void { - Log::debug('Now in updateAccountCurrencies()'); - $users = $this->userRepos->all(); - $defaultCurrencyCode = (string) config('firefly.default_currency', 'EUR'); - Log::debug(sprintf('Default currency is %s', $defaultCurrencyCode)); - foreach ($users as $user) { - $this->updateCurrenciesForUser($user, $defaultCurrencyCode); - } - } - - /** - * @param User $user - * @param string $systemCurrencyCode - */ - private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void - { - Log::debug(sprintf('Now in updateCurrenciesForUser(%s, %s)', $user->email, $systemCurrencyCode)); - $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; - if (!is_string($defaultCurrencyCode)) { - $defaultCurrencyCode = $systemCurrencyCode; - } - Log::debug(sprintf('Users currency pref is %s', $defaultCurrencyCode)); - - /** @var TransactionCurrency $defaultCurrency */ - $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); - - if (null === $defaultCurrency) { - Log::error(sprintf('Users currency pref "%s" does not exist!', $defaultCurrencyCode)); - $this->error(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode)); - - return; - } - - /** @var Account $account */ - foreach ($accounts as $account) { - $this->updateAccount($account, $defaultCurrency); - } + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php index a74f9ade7f..3af1a62fc5 100644 --- a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php +++ b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php @@ -79,7 +79,7 @@ class AppendBudgetLimitPeriods extends Command $this->theresNoLimit(); - $this->markAsExecuted(); + $this->markAsExecuted(); $end = round(microtime(true) - $start, 2); $this->info(sprintf('Fixed budget limits in %s seconds.', $end)); @@ -100,6 +100,44 @@ class AppendBudgetLimitPeriods extends Command return false; // @codeCoverageIgnore } + /** + * + */ + private function theresNoLimit(): void + { + $limits = BudgetLimit::whereNull('period')->get(); + /** @var BudgetLimit $limit */ + foreach ($limits as $limit) { + $this->fixLimit($limit); + } + } + + /** + * @param BudgetLimit $limit + */ + private function fixLimit(BudgetLimit $limit) + { + $period = $this->getLimitPeriod($limit); + + if (null === $period) { + $message = sprintf( + 'Could not guesstimate budget limit #%d (%s - %s) period.', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d') + ); + $this->warn($message); + Log::warning($message); + + return; + } + $limit->period = $period; + $limit->save(); + + $msg = sprintf( + 'Budget limit #%d (%s - %s) period is "%s".', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d'), $period + ); + Log::debug($msg); + + } + /** * @param BudgetLimit $limit * @@ -160,37 +198,4 @@ class AppendBudgetLimitPeriods extends Command { app('fireflyconfig')->set(self::CONFIG_NAME, true); } - - /** - * - */ - private function theresNoLimit(): void - { - $limits = BudgetLimit::whereNull('period')->get(); - /** @var BudgetLimit $limit */ - foreach ($limits as $limit) { - $this->fixLimit($limit); - } - } - - /** - * @param BudgetLimit $limit - */ - private function fixLimit(BudgetLimit $limit) - { - $period = $this->getLimitPeriod($limit); - - if (null === $period) { - $message = sprintf('Could not guesstimate budget limit #%d (%s - %s) period.', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d')); - $this->warn($message); - Log::warning($message); - return; - } - $limit->period = $period; - $limit->save(); - - $msg = sprintf('Budget limit #%d (%s - %s) period is "%s".', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d'), $period); - Log::debug($msg); - - } } diff --git a/app/Console/Commands/Upgrade/BackToJournals.php b/app/Console/Commands/Upgrade/BackToJournals.php index 6681fc030f..e2956bd6ef 100644 --- a/app/Console/Commands/Upgrade/BackToJournals.php +++ b/app/Console/Commands/Upgrade/BackToJournals.php @@ -83,43 +83,16 @@ class BackToJournals extends Command } /** - * @return array + * @return bool */ - private function getIdsForBudgets(): array + private function isMigrated(): bool { - $transactions = DB::table('budget_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); - $array = []; - $chunks = array_chunk($transactions, 500); - - foreach ($chunks as $chunk) { - $set = DB::table('transactions') - ->whereIn('transactions.id', $chunk) - ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); - /** @noinspection SlowArrayOperationsInLoopInspection */ - $array = array_merge($array, $set); + $configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; } - return $array; - } - - /** - * @return array - */ - private function getIdsForCategories(): array - { - $transactions = DB::table('category_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); - $array = []; - $chunks = array_chunk($transactions, 500); - - foreach ($chunks as $chunk) { - $set = DB::table('transactions') - ->whereIn('transactions.id', $chunk) - ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); - /** @noinspection SlowArrayOperationsInLoopInspection */ - $array = array_merge($array, $set); - } - - return $array; + return false; // @codeCoverageIgnore } /** @@ -129,33 +102,12 @@ class BackToJournals extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } - /** - * @return bool - */ - private function isMigrated(): bool - { - $configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * */ @@ -189,6 +141,26 @@ class BackToJournals extends Command } } + /** + * @return array + */ + private function getIdsForBudgets(): array + { + $transactions = DB::table('budget_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); + $array = []; + $chunks = array_chunk($transactions, 500); + + foreach ($chunks as $chunk) { + $set = DB::table('transactions') + ->whereIn('transactions.id', $chunk) + ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); + /** @noinspection SlowArrayOperationsInLoopInspection */ + $array = array_merge($array, $set); + } + + return $array; + } + /** * @param TransactionJournal $journal */ @@ -213,7 +185,7 @@ class BackToJournals extends Command // 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]); + $journal->budgets()->sync([(int)$budget->id]); return; } @@ -221,7 +193,7 @@ class BackToJournals extends Command // transaction has a budget, but the journal doesn't. if (null !== $budget && null === $journalBudget) { // sync to journal: - $journal->budgets()->sync([(int) $budget->id]); + $journal->budgets()->sync([(int)$budget->id]); } } @@ -249,6 +221,26 @@ class BackToJournals extends Command } } + /** + * @return array + */ + private function getIdsForCategories(): array + { + $transactions = DB::table('category_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); + $array = []; + $chunks = array_chunk($transactions, 500); + + foreach ($chunks as $chunk) { + $set = DB::table('transactions') + ->whereIn('transactions.id', $chunk) + ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); + /** @noinspection SlowArrayOperationsInLoopInspection */ + $array = array_merge($array, $set); + } + + return $array; + } + /** * @param TransactionJournal $journal */ @@ -272,12 +264,20 @@ class BackToJournals extends Command // 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]); + $journal->categories()->sync([(int)$category->id]); } // transaction has a category, but the journal doesn't. if (null !== $category && null === $journalCategory) { - $journal->categories()->sync([(int) $category->id]); + $journal->categories()->sync([(int)$category->id]); } } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); + } } diff --git a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php index 37ed9ff665..586d9fae96 100644 --- a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php +++ b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php @@ -103,7 +103,7 @@ class BudgetLimitCurrency extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/CCLiabilities.php b/app/Console/Commands/Upgrade/CCLiabilities.php index 36a23a96a8..e8f442f9df 100644 --- a/app/Console/Commands/Upgrade/CCLiabilities.php +++ b/app/Console/Commands/Upgrade/CCLiabilities.php @@ -102,7 +102,7 @@ class CCLiabilities extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/MigrateAttachments.php b/app/Console/Commands/Upgrade/MigrateAttachments.php index e401a12045..a02d72f793 100644 --- a/app/Console/Commands/Upgrade/MigrateAttachments.php +++ b/app/Console/Commands/Upgrade/MigrateAttachments.php @@ -72,7 +72,7 @@ class MigrateAttachments extends Command foreach ($attachments as $att) { // move description: - $attDescription = (string) $att->description; + $attDescription = (string)$att->description; if ('' !== $attDescription) { // find or create note: @@ -112,7 +112,7 @@ class MigrateAttachments extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/MigrateJournalNotes.php b/app/Console/Commands/Upgrade/MigrateJournalNotes.php index 72760471d8..5b1ef28746 100644 --- a/app/Console/Commands/Upgrade/MigrateJournalNotes.php +++ b/app/Console/Commands/Upgrade/MigrateJournalNotes.php @@ -111,7 +111,7 @@ class MigrateJournalNotes extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php index e02495bd89..0a35c94c5b 100644 --- a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php @@ -85,19 +85,26 @@ class MigrateRecurrenceMeta extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } - /** - * + * @return int */ - private function markAsExecuted(): void + private function migrateMetaData(): int { - app('fireflyconfig')->set(self::CONFIG_NAME, true); + $count = 0; + // get all recurrence meta data: + $collection = RecurrenceMeta::with('recurrence')->get(); + /** @var RecurrenceMeta $meta */ + foreach ($collection as $meta) { + $count += $this->migrateEntry($meta); + } + + return $count; } /** @@ -135,18 +142,10 @@ class MigrateRecurrenceMeta extends Command } /** - * @return int + * */ - private function migrateMetaData(): int + private function markAsExecuted(): void { - $count = 0; - // get all recurrence meta data: - $collection = RecurrenceMeta::with('recurrence')->get(); - /** @var RecurrenceMeta $meta */ - foreach ($collection as $meta) { - $count += $this->migrateEntry($meta); - } - - return $count; + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceType.php b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php new file mode 100644 index 0000000000..0a2ee674e8 --- /dev/null +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php @@ -0,0 +1,109 @@ +isExecuted() && true !== $this->option('force')) { + $this->warn('This command has already been executed.'); + + return 0; + } + + $this->migrateTypes(); + + //$this->markAsExecuted(); + + $end = round(microtime(true) - $start, 2); + $this->info(sprintf('Update recurring transaction types in %s seconds.', $end)); + + return 0; + } + + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + /** + * + */ + private function getInvalidType(): TransactionType + { + return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]); + } + + /** + * + */ + private function migrateTypes(): void + { + $set = Recurrence::get(); + /** @var Recurrence $recurrence */ + foreach ($set as $recurrence) { + if ($recurrence->transactionType->type !== TransactionType::INVALID) { + $this->migrateRecurrence($recurrence); + } + } + } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); + } + + private function migrateRecurrence(Recurrence $recurrence): void + { + $originalType = (int)$recurrence->transaction_type_id; + $newType = $this->getInvalidType(); + $recurrence->transaction_type_id = $newType->id; + $recurrence->save(); + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $transaction->transaction_type_id = $originalType; + $transaction->save(); + } + $this->line(sprintf('Updated recurrence #%d to new transaction type model.', $recurrence->id)); + } +} diff --git a/app/Console/Commands/Upgrade/MigrateTagLocations.php b/app/Console/Commands/Upgrade/MigrateTagLocations.php index b1693b9f40..b6a9bacbad 100644 --- a/app/Console/Commands/Upgrade/MigrateTagLocations.php +++ b/app/Console/Commands/Upgrade/MigrateTagLocations.php @@ -71,6 +71,30 @@ class MigrateTagLocations extends Command return 0; } + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + private function migrateTagLocations(): void + { + $tags = Tag::get(); + /** @var Tag $tag */ + foreach ($tags as $tag) { + if ($this->hasLocationDetails($tag)) { + $this->migrateLocationDetails($tag); + } + } + } + /** * @param Tag $tag * @@ -81,28 +105,6 @@ class MigrateTagLocations extends Command return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel; } - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * @param Tag $tag */ @@ -121,15 +123,12 @@ class MigrateTagLocations extends Command $tag->save(); } - private function migrateTagLocations(): void + /** + * + */ + private function markAsExecuted(): void { - $tags = Tag::get(); - /** @var Tag $tag */ - foreach ($tags as $tag) { - if ($this->hasLocationDetails($tag)) { - $this->migrateLocationDetails($tag); - } - } + app('fireflyconfig')->set(self::CONFIG_NAME, true); } diff --git a/app/Console/Commands/Upgrade/MigrateToGroups.php b/app/Console/Commands/Upgrade/MigrateToGroups.php index 94379c4473..7e12b33577 100644 --- a/app/Console/Commands/Upgrade/MigrateToGroups.php +++ b/app/Console/Commands/Upgrade/MigrateToGroups.php @@ -112,122 +112,19 @@ class MigrateToGroups extends Command } /** - * @param TransactionJournal $journal - * @param Transaction $transaction + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. * - * @return Transaction|null + * @codeCoverageIgnore */ - private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction + private function stupidLaravel(): void { - $set = $journal->transactions->filter( - static function (Transaction $subject) use ($transaction) { - $amount = (float) $transaction->amount * -1 === (float) $subject->amount; - $identifier = $transaction->identifier === $subject->identifier; - Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); - Log::debug(sprintf('ID the same? %s', var_export($identifier, true))); - - return $amount && $identifier; - } - ); - - return $set->first(); - } - - /** - * @param TransactionJournal $journal - * - * @return Collection - */ - private function getDestinationTransactions(TransactionJournal $journal): Collection - { - return $journal->transactions->filter( - static function (Transaction $transaction) { - return $transaction->amount > 0; - } - ); - } - - /** - * @param Transaction $left - * @param Transaction $right - * - * @return int|null - */ - private function getTransactionBudget(Transaction $left, Transaction $right): ?int - { - Log::debug('Now in getTransactionBudget()'); - - // try to get a budget ID from the left transaction: - /** @var Budget $budget */ - $budget = $left->budgets()->first(); - if (null !== $budget) { - Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); - - return (int) $budget->id; - } - - // try to get a budget ID from the right transaction: - /** @var Budget $budget */ - $budget = $right->budgets()->first(); - if (null !== $budget) { - Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); - - return (int) $budget->id; - } - Log::debug('Neither left or right have a budget, return NULL'); - - // if all fails, return NULL. - return null; - } - - /** - * @param Transaction $left - * @param Transaction $right - * - * @return int|null - */ - private function getTransactionCategory(Transaction $left, Transaction $right): ?int - { - Log::debug('Now in getTransactionCategory()'); - - // try to get a category ID from the left transaction: - /** @var Category $category */ - $category = $left->categories()->first(); - if (null !== $category) { - Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); - - return (int) $category->id; - } - - // try to get a category ID from the left transaction: - /** @var Category $category */ - $category = $right->categories()->first(); - if (null !== $category) { - Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); - - return (int) $category->id; - } - Log::debug('Neither left or right have a category, return NULL'); - - // if all fails, return NULL. - return null; - } - - /** - * @param array $array - */ - private function giveGroup(array $array): void - { - $groupId = DB::table('transaction_groups')->insertGetId( - [ - 'created_at' => date('Y-m-d H:i:s'), - 'updated_at' => date('Y-m-d H:i:s'), - 'title' => null, - 'user_id' => $array['user_id'], - ] - ); - DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]); - $this->count++; + $this->count = 0; + $this->journalRepository = app(JournalRepositoryInterface::class); + $this->service = app(JournalDestroyService::class); + $this->groupFactory = app(TransactionGroupFactory::class); + $this->cliRepository = app(JournalCLIRepositoryInterface::class); } /** @@ -237,32 +134,12 @@ class MigrateToGroups extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } - /** - * Gives all journals without a group a group. - */ - private function makeGroupsFromAll(): void - { - $orphanedJournals = $this->cliRepository->getJournalsWithoutGroup(); - $total = count($orphanedJournals); - if ($total > 0) { - Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); - $this->line(sprintf('Going to convert %d transaction journals. Please hold..', $total)); - /** @var array $journal */ - foreach ($orphanedJournals as $array) { - $this->giveGroup($array); - } - } - if (0 === $total) { - $this->info('No need to convert transaction journals.'); - } - } - /** * @throws Exception */ @@ -427,6 +304,145 @@ class MigrateToGroups extends Command ); } + /** + * @param TransactionJournal $journal + * + * @return Collection + */ + private function getDestinationTransactions(TransactionJournal $journal): Collection + { + return $journal->transactions->filter( + static function (Transaction $transaction) { + return $transaction->amount > 0; + } + ); + } + + /** + * @param TransactionJournal $journal + * @param Transaction $transaction + * + * @return Transaction|null + */ + private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction + { + $set = $journal->transactions->filter( + static function (Transaction $subject) use ($transaction) { + $amount = (float)$transaction->amount * -1 === (float)$subject->amount; + $identifier = $transaction->identifier === $subject->identifier; + Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); + Log::debug(sprintf('ID the same? %s', var_export($identifier, true))); + + return $amount && $identifier; + } + ); + + return $set->first(); + } + + /** + * @param Transaction $left + * @param Transaction $right + * + * @return int|null + */ + private function getTransactionBudget(Transaction $left, Transaction $right): ?int + { + Log::debug('Now in getTransactionBudget()'); + + // try to get a budget ID from the left transaction: + /** @var Budget $budget */ + $budget = $left->budgets()->first(); + if (null !== $budget) { + Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); + + return (int)$budget->id; + } + + // try to get a budget ID from the right transaction: + /** @var Budget $budget */ + $budget = $right->budgets()->first(); + if (null !== $budget) { + Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); + + return (int)$budget->id; + } + Log::debug('Neither left or right have a budget, return NULL'); + + // if all fails, return NULL. + return null; + } + + /** + * @param Transaction $left + * @param Transaction $right + * + * @return int|null + */ + private function getTransactionCategory(Transaction $left, Transaction $right): ?int + { + Log::debug('Now in getTransactionCategory()'); + + // try to get a category ID from the left transaction: + /** @var Category $category */ + $category = $left->categories()->first(); + if (null !== $category) { + Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); + + return (int)$category->id; + } + + // try to get a category ID from the left transaction: + /** @var Category $category */ + $category = $right->categories()->first(); + if (null !== $category) { + Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); + + return (int)$category->id; + } + Log::debug('Neither left or right have a category, return NULL'); + + // if all fails, return NULL. + return null; + } + + /** + * Gives all journals without a group a group. + */ + private function makeGroupsFromAll(): void + { + $orphanedJournals = $this->cliRepository->getJournalsWithoutGroup(); + $total = count($orphanedJournals); + if ($total > 0) { + Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); + $this->line(sprintf('Going to convert %d transaction journals. Please hold..', $total)); + /** @var array $journal */ + foreach ($orphanedJournals as $array) { + $this->giveGroup($array); + } + } + if (0 === $total) { + $this->info('No need to convert transaction journals.'); + } + } + + /** + * @param array $array + */ + private function giveGroup(array $array): void + { + $groupId = DB::table('transaction_groups')->insertGetId( + [ + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + 'title' => null, + 'user_id' => $array['user_id'], + ] + ); + DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]); + $this->count++; + } + /** * */ @@ -434,20 +450,4 @@ class MigrateToGroups extends Command { app('fireflyconfig')->set(self::CONFIG_NAME, true); } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->journalRepository = app(JournalRepositoryInterface::class); - $this->service = app(JournalDestroyService::class); - $this->groupFactory = app(TransactionGroupFactory::class); - $this->cliRepository = app(JournalCLIRepositoryInterface::class); - } } diff --git a/app/Console/Commands/Upgrade/MigrateToRules.php b/app/Console/Commands/Upgrade/MigrateToRules.php index 5029dbd70d..d7b8a1a2b3 100644 --- a/app/Console/Commands/Upgrade/MigrateToRules.php +++ b/app/Console/Commands/Upgrade/MigrateToRules.php @@ -67,8 +67,8 @@ class MigrateToRules extends Command /** * Execute the console command. * - * @throws FireflyException * @return int + * @throws FireflyException */ public function handle(): int { @@ -103,6 +103,22 @@ class MigrateToRules extends Command return 0; } + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->count = 0; + $this->userRepository = app(UserRepositoryInterface::class); + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->billRepository = app(BillRepositoryInterface::class); + $this->ruleRepository = app(RuleRepositoryInterface::class); + } + /** * @return bool */ @@ -110,18 +126,46 @@ class MigrateToRules extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } /** + * Migrate bills to new rule structure for a specific user. * + * @param User $user + * + * @throws FireflyException */ - private function markAsExecuted(): void + private function migrateUser(User $user): void { - app('fireflyconfig')->set(self::CONFIG_NAME, true); + $this->ruleGroupRepository->setUser($user); + $this->billRepository->setUser($user); + $this->ruleRepository->setUser($user); + + /** @var Preference $lang */ + $lang = app('preferences')->getForUser($user, 'language', 'en_US'); + $groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data); + $ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle); + + if (null === $ruleGroup) { + $ruleGroup = $this->ruleGroupRepository->store( + [ + 'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data), + 'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data), + 'active' => true, + ] + ); + } + $bills = $this->billRepository->getBills(); + + /** @var Bill $bill */ + foreach ($bills as $bill) { + $this->migrateBill($ruleGroup, $bill, $lang); + } + } /** @@ -142,8 +186,8 @@ class MigrateToRules extends Command 'active' => true, 'strict' => false, 'stop_processing' => false, // field is no longer used. - 'title' => (string) trans('firefly.rule_for_bill_title', ['name' => $bill->name], $language->data), - 'description' => (string) trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data), + 'title' => (string)trans('firefly.rule_for_bill_title', ['name' => $bill->name], $language->data), + 'description' => (string)trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data), 'trigger' => 'store-journal', 'triggers' => [ [ @@ -196,54 +240,10 @@ class MigrateToRules extends Command } /** - * Migrate bills to new rule structure for a specific user. * - * @param User $user - * - * @throws FireflyException */ - private function migrateUser(User $user): void + private function markAsExecuted(): void { - $this->ruleGroupRepository->setUser($user); - $this->billRepository->setUser($user); - $this->ruleRepository->setUser($user); - - /** @var Preference $lang */ - $lang = app('preferences')->getForUser($user, 'language', 'en_US'); - $groupTitle = (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data); - $ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle); - - if (null === $ruleGroup) { - $ruleGroup = $this->ruleGroupRepository->store( - [ - 'title' => (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data), - 'description' => (string) trans('firefly.rulegroup_for_bills_description', [], $lang->data), - 'active' => true, - ] - ); - } - $bills = $this->billRepository->getBills(); - - /** @var Bill $bill */ - foreach ($bills as $bill) { - $this->migrateBill($ruleGroup, $bill, $lang); - } - - } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->userRepository = app(UserRepositoryInterface::class); - $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); - $this->billRepository = app(BillRepositoryInterface::class); - $this->ruleRepository = app(RuleRepositoryInterface::class); + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php index ba3999405a..7aee00ec54 100644 --- a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php @@ -95,68 +95,20 @@ class OtherCurrenciesCorrections extends Command } /** - * @param Account $account + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. * - * @return TransactionCurrency|null + * @codeCoverageIgnore */ - private function getCurrency(Account $account): ?TransactionCurrency + private function stupidLaravel(): void { - $accountId = $account->id; - if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { - return null; // @codeCoverageIgnore - } - if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { - return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore - } - $currency = $this->accountRepos->getAccountCurrency($account); - if (null === $currency) { - // @codeCoverageIgnoreStart - $this->accountCurrencies[$accountId] = 0; - - return null; - // @codeCoverageIgnoreEnd - } - $this->accountCurrencies[$accountId] = $currency; - - return $currency; - } - - /** - * Gets the transaction that determines the transaction that "leads" and will determine - * the currency to be used by all transactions, and the journal itself. - * - * @param TransactionJournal $journal - * - * @return Transaction|null - */ - private function getLeadTransaction(TransactionJournal $journal): ?Transaction - { - /** @var Transaction $lead */ - $lead = null; - switch ($journal->transactionType->type) { - default: - break; - case TransactionType::WITHDRAWAL: - $lead = $journal->transactions()->where('amount', '<', 0)->first(); - break; - case TransactionType::DEPOSIT: - $lead = $journal->transactions()->where('amount', '>', 0)->first(); - break; - case TransactionType::OPENING_BALANCE: - // whichever isn't an initial balance account: - $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( - 'account_types', 'accounts.account_type_id', '=', 'account_types.id' - )->where('account_types.type', '!=', AccountType::INITIAL_BALANCE)->first(['transactions.*']); - break; - case TransactionType::RECONCILIATION: - // whichever isn't the reconciliation account: - $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( - 'account_types', 'accounts.account_type_id', '=', 'account_types.id' - )->where('account_types.type', '!=', AccountType::RECONCILIATION)->first(['transactions.*']); - break; - } - - return $lead; + $this->count = 0; + $this->accountCurrencies = []; + $this->accountRepos = app(AccountRepositoryInterface::class); + $this->currencyRepos = app(CurrencyRepositoryInterface::class); + $this->journalRepos = app(JournalRepositoryInterface::class); + $this->cliRepos = app(JournalCLIRepositoryInterface::class); } /** @@ -173,28 +125,21 @@ class OtherCurrenciesCorrections extends Command } /** - * + * This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for + * the accounts they are linked to. + * Both source and destination must match the respective currency preference of the related asset account. + * So FF3 must verify all transactions. */ - private function markAsExecuted(): void + private function updateOtherJournalsCurrencies(): void { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } + $set = $this->cliRepos->getAllJournals( + [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,] + ); - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->accountCurrencies = []; - $this->accountRepos = app(AccountRepositoryInterface::class); - $this->currencyRepos = app(CurrencyRepositoryInterface::class); - $this->journalRepos = app(JournalRepositoryInterface::class); - $this->cliRepos = app(JournalCLIRepositoryInterface::class); + /** @var TransactionJournal $journal */ + foreach ($set as $journal) { + $this->updateJournalCurrency($journal); + } } /** @@ -256,20 +201,75 @@ class OtherCurrenciesCorrections extends Command } /** - * This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for - * the accounts they are linked to. - * Both source and destination must match the respective currency preference of the related asset account. - * So FF3 must verify all transactions. + * Gets the transaction that determines the transaction that "leads" and will determine + * the currency to be used by all transactions, and the journal itself. + * + * @param TransactionJournal $journal + * + * @return Transaction|null */ - private function updateOtherJournalsCurrencies(): void + private function getLeadTransaction(TransactionJournal $journal): ?Transaction { - $set = $this->cliRepos->getAllJournals( - [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,] - ); - - /** @var TransactionJournal $journal */ - foreach ($set as $journal) { - $this->updateJournalCurrency($journal); + /** @var Transaction $lead */ + $lead = null; + switch ($journal->transactionType->type) { + default: + break; + case TransactionType::WITHDRAWAL: + $lead = $journal->transactions()->where('amount', '<', 0)->first(); + break; + case TransactionType::DEPOSIT: + $lead = $journal->transactions()->where('amount', '>', 0)->first(); + break; + case TransactionType::OPENING_BALANCE: + // whichever isn't an initial balance account: + $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( + 'account_types', 'accounts.account_type_id', '=', 'account_types.id' + )->where('account_types.type', '!=', AccountType::INITIAL_BALANCE)->first(['transactions.*']); + break; + case TransactionType::RECONCILIATION: + // whichever isn't the reconciliation account: + $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( + 'account_types', 'accounts.account_type_id', '=', 'account_types.id' + )->where('account_types.type', '!=', AccountType::RECONCILIATION)->first(['transactions.*']); + break; } + + return $lead; + } + + /** + * @param Account $account + * + * @return TransactionCurrency|null + */ + private function getCurrency(Account $account): ?TransactionCurrency + { + $accountId = $account->id; + if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { + return null; // @codeCoverageIgnore + } + if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { + return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore + } + $currency = $this->accountRepos->getAccountCurrency($account); + if (null === $currency) { + // @codeCoverageIgnoreStart + $this->accountCurrencies[$accountId] = 0; + + return null; + // @codeCoverageIgnoreEnd + } + $this->accountCurrencies[$accountId] = $currency; + + return $currency; + } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/RenameAccountMeta.php b/app/Console/Commands/Upgrade/RenameAccountMeta.php index 01aaa1b6fd..d697c17c34 100644 --- a/app/Console/Commands/Upgrade/RenameAccountMeta.php +++ b/app/Console/Commands/Upgrade/RenameAccountMeta.php @@ -102,7 +102,7 @@ class RenameAccountMeta extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/TransactionIdentifier.php b/app/Console/Commands/Upgrade/TransactionIdentifier.php index d220a8b27e..9ddad9f896 100644 --- a/app/Console/Commands/Upgrade/TransactionIdentifier.php +++ b/app/Console/Commands/Upgrade/TransactionIdentifier.php @@ -105,60 +105,6 @@ class TransactionIdentifier extends Command return 0; } - /** - * @param Transaction $transaction - * @param array $exclude - * - * @return Transaction|null - */ - private function findOpposing(Transaction $transaction, array $exclude): ?Transaction - { - // find opposing: - $amount = bcmul((string) $transaction->amount, '-1'); - - try { - /** @var Transaction $opposing */ - $opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id) - ->where('amount', $amount)->where('identifier', '=', 0) - ->whereNotIn('id', $exclude) - ->first(); - // @codeCoverageIgnoreStart - } catch (QueryException $e) { - Log::error($e->getMessage()); - $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); - $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); - $this->error('Please run "php artisan migrate" to add this field to the table.'); - $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); - - return null; - } - - // @codeCoverageIgnoreEnd - - return $opposing; - } - - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -173,6 +119,19 @@ class TransactionIdentifier extends Command $this->count = 0; } + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + /** * Grab all positive transactions from this journal that are not deleted. for each one, grab the negative opposing one * which has 0 as an identifier and give it the same identifier. @@ -202,4 +161,45 @@ class TransactionIdentifier extends Command } } + + /** + * @param Transaction $transaction + * @param array $exclude + * + * @return Transaction|null + */ + private function findOpposing(Transaction $transaction, array $exclude): ?Transaction + { + // find opposing: + $amount = bcmul((string)$transaction->amount, '-1'); + + try { + /** @var Transaction $opposing */ + $opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id) + ->where('amount', $amount)->where('identifier', '=', 0) + ->whereNotIn('id', $exclude) + ->first(); + // @codeCoverageIgnoreStart + } catch (QueryException $e) { + Log::error($e->getMessage()); + $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); + $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); + $this->error('Please run "php artisan migrate" to add this field to the table.'); + $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); + + return null; + } + + // @codeCoverageIgnoreEnd + + return $opposing; + } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); + } } diff --git a/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php b/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php index 7565e20473..b23de4ae19 100644 --- a/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php @@ -56,12 +56,12 @@ class TransferCurrenciesCorrections extends Command private JournalCLIRepositoryInterface $cliRepos; private int $count; - private ?Account $destinationAccount; - private ?TransactionCurrency $destinationCurrency; - private ?Transaction $destinationTransaction; - private ?Account $sourceAccount; - private ?TransactionCurrency $sourceCurrency; - private ?Transaction $sourceTransaction; + private ?Account $destinationAccount; + private ?TransactionCurrency $destinationCurrency; + private ?Transaction $destinationTransaction; + private ?Account $sourceAccount; + private ?TransactionCurrency $sourceCurrency; + private ?Transaction $sourceTransaction; /** * Execute the console command. @@ -99,6 +99,313 @@ class TransferCurrenciesCorrections extends Command return 0; } + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->count = 0; + $this->accountRepos = app(AccountRepositoryInterface::class); + $this->cliRepos = app(JournalCLIRepositoryInterface::class); + $this->accountCurrencies = []; + $this->resetInformation(); + } + + /** + * Reset all the class fields for the current transfer. + * + * @codeCoverageIgnore + */ + private function resetInformation(): void + { + $this->sourceTransaction = null; + $this->sourceAccount = null; + $this->sourceCurrency = null; + $this->destinationTransaction = null; + $this->destinationAccount = null; + $this->destinationCurrency = null; + } + + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + /** + * This routine verifies that transfers have the correct currency settings for the accounts they are linked to. + * For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they + * like it or not. Previous routines MUST have set the currency setting for both accounts for this to work. + * + * Both source and destination must match the respective currency preference. So FF3 must verify ALL + * transactions. + */ + private function startUpdateRoutine(): void + { + $set = $this->cliRepos->getAllJournals([TransactionType::TRANSFER]); + /** @var TransactionJournal $journal */ + foreach ($set as $journal) { + $this->updateTransferCurrency($journal); + } + } + + /** + * @param TransactionJournal $transfer + */ + private function updateTransferCurrency(TransactionJournal $transfer): void + { + $this->resetInformation(); + + // @codeCoverageIgnoreStart + if ($this->isSplitJournal($transfer)) { + $this->line(sprintf(sprintf('Transaction journal #%d is a split journal. Cannot continue.', $transfer->id))); + + return; + } + // @codeCoverageIgnoreEnd + + $this->getSourceInformation($transfer); + $this->getDestinationInformation($transfer); + + // unexpectedly, either one is null: + // @codeCoverageIgnoreStart + if ($this->isEmptyTransactions()) { + $this->error(sprintf('Source or destination information for transaction journal #%d is null. Cannot fix this one.', $transfer->id)); + + return; + } + // @codeCoverageIgnoreEnd + + + // both accounts must have currency preference: + // @codeCoverageIgnoreStart + if ($this->isNoCurrencyPresent()) { + $this->error( + sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id) + ); + + return; + } + // @codeCoverageIgnoreEnd + + // fix source transaction having no currency. + $this->fixSourceNoCurrency(); + + // fix source transaction having bad currency. + $this->fixSourceUnmatchedCurrency(); + + // fix destination transaction having no currency. + $this->fixDestNoCurrency(); + + // fix destination transaction having bad currency. + $this->fixDestinationUnmatchedCurrency(); + + // remove foreign currency information if not necessary. + $this->fixInvalidForeignCurrency(); + // correct foreign currency info if necessary. + $this->fixMismatchedForeignCurrency(); + + // restore missing foreign currency amount. + $this->fixSourceNullForeignAmount(); + + $this->fixDestNullForeignAmount(); + + // fix journal itself: + $this->fixTransactionJournalCurrency($transfer); + } + + /** + * Is this a split transaction journal? + * + * @param TransactionJournal $transfer + * + * @return bool + * @codeCoverageIgnore + */ + private function isSplitJournal(TransactionJournal $transfer): bool + { + return $transfer->transactions->count() > 2; + } + + /** + * Extract source transaction, source account + source account currency from the journal. + * + * @param TransactionJournal $journal + * + * @codeCoverageIgnore + */ + private function getSourceInformation(TransactionJournal $journal): void + { + $this->sourceTransaction = $this->getSourceTransaction($journal); + $this->sourceAccount = null === $this->sourceTransaction ? null : $this->sourceTransaction->account; + $this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount); + } + + /** + * @param TransactionJournal $transfer + * + * @return Transaction|null + * @codeCoverageIgnore + */ + private function getSourceTransaction(TransactionJournal $transfer): ?Transaction + { + return $transfer->transactions()->where('amount', '<', 0)->first(); + } + + /** + * @param Account $account + * + * @return TransactionCurrency|null + */ + private function getCurrency(Account $account): ?TransactionCurrency + { + $accountId = $account->id; + if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { + return null; // @codeCoverageIgnore + } + if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { + return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore + } + $currency = $this->accountRepos->getAccountCurrency($account); + if (null === $currency) { + // @codeCoverageIgnoreStart + $this->accountCurrencies[$accountId] = 0; + + return null; + // @codeCoverageIgnoreEnd + } + $this->accountCurrencies[$accountId] = $currency; + + return $currency; + } + + /** + * Extract destination transaction, destination account + destination account currency from the journal. + * + * @param TransactionJournal $journal + * + * @codeCoverageIgnore + */ + private function getDestinationInformation(TransactionJournal $journal): void + { + $this->destinationTransaction = $this->getDestinationTransaction($journal); + $this->destinationAccount = null === $this->destinationTransaction ? null : $this->destinationTransaction->account; + $this->destinationCurrency = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount); + } + + /** + * @param TransactionJournal $transfer + * + * @return Transaction|null + * @codeCoverageIgnore + */ + private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction + { + return $transfer->transactions()->where('amount', '>', 0)->first(); + } + + /** + * Is either the source or destination transaction NULL? + * + * @return bool + * @codeCoverageIgnore + */ + private function isEmptyTransactions(): bool + { + return null === $this->sourceTransaction || null === $this->destinationTransaction + || null === $this->sourceAccount + || null === $this->destinationAccount; + } + + /** + * @return bool + * @codeCoverageIgnore + */ + private function isNoCurrencyPresent(): bool + { + // source account must have a currency preference. + if (null === $this->sourceCurrency) { + $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); + Log::error($message); + $this->error($message); + + return true; + } + + // destination account must have a currency preference. + if (null === $this->destinationCurrency) { + $message = sprintf( + 'Account #%d ("%s") must have currency preference but has none.', + $this->destinationAccount->id, + $this->destinationAccount->name + ); + Log::error($message); + $this->error($message); + + return true; + } + + return false; + } + + /** + * The source transaction must have a currency. If not, it will be added by + * taking it from the source account's preference. + */ + private function fixSourceNoCurrency(): void + { + if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { + $this->sourceTransaction + ->transaction_currency_id + = (int)$this->sourceCurrency->id; + $message = sprintf( + 'Transaction #%d has no currency setting, now set to %s.', + $this->sourceTransaction->id, + $this->sourceCurrency->code + ); + Log::debug($message); + $this->line($message); + $this->count++; + $this->sourceTransaction->save(); + } + } + + /** + * The source transaction must have the correct currency. If not, it will be set by + * taking it from the source account's preference. + */ + private function fixSourceUnmatchedCurrency(): void + { + if (null !== $this->sourceCurrency + && null === $this->sourceTransaction->foreign_amount + && (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id + ) { + $message = sprintf( + 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', + $this->sourceTransaction->id, + $this->sourceTransaction->transaction_currency_id, + $this->sourceAccount->id, + $this->sourceTransaction->amount + ); + Log::debug($message); + $this->line($message); + $this->count++; + $this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id; + $this->sourceTransaction->save(); + } + } + /** * The destination transaction must have a currency. If not, it will be added by * taking it from the destination account's preference. @@ -121,26 +428,6 @@ class TransferCurrenciesCorrections extends Command } } - /** - * If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code - * to restore it. - */ - private function fixDestNullForeignAmount(): void - { - if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { - $this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1'); - $this->destinationTransaction->save(); - $this->count++; - Log::debug( - sprintf( - 'Restored foreign amount of destination transaction #%d to %s', - $this->destinationTransaction->id, - $this->destinationTransaction->foreign_amount - ) - ); - } - } - /** * The destination transaction must have the correct currency. If not, it will be set by * taking it from the destination account's preference. @@ -220,28 +507,6 @@ class TransferCurrenciesCorrections extends Command } } - /** - * The source transaction must have a currency. If not, it will be added by - * taking it from the source account's preference. - */ - private function fixSourceNoCurrency(): void - { - if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { - $this->sourceTransaction - ->transaction_currency_id - = (int)$this->sourceCurrency->id; - $message = sprintf( - 'Transaction #%d has no currency setting, now set to %s.', - $this->sourceTransaction->id, - $this->sourceCurrency->code - ); - Log::debug($message); - $this->line($message); - $this->count++; - $this->sourceTransaction->save(); - } - } - /** * If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code * to restore it. @@ -263,27 +528,22 @@ class TransferCurrenciesCorrections extends Command } /** - * The source transaction must have the correct currency. If not, it will be set by - * taking it from the source account's preference. + * If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code + * to restore it. */ - private function fixSourceUnmatchedCurrency(): void + private function fixDestNullForeignAmount(): void { - if (null !== $this->sourceCurrency - && null === $this->sourceTransaction->foreign_amount - && (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id - ) { - $message = sprintf( - 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', - $this->sourceTransaction->id, - $this->sourceTransaction->transaction_currency_id, - $this->sourceAccount->id, - $this->sourceTransaction->amount - ); - Log::debug($message); - $this->line($message); + if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { + $this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1'); + $this->destinationTransaction->save(); $this->count++; - $this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id; - $this->sourceTransaction->save(); + Log::debug( + sprintf( + 'Restored foreign amount of destination transaction #%d to %s', + $this->destinationTransaction->id, + $this->destinationTransaction->foreign_amount + ) + ); } } @@ -311,153 +571,6 @@ class TransferCurrenciesCorrections extends Command } } - /** - * @param Account $account - * - * @return TransactionCurrency|null - */ - private function getCurrency(Account $account): ?TransactionCurrency - { - $accountId = $account->id; - if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { - return null; // @codeCoverageIgnore - } - if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { - return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore - } - $currency = $this->accountRepos->getAccountCurrency($account); - if (null === $currency) { - // @codeCoverageIgnoreStart - $this->accountCurrencies[$accountId] = 0; - - return null; - // @codeCoverageIgnoreEnd - } - $this->accountCurrencies[$accountId] = $currency; - - return $currency; - } - - /** - * Extract destination transaction, destination account + destination account currency from the journal. - * - * @param TransactionJournal $journal - * - * @codeCoverageIgnore - */ - private function getDestinationInformation(TransactionJournal $journal): void - { - $this->destinationTransaction = $this->getDestinationTransaction($journal); - $this->destinationAccount = null === $this->destinationTransaction ? null : $this->destinationTransaction->account; - $this->destinationCurrency = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount); - } - - /** - * @param TransactionJournal $transfer - * - * @return Transaction|null - * @codeCoverageIgnore - */ - private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction - { - return $transfer->transactions()->where('amount', '>', 0)->first(); - } - - /** - * Extract source transaction, source account + source account currency from the journal. - * - * @param TransactionJournal $journal - * - * @codeCoverageIgnore - */ - private function getSourceInformation(TransactionJournal $journal): void - { - $this->sourceTransaction = $this->getSourceTransaction($journal); - $this->sourceAccount = null === $this->sourceTransaction ? null : $this->sourceTransaction->account; - $this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount); - } - - /** - * @param TransactionJournal $transfer - * - * @return Transaction|null - * @codeCoverageIgnore - */ - private function getSourceTransaction(TransactionJournal $transfer): ?Transaction - { - return $transfer->transactions()->where('amount', '<', 0)->first(); - } - - /** - * Is either the source or destination transaction NULL? - * - * @return bool - * @codeCoverageIgnore - */ - private function isEmptyTransactions(): bool - { - return null === $this->sourceTransaction || null === $this->destinationTransaction - || null === $this->sourceAccount - || null === $this->destinationAccount; - } - - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool)$configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * @return bool - * @codeCoverageIgnore - */ - private function isNoCurrencyPresent(): bool - { - // source account must have a currency preference. - if (null === $this->sourceCurrency) { - $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); - Log::error($message); - $this->error($message); - - return true; - } - - // destination account must have a currency preference. - if (null === $this->destinationCurrency) { - $message = sprintf( - 'Account #%d ("%s") must have currency preference but has none.', - $this->destinationAccount->id, - $this->destinationAccount->name - ); - Log::error($message); - $this->error($message); - - return true; - } - - return false; - } - - /** - * Is this a split transaction journal? - * - * @param TransactionJournal $transfer - * - * @return bool - * @codeCoverageIgnore - */ - private function isSplitJournal(TransactionJournal $transfer): bool - { - return $transfer->transactions->count() > 2; - } - /** * */ @@ -465,117 +578,4 @@ class TransferCurrenciesCorrections extends Command { app('fireflyconfig')->set(self::CONFIG_NAME, true); } - - /** - * Reset all the class fields for the current transfer. - * - * @codeCoverageIgnore - */ - private function resetInformation(): void - { - $this->sourceTransaction = null; - $this->sourceAccount = null; - $this->sourceCurrency = null; - $this->destinationTransaction = null; - $this->destinationAccount = null; - $this->destinationCurrency = null; - } - - /** - * This routine verifies that transfers have the correct currency settings for the accounts they are linked to. - * For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they - * like it or not. Previous routines MUST have set the currency setting for both accounts for this to work. - * - * Both source and destination must match the respective currency preference. So FF3 must verify ALL - * transactions. - */ - private function startUpdateRoutine(): void - { - $set = $this->cliRepos->getAllJournals([TransactionType::TRANSFER]); - /** @var TransactionJournal $journal */ - foreach ($set as $journal) { - $this->updateTransferCurrency($journal); - } - } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->accountRepos = app(AccountRepositoryInterface::class); - $this->cliRepos = app(JournalCLIRepositoryInterface::class); - $this->accountCurrencies = []; - $this->resetInformation(); - } - - /** - * @param TransactionJournal $transfer - */ - private function updateTransferCurrency(TransactionJournal $transfer): void - { - $this->resetInformation(); - - // @codeCoverageIgnoreStart - if ($this->isSplitJournal($transfer)) { - $this->line(sprintf(sprintf('Transaction journal #%d is a split journal. Cannot continue.', $transfer->id))); - - return; - } - // @codeCoverageIgnoreEnd - - $this->getSourceInformation($transfer); - $this->getDestinationInformation($transfer); - - // unexpectedly, either one is null: - // @codeCoverageIgnoreStart - if ($this->isEmptyTransactions()) { - $this->error(sprintf('Source or destination information for transaction journal #%d is null. Cannot fix this one.', $transfer->id)); - - return; - } - // @codeCoverageIgnoreEnd - - - // both accounts must have currency preference: - // @codeCoverageIgnoreStart - if ($this->isNoCurrencyPresent()) { - $this->error( - sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id) - ); - - return; - } - // @codeCoverageIgnoreEnd - - // fix source transaction having no currency. - $this->fixSourceNoCurrency(); - - // fix source transaction having bad currency. - $this->fixSourceUnmatchedCurrency(); - - // fix destination transaction having no currency. - $this->fixDestNoCurrency(); - - // fix destination transaction having bad currency. - $this->fixDestinationUnmatchedCurrency(); - - // remove foreign currency information if not necessary. - $this->fixInvalidForeignCurrency(); - // correct foreign currency info if necessary. - $this->fixMismatchedForeignCurrency(); - - // restore missing foreign currency amount. - $this->fixSourceNullForeignAmount(); - - $this->fixDestNullForeignAmount(); - - // fix journal itself: - $this->fixTransactionJournalCurrency($transfer); - } } diff --git a/app/Console/Commands/Upgrade/UpgradeDatabase.php b/app/Console/Commands/Upgrade/UpgradeDatabase.php index 0167595dad..ca3d4ca158 100644 --- a/app/Console/Commands/Upgrade/UpgradeDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradeDatabase.php @@ -76,6 +76,7 @@ class UpgradeDatabase extends Command 'firefly-iii:rename-account-meta', 'firefly-iii:migrate-recurrence-meta', 'firefly-iii:migrate-tag-locations', + 'firefly-iii:migrate-recurrence-type', // there are 16 verify commands. 'firefly-iii:fix-piggies', @@ -117,9 +118,9 @@ class UpgradeDatabase extends Command echo $result; } // set new DB version. - app('fireflyconfig')->set('db_version', (int) config('firefly.db_version')); + app('fireflyconfig')->set('db_version', (int)config('firefly.db_version')); // index will set FF3 version. - app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); + app('fireflyconfig')->set('ff3_version', (string)config('firefly.version')); return 0; } diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index bc53457f92..e540f432f7 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -83,6 +83,7 @@ class InstallController extends Controller 'firefly-iii:rename-account-meta' => [], 'firefly-iii:migrate-recurrence-meta' => [], 'firefly-iii:migrate-tag-locations' => [], + 'firefly-iii:migrate-recurrence-type' => [], // verify commands 'firefly-iii:fix-piggies' => [], @@ -120,10 +121,10 @@ class InstallController extends Controller public function index() { // index will set FF3 version. - app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); + app('fireflyconfig')->set('ff3_version', (string)config('firefly.version')); // set new DB version. - app('fireflyconfig')->set('db_version', (int) config('firefly.db_version')); + app('fireflyconfig')->set('db_version', (int)config('firefly.db_version')); return prefixView('install.index'); } @@ -156,7 +157,7 @@ class InstallController extends Controller */ public function runCommand(Request $request): JsonResponse { - $requestIndex = (int) $request->get('index'); + $requestIndex = (int)$request->get('index'); $response = [ 'hasNextCommand' => false, 'done' => true, @@ -183,6 +184,7 @@ class InstallController extends Controller if (false === $result) { $response['errorMessage'] = $this->lastError; $response['error'] = true; + return response()->json($response); } $index++; @@ -197,6 +199,7 @@ class InstallController extends Controller /** * @param string $command * @param array $args + * * @return bool */ private function executeCommand(string $command, array $args): bool @@ -215,15 +218,17 @@ class InstallController extends Controller Log::error($e->getTraceAsString()); if (strpos($e->getMessage(), 'open_basedir restriction in effect')) { $this->lastError = self::BASEDIR_ERROR; + return false; } $this->lastError = sprintf('%s %s', self::OTHER_ERROR, $e->getMessage()); + return false; } // clear cache as well. Cache::clear(); Preferences::mark(); - + return true; } } diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index b1551f5bea..dcb1c804b0 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -74,6 +74,8 @@ use Illuminate\Support\Collection; * @method static Builder|RecurrenceTransaction withTrashed() * @method static Builder|RecurrenceTransaction withoutTrashed() * @mixin Eloquent + * @property int|null $transaction_type_id + * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionTypeId($value) */ class RecurrenceTransaction extends Model { diff --git a/app/Models/WebhookAttempt.php b/app/Models/WebhookAttempt.php index 7002e7fb8f..2fc8420963 100644 --- a/app/Models/WebhookAttempt.php +++ b/app/Models/WebhookAttempt.php @@ -74,6 +74,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereWebhookMessageId($value) * @mixin \Eloquent + * @method static \Illuminate\Database\Query\Builder|WebhookAttempt onlyTrashed() + * @method static \Illuminate\Database\Query\Builder|WebhookAttempt withTrashed() + * @method static \Illuminate\Database\Query\Builder|WebhookAttempt withoutTrashed() */ class WebhookAttempt extends Model { diff --git a/composer.json b/composer.json index 11190509d1..362b8f0390 100644 --- a/composer.json +++ b/composer.json @@ -170,6 +170,7 @@ "@php artisan firefly-iii:rename-account-meta", "@php artisan firefly-iii:migrate-recurrence-meta", "@php artisan firefly-iii:migrate-tag-locations", + "@php artisan firefly-iii:migrate-recurrence-type", "@php artisan firefly-iii:fix-piggies", "@php artisan firefly-iii:create-link-types", diff --git a/composer.lock b/composer.lock index 3ea7a15e67..04fa765d0b 100644 --- a/composer.lock +++ b/composer.lock @@ -1356,16 +1356,16 @@ }, { "name": "guzzlehttp/promises", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "60d379c243457e073cff02bc323a2a86cb355631" + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", - "reference": "60d379c243457e073cff02bc323a2a86cb355631", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { @@ -1405,9 +1405,9 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.0" + "source": "https://github.com/guzzle/promises/tree/1.4.1" }, - "time": "2020-09-30T07:37:28+00:00" + "time": "2021-03-07T09:25:29+00:00" }, { "name": "guzzlehttp/psr7", @@ -1641,16 +1641,16 @@ }, { "name": "laravel/framework", - "version": "v8.31.0", + "version": "v8.32.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35" + "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/2aa5c2488d25178ebc097052c7897a0e463ddc35", - "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35", + "url": "https://api.github.com/repos/laravel/framework/zipball/7c37b64f8153c16b6406f5c28cf37828ebbe8846", + "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846", "shasum": "" }, "require": { @@ -1805,7 +1805,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-03-04T15:22:36+00:00" + "time": "2021-03-09T15:37:45+00:00" }, { "name": "laravel/passport", @@ -2792,16 +2792,16 @@ }, { "name": "nesbot/carbon", - "version": "2.45.1", + "version": "2.46.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d" + "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/528783b188bdb853eb21239b1722831e0f000a8d", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4", + "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4", "shasum": "" }, "require": { @@ -2881,7 +2881,7 @@ "type": "tidelift" } ], - "time": "2021-02-11T18:30:17+00:00" + "time": "2021-02-24T17:30:44+00:00" }, { "name": "nyholm/psr7", @@ -4287,20 +4287,20 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v6.2.6", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff" + "reference": "15f7faf8508e04471f666633addacf54c0ab5933" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d2791ff0b73247cdc2096b14f5580aba40c12bff", - "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/15f7faf8508e04471f666633addacf54c0ab5933", + "reference": "15f7faf8508e04471f666633addacf54c0ab5933", "shasum": "" }, "require": { - "egulias/email-validator": "^2.0", + "egulias/email-validator": "^2.0|^3.1", "php": ">=7.0.0", "symfony/polyfill-iconv": "^1.0", "symfony/polyfill-intl-idn": "^1.10", @@ -4346,7 +4346,7 @@ ], "support": { "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.6" + "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.7" }, "funding": [ { @@ -4358,20 +4358,20 @@ "type": "tidelift" } ], - "time": "2021-03-05T12:08:49+00:00" + "time": "2021-03-09T12:30:35+00:00" }, { "name": "symfony/console", - "version": "v5.2.4", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556" + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", - "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", + "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79", + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79", "shasum": "" }, "require": { @@ -4439,7 +4439,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.4" + "source": "https://github.com/symfony/console/tree/v5.2.5" }, "funding": [ { @@ -4455,7 +4455,7 @@ "type": "tidelift" } ], - "time": "2021-02-23T10:08:49+00:00" + "time": "2021-03-06T13:42:15+00:00" }, { "name": "symfony/css-selector", @@ -5037,16 +5037,16 @@ }, { "name": "symfony/http-kernel", - "version": "v5.2.4", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "c452dbe4f385f030c3957821bf921b13815d6140" + "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c452dbe4f385f030c3957821bf921b13815d6140", - "reference": "c452dbe4f385f030c3957821bf921b13815d6140", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b8c63ef63c2364e174c3b3e0ba0bf83455f97f73", + "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73", "shasum": "" }, "require": { @@ -5129,7 +5129,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.2.4" + "source": "https://github.com/symfony/http-kernel/tree/v5.2.5" }, "funding": [ { @@ -5145,20 +5145,20 @@ "type": "tidelift" } ], - "time": "2021-03-04T18:05:55+00:00" + "time": "2021-03-10T17:07:35+00:00" }, { "name": "symfony/mime", - "version": "v5.2.4", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd" + "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd", - "reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd", + "url": "https://api.github.com/repos/symfony/mime/zipball/554ba128f1955038b45db5e1fa7e93bfc683b139", + "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139", "shasum": "" }, "require": { @@ -5169,12 +5169,13 @@ "symfony/polyfill-php80": "^1.15" }, "conflict": { + "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<4.4" }, "require-dev": { - "egulias/email-validator": "^2.1.10", + "egulias/email-validator": "^2.1.10|^3.1", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/property-access": "^4.4|^5.1", @@ -5211,7 +5212,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.2.4" + "source": "https://github.com/symfony/mime/tree/v5.2.5" }, "funding": [ { @@ -5227,7 +5228,7 @@ "type": "tidelift" } ], - "time": "2021-02-15T18:55:04+00:00" + "time": "2021-03-07T16:08:20+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6362,16 +6363,16 @@ }, { "name": "symfony/translation", - "version": "v5.2.4", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "74b0353ab34ff4cca827a2cf909e325d96815e60" + "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/74b0353ab34ff4cca827a2cf909e325d96815e60", - "reference": "74b0353ab34ff4cca827a2cf909e325d96815e60", + "url": "https://api.github.com/repos/symfony/translation/zipball/0947ab1e3aabd22a6bef393874b2555d2bb976da", + "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da", "shasum": "" }, "require": { @@ -6435,7 +6436,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.2.4" + "source": "https://github.com/symfony/translation/tree/v5.2.5" }, "funding": [ { @@ -6451,7 +6452,7 @@ "type": "tidelift" } ], - "time": "2021-03-04T15:41:09+00:00" + "time": "2021-03-06T07:59:01+00:00" }, { "name": "symfony/translation-contracts", @@ -6533,16 +6534,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.2.4", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "6a81fec0628c468cf6d5c87a4d003725e040e223" + "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6a81fec0628c468cf6d5c87a4d003725e040e223", - "reference": "6a81fec0628c468cf6d5c87a4d003725e040e223", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/002ab5a36702adf0c9a11e6d8836623253e9045e", + "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e", "shasum": "" }, "require": { @@ -6601,7 +6602,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.2.4" + "source": "https://github.com/symfony/var-dumper/tree/v5.2.5" }, "funding": [ { @@ -6617,7 +6618,7 @@ "type": "tidelift" } ], - "time": "2021-02-18T23:11:19+00:00" + "time": "2021-03-06T07:59:01+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -6674,16 +6675,16 @@ }, { "name": "twig/twig", - "version": "v2.14.3", + "version": "v2.14.4", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "8bc568d460d88b25c00c046256ec14a787ea60d9" + "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/8bc568d460d88b25c00c046256ec14a787ea60d9", - "reference": "8bc568d460d88b25c00c046256ec14a787ea60d9", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/0b4ba691fb99ec7952d25deb36c0a83061b93bbf", + "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf", "shasum": "" }, "require": { @@ -6737,7 +6738,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v2.14.3" + "source": "https://github.com/twigphp/Twig/tree/v2.14.4" }, "funding": [ { @@ -6749,7 +6750,7 @@ "type": "tidelift" } ], - "time": "2021-01-05T15:34:33+00:00" + "time": "2021-03-10T10:05:55+00:00" }, { "name": "vlucas/phpdotenv", @@ -9105,16 +9106,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.80", + "version": "0.12.81", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "c6a1b17f22ecf708d434d6bee05092647ec7e686" + "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6a1b17f22ecf708d434d6bee05092647ec7e686", - "reference": "c6a1b17f22ecf708d434d6bee05092647ec7e686", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", + "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", "shasum": "" }, "require": { @@ -9145,7 +9146,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.80" + "source": "https://github.com/phpstan/phpstan/tree/0.12.81" }, "funding": [ { @@ -9161,7 +9162,7 @@ "type": "tidelift" } ], - "time": "2021-02-28T20:22:43+00:00" + "time": "2021-03-08T22:03:02+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -9691,12 +9692,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "0745f820eed6cb92603ca44a9c137ff8ce315e86" + "reference": "672ed7cb0191a12cf8b12b752c9ef74bb5d21cec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0745f820eed6cb92603ca44a9c137ff8ce315e86", - "reference": "0745f820eed6cb92603ca44a9c137ff8ce315e86", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/672ed7cb0191a12cf8b12b752c9ef74bb5d21cec", + "reference": "672ed7cb0191a12cf8b12b752c9ef74bb5d21cec", "shasum": "" }, "conflict": { @@ -9756,8 +9757,9 @@ "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", "ezsystems/ezplatform-kernel": ">=1,<1.0.2.1", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<=1.3.1", "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.14.2|>=6,<6.7.9.1|>=6.8,<6.13.6.3|>=7,<7.2.4.1|>=7.3,<7.3.2.1|>=7.5,<7.5.7.1", + "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.14.2|>=6,<6.7.9.1|>=6.8,<=6.13.8|>=7,<7.2.4.1|>=7.3,<7.3.2.1|>=7.5,<=7.5.15", "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.14.2|>=2011,<2017.12.7.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3|>=2019.3,<2019.3.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1", @@ -9787,6 +9789,7 @@ "illuminate/view": ">=7,<7.1.2", "ivankristianto/phpwhois": "<=4.3", "james-heinrich/getid3": "<1.9.9", + "joomla/archive": "<1.1.10", "joomla/session": "<1.3.1", "jsmitty12/phpwhois": "<5.1", "kazist/phpwhois": "<=4.2.6", @@ -9812,7 +9815,7 @@ "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nystudio107/craft-seomatic": "<3.3", "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", - "october/backend": ">=1.0.319,<1.0.470", + "october/backend": "<1.1.2", "october/cms": "= 1.0.469|>=1.0.319,<1.0.469", "october/october": ">=1.0.319,<1.0.466", "october/rain": "<1.0.472|>=1.1,<1.1.2", @@ -10011,7 +10014,7 @@ "type": "tidelift" } ], - "time": "2021-03-03T23:02:20+00:00" + "time": "2021-03-11T18:09:51+00:00" }, { "name": "sebastian/cli-parser",