. */ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; use FireflyIII\Console\Commands\ShowsFriendlyMessages; 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 { use ShowsFriendlyMessages; 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. */ 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->friendlyInfo($message); } if (0 === $count) { $message = 'There was nothing to fix in the opening balance transactions.'; $this->friendlyPositive($message); } return 0; } 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.*']) ; } 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->friendlyError($message); return 0; } // update journal and all transactions: return $this->setCorrectCurrency($account, $journal); } private function getAccount(TransactionJournal $journal): ?Account { $transactions = $journal->transactions()->get(); /** @var Transaction $transaction */ foreach ($transactions as $transaction) { /** @var null|Account $account */ $account = $transaction->account()->first(); if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) { return $account; } } return null; } private function setCorrectCurrency(Account $account, TransactionJournal $journal): int { $currency = $this->getCurrency($account); $count = 0; if ((int)$journal->transaction_currency_id !== $currency->id) { $journal->transaction_currency_id = $currency->id; $journal->save(); $count = 1; } /** @var Transaction $transaction */ foreach ($journal->transactions as $transaction) { if ($transaction->transaction_currency_id !== $currency->id) { $transaction->transaction_currency_id = $currency->id; $transaction->save(); $count = 1; } } return $count; } private function getCurrency(Account $account): TransactionCurrency { /** @var AccountRepositoryInterface $repos */ $repos = app(AccountRepositoryInterface::class); $repos->setUser($account->user); return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->userGroup); } }