. */ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Support\Collection; /** * Class CorrectOpeningBalanceCurrencies */ class CorrectOpeningBalanceCurrencies extends Command { protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.'; protected $signature = 'firefly-iii:fix-ob-currencies'; /** * Execute the console command. * * @return int */ public function handle(): int { $journals = $this->getJournals(); $count = 0; /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $count += $this->correctJournal($journal); } if ($count > 0) { $message = sprintf('Corrected %d opening balance transaction(s).', $count); $this->line($message); } if (0 === $count) { $message = 'Correct: There was nothing to fix in the opening balance transactions.'; $this->info($message); } return 0; } /** * @param TransactionJournal $journal * * @return int */ private function correctJournal(TransactionJournal $journal): int { // get the asset account for this opening balance: $account = $this->getAccount($journal); if (null === $account) { $message = sprintf('Transaction journal #%d has no valid account. Can\'t fix this line.', $journal->id); app('log')->warning($message); $this->warn($message); return 0; } // update journal and all transactions: return $this->setCorrectCurrency($account, $journal); } /** * @param TransactionJournal $journal * * @return Account|null */ private function getAccount(TransactionJournal $journal): ?Account { $transactions = $journal->transactions()->get(); /** @var Transaction $transaction */ foreach ($transactions as $transaction) { /** @var Account $account */ $account = $transaction->account()->first(); if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) { return $account; } } return null; } /** * @param Account $account * * @return TransactionCurrency */ private function getCurrency(Account $account): TransactionCurrency { /** @var AccountRepositoryInterface $repos */ $repos = app(AccountRepositoryInterface::class); $repos->setUser($account->user); return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user); } /** * @return Collection */ private function getJournals(): Collection { /** @var Collection */ return TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->whereNull('transaction_journals.deleted_at') ->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']); } /** * @param Account $account * @param TransactionJournal $journal * @return int */ private function setCorrectCurrency(Account $account, TransactionJournal $journal): int { $currency = $this->getCurrency($account); $count = 0; if ((int)$journal->transaction_currency_id !== (int)$currency->id) { $journal->transaction_currency_id = $currency->id; $journal->save(); $count = 1; } /** @var Transaction $transaction */ foreach ($journal->transactions as $transaction) { if ((int)$transaction->transaction_currency_id !== (int)$currency->id) { $transaction->transaction_currency_id = $currency->id; $transaction->save(); $count = 1; } } return $count; } }