mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-18 10:16:49 +00:00
Refactor upgrade and verify commands.
This commit is contained in:
@@ -26,9 +26,9 @@ use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Log;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Class AccountCurrencies
|
||||
@@ -49,6 +49,9 @@ class AccountCurrencies extends Command
|
||||
*/
|
||||
protected $signature = 'firefly-iii:account-currencies {--F|force : Force the execution of this command.}';
|
||||
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account.
|
||||
*
|
||||
@@ -56,78 +59,21 @@ class AccountCurrencies extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Log::debug('Now in updateAccountCurrencies()');
|
||||
$this->updateAccountCurrencies();
|
||||
|
||||
$defaultConfig = (string)config('firefly.default_currency', 'EUR');
|
||||
Log::debug(sprintf('System default currency is "%s"', $defaultConfig));
|
||||
|
||||
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$accounts->each(
|
||||
function (Account $account) use ($repository, $defaultConfig) {
|
||||
$repository->setUser($account->user);
|
||||
// get users preference, fall back to system pref.
|
||||
|
||||
// expand and debug routine.
|
||||
$defaultCurrencyCode = app('preferences')->getForUser($account->user, 'currencyPreference', $defaultConfig)->data;
|
||||
Log::debug(sprintf('Default currency code is "%s"', var_export($defaultCurrencyCode, true)));
|
||||
if (!is_string($defaultCurrencyCode)) {
|
||||
$defaultCurrencyCode = $defaultConfig;
|
||||
Log::debug(sprintf('Default currency code is not a string, now set to "%s"', $defaultCurrencyCode));
|
||||
}
|
||||
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
||||
$accountCurrency = (int)$repository->getMetaValue($account, 'currency_id');
|
||||
$openingBalance = $account->getOpeningBalance();
|
||||
$obCurrency = (int)$openingBalance->transaction_currency_id;
|
||||
|
||||
if (null === $defaultCurrency) {
|
||||
throw new UnexpectedValueException(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode));
|
||||
}
|
||||
Log::debug(
|
||||
sprintf('Found default currency #%d (%s) while searching for "%s"', $defaultCurrency->id, $defaultCurrency->code, $defaultCurrencyCode)
|
||||
);
|
||||
|
||||
// both 0? set to default currency:
|
||||
if (0 === $accountCurrency && 0 === $obCurrency) {
|
||||
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// account is set to 0, opening balance is not?
|
||||
if (0 === $accountCurrency && $obCurrency > 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// do not match and opening balance id is not null.
|
||||
if ($accountCurrency !== $obCurrency && $openingBalance->id > 0) {
|
||||
// update opening balance:
|
||||
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||
$openingBalance->save();
|
||||
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified and fixed account currencies in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -152,4 +98,87 @@ class AccountCurrencies extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionCurrency $currency
|
||||
*/
|
||||
private function updateAccount(Account $account, TransactionCurrency $currency): void
|
||||
{
|
||||
$this->repository->setUser($account->user);
|
||||
|
||||
$accountCurrency = (int)$this->repository->getMetaValue($account, 'currency_id');
|
||||
$openingBalance = $account->getOpeningBalance();
|
||||
$obCurrency = (int)$openingBalance->transaction_currency_id;
|
||||
|
||||
|
||||
// both 0? set to default currency:
|
||||
if (0 === $accountCurrency && 0 === $obCurrency) {
|
||||
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $currency->id]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// account is set to 0, opening balance is not?
|
||||
if (0 === $accountCurrency && $obCurrency > 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// do not match and opening balance id is not null.
|
||||
if ($accountCurrency !== $obCurrency && $openingBalance->id > 0) {
|
||||
// update opening balance:
|
||||
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||
$openingBalance->save();
|
||||
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function updateAccountCurrencies(): void
|
||||
{
|
||||
|
||||
$defaultCurrencyCode = (string)config('firefly.default_currency', 'EUR');
|
||||
$users = User::get();
|
||||
foreach ($users as $user) {
|
||||
$this->updateCurrenciesForUser($user, $defaultCurrencyCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param string $systemCurrencyCode
|
||||
*/
|
||||
private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void
|
||||
{
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])
|
||||
->get(['accounts.*']);
|
||||
|
||||
// get user's currency preference:
|
||||
$defaultCurrencyCode = app('preferences')->getForUser($user, 'currencyPreference', $systemCurrencyCode)->data;
|
||||
if (!is_string($defaultCurrencyCode)) {
|
||||
$defaultCurrencyCode = $systemCurrencyCode;
|
||||
}
|
||||
/** @var TransactionCurrency $defaultCurrency */
|
||||
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
||||
|
||||
if (null === $defaultCurrency) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@@ -53,6 +54,7 @@ class BackToJournals extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
if (!$this->isMigrated()) {
|
||||
$this->error('Please run firefly-iii:migrate-to-groups first.');
|
||||
}
|
||||
@@ -66,13 +68,35 @@ class BackToJournals extends Command
|
||||
}
|
||||
|
||||
$this->migrateAll();
|
||||
|
||||
$this->info('Updated category and budget info for all journals.');
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Updated category and budget info for all transaction journals in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForBudgets(): array
|
||||
{
|
||||
$transactions = DB::table('budget_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray();
|
||||
|
||||
return DB::table('transactions')->whereIn('transactions.id', $transactions)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForCategories(): array
|
||||
{
|
||||
$transactions = DB::table('category_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray();
|
||||
|
||||
return DB::table('transactions')->whereIn('transactions.id', $transactions)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -112,10 +136,24 @@ class BackToJournals extends Command
|
||||
*/
|
||||
private function migrateAll(): void
|
||||
{
|
||||
$journals = TransactionJournal::get();
|
||||
$this->migrateBudgets();
|
||||
$this->migrateCategories();
|
||||
|
||||
// empty tables
|
||||
DB::table('budget_transaction')->delete();
|
||||
DB::table('categories_transaction')->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function migrateBudgets(): void
|
||||
{
|
||||
$journalIds = $this->getIdsForBudgets();
|
||||
$journals = TransactionJournal::whereIn('id', $journalIds)->with(['transactions', 'budgets', 'transactions.budgets'])->get();
|
||||
$this->line(sprintf('Check %d transaction journals for budget info.', $journals->count()));
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->migrateCategoriesForJournal($journal);
|
||||
$this->migrateBudgetsForJournal($journal);
|
||||
}
|
||||
}
|
||||
@@ -127,19 +165,38 @@ class BackToJournals extends Command
|
||||
{
|
||||
// grab category from first transaction
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->first();
|
||||
$transaction = $journal->transactions->first();
|
||||
if (null === $transaction) {
|
||||
$this->info(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
/** @var Budget $budget */
|
||||
$budget = $transaction->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
$budget = $transaction->budgets->first();
|
||||
/** @var Budget $journalBudget */
|
||||
$journalBudget = $journal->budgets->first();
|
||||
if (null !== $budget && null !== $journalBudget && $budget->id !== $journalBudget->id) {
|
||||
// sync to journal:
|
||||
$journal->budgets()->sync([(int)$budget->id]);
|
||||
}
|
||||
|
||||
// remove from transactions:
|
||||
$journal->transactions()->each(
|
||||
function (Transaction $transaction) {
|
||||
$transaction->budgets()->sync([]);
|
||||
}
|
||||
);
|
||||
// budget in transaction overrules journal.
|
||||
if (null === $budget && null !== $journalBudget) {
|
||||
$journal->budgets()->sync([]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function migrateCategories(): void
|
||||
{
|
||||
$journalIds = $this->getIdsForCategories();
|
||||
$journals = TransactionJournal::whereIn('id', $journalIds)->with(['transactions', 'categories', 'transactions.categories'])->get();
|
||||
$this->line(sprintf('Check %d transaction journals for category info.', $journals->count()));
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->migrateCategoriesForJournal($journal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,19 +207,24 @@ class BackToJournals extends Command
|
||||
{
|
||||
// grab category from first transaction
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->first();
|
||||
$transaction = $journal->transactions->first();
|
||||
if (null === $transaction) {
|
||||
$this->info(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
/** @var Category $category */
|
||||
$category = $transaction->categories()->first();
|
||||
if (null !== $category) {
|
||||
$category = $transaction->categories->first();
|
||||
/** @var Category $journalCategory */
|
||||
$journalCategory = $journal->categories->first();
|
||||
if (null !== $category && null !== $journalCategory && $category->id !== $journalCategory->id) {
|
||||
// sync to journal:
|
||||
$journal->categories()->sync([(int)$category->id]);
|
||||
}
|
||||
|
||||
// remove from transactions:
|
||||
$journal->transactions()->each(
|
||||
function (Transaction $transaction) {
|
||||
$transaction->categories()->sync([]);
|
||||
}
|
||||
);
|
||||
// category in transaction overrules journal.
|
||||
if (null === $category && null !== $journalCategory) {
|
||||
$journal->categories()->sync([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -54,12 +54,13 @@ class BudgetLimitCurrency extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$budgetLimits = BudgetLimit::get();
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
foreach ($budgetLimits as $budgetLimit) {
|
||||
@@ -75,10 +76,16 @@ class BudgetLimitCurrency extends Command
|
||||
$this->line(
|
||||
sprintf('Budget limit #%d (part of budget "%s") now has a currency setting (%s).', $budgetLimit->id, $budget->name, $currency->name)
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('All budget limits are correct.');
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified budget limits in %s seconds.', $end));
|
||||
|
||||
$this->markAsExecuted();
|
||||
|
||||
|
@@ -57,6 +57,7 @@ class CCLiabilities extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
@@ -77,6 +78,11 @@ class CCLiabilities extends Command
|
||||
if ($accounts->count() > 0) {
|
||||
$this->info('Credit card liability types are no longer supported and have been converted to generic debts. See: http://bit.ly/FF3-credit-cards');
|
||||
}
|
||||
if (0 === $accounts->count()) {
|
||||
$this->info('No incorrectly stored credit card liabilities.');
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified credit card liabilities in %s seconds', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
|
@@ -26,13 +26,13 @@ namespace FireflyIII\Console\Commands\Upgrade;
|
||||
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 FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -54,6 +54,14 @@ class JournalCurrencies extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:journal-currencies {--F|force : Force the execution of this command.}';
|
||||
/** @var array */
|
||||
private $accountCurrencies;
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepos;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
@@ -62,6 +70,12 @@ class JournalCurrencies extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$this->accountCurrencies = [];
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
@@ -69,106 +83,76 @@ class JournalCurrencies extends Command
|
||||
}
|
||||
|
||||
$this->updateTransferCurrencies();
|
||||
$this->updateOtherCurrencies();
|
||||
$this->updateOtherJournalsCurrencies();
|
||||
$this->markAsExecuted();
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified and fixed transaction currencies in %s seconds.', $end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for
|
||||
* the accounts they are linked to.
|
||||
* @param Account $account
|
||||
*
|
||||
* Both source and destination must match the respective currency preference of the related asset account.
|
||||
* So FF3 must verify all transactions.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
public function updateOtherCurrencies(): void
|
||||
private function getCurrency(Account $account): ?TransactionCurrency
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
/** @var AccountRepositoryInterface $accountRepos */
|
||||
$accountRepos = app(AccountRepositoryInterface::class);
|
||||
$set = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->whereIn('transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE])
|
||||
->get(['transaction_journals.*']);
|
||||
$accountId = $account->id;
|
||||
if (isset($this->accountCurrencies[$accountId]) && 0 === $this->accountCurrencies[$accountId]) {
|
||||
return null;
|
||||
}
|
||||
if (isset($this->accountCurrencies[$accountId]) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) {
|
||||
return $this->accountCurrencies[$accountId];
|
||||
}
|
||||
$currencyId = (int)$this->accountRepos->getMetaValue($account, 'currency_id');
|
||||
$result = $this->currencyRepos->findNull($currencyId);
|
||||
if (null === $result) {
|
||||
$this->accountCurrencies[$accountId] = 0;
|
||||
|
||||
$set->each(
|
||||
function (TransactionJournal $journal) use ($repository, $accountRepos) {
|
||||
// get the transaction with the asset account in it:
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->first(['transactions.*']);
|
||||
if (null === $transaction) {
|
||||
return;
|
||||
}
|
||||
$accountRepos->setUser($journal->user);
|
||||
/** @var Account $account */
|
||||
$account = $transaction->account;
|
||||
$currency = $repository->findNull((int)$accountRepos->getMetaValue($account, 'currency_id'));
|
||||
if (null === $currency) {
|
||||
return;
|
||||
}
|
||||
$transactions = $journal->transactions()->get();
|
||||
$transactions->each(
|
||||
function (Transaction $transaction) use ($currency) {
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
$this->accountCurrencies[$accountId] = $result;
|
||||
|
||||
return $result;
|
||||
|
||||
// when mismatch in transaction:
|
||||
if (!((int)$transaction->transaction_currency_id === (int)$currency->id)) {
|
||||
$transaction->foreign_currency_id = (int)$transaction->transaction_currency_id;
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
// also update the journal, of course:
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$journal->save();
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* A transfer always has the
|
||||
*
|
||||
* Both source and destination must match the respective currency preference. So FF3 must verify ALL
|
||||
* transactions.
|
||||
* @return Transaction|null
|
||||
*/
|
||||
public function updateTransferCurrencies(): void
|
||||
private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction
|
||||
{
|
||||
$set = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->where('transaction_types.type', TransactionType::TRANSFER)
|
||||
->get(['transaction_journals.*']);
|
||||
return $transfer->transactions->firstWhere('amount', '>', 0);
|
||||
}
|
||||
|
||||
$set->each(
|
||||
function (TransactionJournal $transfer) {
|
||||
// select all "source" transactions:
|
||||
/** @var Collection $transactions */
|
||||
$transactions = $transfer->transactions()->where('amount', '<', 0)->get();
|
||||
$transactions->each(
|
||||
function (Transaction $transaction) {
|
||||
$this->updateTransactionCurrency($transaction);
|
||||
$this->updateJournalCurrency($transaction);
|
||||
}
|
||||
);
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getFirstAssetTransaction(TransactionJournal $journal): ?Transaction
|
||||
{
|
||||
$result = $journal->transactions->first(
|
||||
function (Transaction $transaction) {
|
||||
return AccountType::ASSET === $transaction->account->accountType->type;
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $transfer): ?Transaction
|
||||
{
|
||||
return $transfer->transactions->firstWhere('amount', '<', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,17 +179,12 @@ class JournalCurrencies extends Command
|
||||
/**
|
||||
* This method makes sure that the transaction journal uses the currency given in the transaction.
|
||||
*
|
||||
* @param Transaction $transaction
|
||||
* @param TransactionJournal $journal
|
||||
* @param Transaction $transaction
|
||||
*/
|
||||
private function updateJournalCurrency(Transaction $transaction): void
|
||||
private function updateJournalCurrency(TransactionJournal $journal, Transaction $transaction): void
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
/** @var AccountRepositoryInterface $accountRepos */
|
||||
$accountRepos = app(AccountRepositoryInterface::class);
|
||||
$accountRepos->setUser($transaction->account->user);
|
||||
$currency = $repository->findNull((int)$accountRepos->getMetaValue($transaction->account, 'currency_id'));
|
||||
$journal = $transaction->transactionJournal;
|
||||
$currency = $this->getCurrency($transaction->account);
|
||||
$currencyCode = $journal->transactionCurrency->code ?? '(nothing)';
|
||||
|
||||
if (null === $currency) {
|
||||
@@ -225,11 +204,71 @@ class JournalCurrencies extends Command
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method makes sure that the tranaction uses the same currency as the source account does.
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function updateOtherJournalCurrency(TransactionJournal $journal): void
|
||||
{
|
||||
$transaction = $this->getFirstAssetTransaction($journal);
|
||||
if (null === $transaction) {
|
||||
return;
|
||||
}
|
||||
/** @var Account $account */
|
||||
$account = $transaction->account;
|
||||
$currency = $this->getCurrency($account);
|
||||
if (null === $currency) {
|
||||
return;
|
||||
}
|
||||
|
||||
$journal->transactions->each(
|
||||
function (Transaction $transaction) use ($currency) {
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
|
||||
// when mismatch in transaction:
|
||||
if (!((int)$transaction->transaction_currency_id === (int)$currency->id)) {
|
||||
$transaction->foreign_currency_id = (int)$transaction->transaction_currency_id;
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
// also update the journal, of course:
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*/
|
||||
private function updateOtherJournalsCurrencies(): void
|
||||
{
|
||||
$set = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->whereNotIn('transaction_types.type', [TransactionType::TRANSFER])
|
||||
->with(['transactions', 'transactions.account', 'transactions.account.accountType'])
|
||||
->get(['transaction_journals.*']);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateOtherJournalCurrency($journal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method makes sure that the transaction uses the same currency as the source account does.
|
||||
* If not, the currency is updated to include a reference to its original currency as the "foreign" currency.
|
||||
*
|
||||
* The transaction that is sent to this function MUST be the source transaction (amount negative).
|
||||
@@ -242,116 +281,108 @@ class JournalCurrencies extends Command
|
||||
*
|
||||
* @param Transaction $transaction
|
||||
*/
|
||||
private function updateTransactionCurrency(Transaction $transaction): void
|
||||
private function updateTransactionCurrency(TransactionJournal $journal, Transaction $source, Transaction $destination): void
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
/** @var AccountRepositoryInterface $accountRepos */
|
||||
$accountRepos = app(AccountRepositoryInterface::class);
|
||||
/** @var JournalRepositoryInterface $journalRepos */
|
||||
$journalRepos = app(JournalRepositoryInterface::class);
|
||||
$user = $journal->user;
|
||||
$sourceAccount = $source->account;
|
||||
$destAccount = $destination->account;
|
||||
$this->accountRepos->setUser($user);
|
||||
$this->journalRepos->setUser($user);
|
||||
$this->currencyRepos->setUser($user);
|
||||
|
||||
$accountRepos->setUser($transaction->account->user);
|
||||
$journalRepos->setUser($transaction->account->user);
|
||||
$currency = $repository->findNull((int)$accountRepos->getMetaValue($transaction->account, 'currency_id'));
|
||||
|
||||
if (null === $currency) {
|
||||
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $transaction->account->id, $transaction->account->name));
|
||||
$sourceAccountCurrency = $this->getCurrency($sourceAccount);
|
||||
$destAccountCurrency = $this->getCurrency($destAccount);
|
||||
if (null === $sourceAccountCurrency) {
|
||||
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $sourceAccount->id, $sourceAccount->name));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// has no currency ID? Must have, so fill in using account preference:
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = (int)$currency->id;
|
||||
Log::debug(sprintf('Transaction #%d has no currency setting, now set to %s', $transaction->id, $currency->code));
|
||||
$transaction->save();
|
||||
if (null === $source->transaction_currency_id) {
|
||||
$source->transaction_currency_id = (int)$sourceAccountCurrency->id;
|
||||
Log::debug(sprintf('Transaction #%d has no currency setting, now set to %s', $source->id, $sourceAccountCurrency->code));
|
||||
$source->save();
|
||||
}
|
||||
|
||||
// does not match the source account (see above)? Can be fixed
|
||||
// when mismatch in transaction and NO foreign amount is set:
|
||||
if (!((int)$transaction->transaction_currency_id === (int)$currency->id) && null === $transaction->foreign_amount) {
|
||||
if (!((int)$source->transaction_currency_id === (int)$sourceAccountCurrency->id) && null === $source->foreign_amount) {
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$transaction->id,
|
||||
$transaction->transaction_currency_id,
|
||||
$currency->id,
|
||||
$transaction->amount
|
||||
$source->id,
|
||||
$source->transaction_currency_id,
|
||||
$sourceAccountCurrency->id,
|
||||
$source->amount
|
||||
)
|
||||
);
|
||||
$transaction->transaction_currency_id = (int)$currency->id;
|
||||
$transaction->save();
|
||||
$source->transaction_currency_id = (int)$sourceAccountCurrency->id;
|
||||
$source->save();
|
||||
}
|
||||
|
||||
// grab opposing transaction:
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $transaction->transactionJournal;
|
||||
/** @var Transaction $opposing */
|
||||
$opposing = $journal->transactions()->where('amount', '>', 0)->where('identifier', $transaction->identifier)->first();
|
||||
$opposingCurrency = $repository->findNull((int)$accountRepos->getMetaValue($opposing->account, 'currency_id'));
|
||||
|
||||
if (null === $opposingCurrency) {
|
||||
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $opposing->account->id, $opposing->account->name));
|
||||
if (null === $destAccountCurrency) {
|
||||
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $destAccount->id, $destAccount->name));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if the destination account currency is the same, both foreign_amount and foreign_currency_id must be NULL for both transactions:
|
||||
if ((int)$opposingCurrency->id === (int)$currency->id) {
|
||||
if ((int)$destAccountCurrency->id === (int)$sourceAccountCurrency->id) {
|
||||
// update both transactions to match:
|
||||
$transaction->foreign_amount = null;
|
||||
$transaction->foreign_currency_id = null;
|
||||
$opposing->foreign_amount = null;
|
||||
$opposing->foreign_currency_id = null;
|
||||
$opposing->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
$opposing->save();
|
||||
$source->foreign_amount = null;
|
||||
$source->foreign_currency_id = null;
|
||||
$destination->foreign_amount = null;
|
||||
$destination->foreign_currency_id = null;
|
||||
$source->save();
|
||||
$destination->save();
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Currency for account "%s" is %s, and currency for account "%s" is also
|
||||
%s, so %s #%d (#%d and #%d) has been verified to be to %s exclusively.',
|
||||
$opposing->account->name, $opposingCurrency->code,
|
||||
$transaction->account->name, $transaction->transactionCurrency->code,
|
||||
$destAccount->name, $destAccountCurrency->code,
|
||||
$sourceAccount->name, $sourceAccountCurrency->code,
|
||||
$journal->transactionType->type, $journal->id,
|
||||
$transaction->id, $opposing->id, $currency->code
|
||||
$source->id, $destination->id, $sourceAccountCurrency->code
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if destination account currency is different, both transactions must have this currency as foreign currency id.
|
||||
if (!((int)$opposingCurrency->id === (int)$currency->id)) {
|
||||
$transaction->foreign_currency_id = $opposingCurrency->id;
|
||||
$opposing->foreign_currency_id = $opposingCurrency->id;
|
||||
$transaction->save();
|
||||
$opposing->save();
|
||||
Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $transaction->id, $opposing->id));
|
||||
if (!((int)$destAccountCurrency->id === (int)$sourceAccountCurrency->id)) {
|
||||
$source->foreign_currency_id = $destAccountCurrency->id;
|
||||
$destination->foreign_currency_id = $destAccountCurrency->id;
|
||||
$source->save();
|
||||
$destination->save();
|
||||
Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $source->id, $destination->id));
|
||||
}
|
||||
|
||||
// if foreign amount of one is null and the other is not, use this to restore:
|
||||
if (null === $transaction->foreign_amount && null !== $opposing->foreign_amount) {
|
||||
$transaction->foreign_amount = bcmul((string)$opposing->foreign_amount, '-1');
|
||||
$transaction->save();
|
||||
Log::debug(sprintf('Restored foreign amount of transaction (1) #%d to %s', $transaction->id, $transaction->foreign_amount));
|
||||
if (null === $source->foreign_amount && null !== $destination->foreign_amount) {
|
||||
$source->foreign_amount = bcmul((string)$destination->foreign_amount, '-1');
|
||||
$source->save();
|
||||
Log::debug(sprintf('Restored foreign amount of source transaction (1) #%d to %s', $source->id, $source->foreign_amount));
|
||||
}
|
||||
|
||||
// if foreign amount of one is null and the other is not, use this to restore (other way around)
|
||||
if (null === $opposing->foreign_amount && null !== $transaction->foreign_amount) {
|
||||
$opposing->foreign_amount = bcmul((string)$transaction->foreign_amount, '-1');
|
||||
$opposing->save();
|
||||
Log::debug(sprintf('Restored foreign amount of transaction (2) #%d to %s', $opposing->id, $opposing->foreign_amount));
|
||||
if (null === $destination->foreign_amount && null !== $destination->foreign_amount) {
|
||||
$destination->foreign_amount = bcmul((string)$destination->foreign_amount, '-1');
|
||||
$destination->save();
|
||||
Log::debug(sprintf('Restored foreign amount of destination transaction (2) #%d to %s', $destination->id, $destination->foreign_amount));
|
||||
}
|
||||
|
||||
// when both are zero, try to grab it from journal:
|
||||
if (null === $opposing->foreign_amount && null === $transaction->foreign_amount) {
|
||||
$foreignAmount = $journalRepos->getMetaField($journal, 'foreign_amount');
|
||||
if (null === $source->foreign_amount && null === $destination->foreign_amount) {
|
||||
$foreignAmount = $this->journalRepos->getMetaField($journal, 'foreign_amount');
|
||||
if (null === $foreignAmount) {
|
||||
Log::debug(sprintf('Journal #%d has missing foreign currency data, forced to do 1:1 conversion :(.', $transaction->transaction_journal_id));
|
||||
$transaction->foreign_amount = bcmul((string)$transaction->amount, '-1');
|
||||
$opposing->foreign_amount = bcmul((string)$opposing->amount, '-1');
|
||||
$transaction->save();
|
||||
$opposing->save();
|
||||
Log::debug(sprintf('Journal #%d has missing foreign currency data, forced to do 1:1 conversion :(.', $source->transaction_journal_id));
|
||||
$source->foreign_amount = $source->amount;
|
||||
$destination->foreign_amount = $destination->amount;
|
||||
$source->save();
|
||||
$destination->save();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -359,15 +390,61 @@ class JournalCurrencies extends Command
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Journal #%d has missing foreign currency info, try to restore from meta-data ("%s").',
|
||||
$transaction->transaction_journal_id,
|
||||
$source->transaction_journal_id,
|
||||
$foreignAmount
|
||||
)
|
||||
);
|
||||
$transaction->foreign_amount = bcmul($foreignPositive, '-1');
|
||||
$opposing->foreign_amount = $foreignPositive;
|
||||
$transaction->save();
|
||||
$opposing->save();
|
||||
$source->foreign_amount = bcmul($foreignPositive, '-1');
|
||||
$destination->foreign_amount = $foreignPositive;
|
||||
$source->save();
|
||||
$destination->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* A transfer always has the
|
||||
*
|
||||
* Both source and destination must match the respective currency preference. So FF3 must verify ALL
|
||||
* transactions.
|
||||
*/
|
||||
private function updateTransferCurrencies(): void
|
||||
{
|
||||
$set = TransactionJournal
|
||||
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->where('transaction_types.type', TransactionType::TRANSFER)
|
||||
->with(['user', 'transactionType', 'transactionCurrency', 'transactions', 'transactions.account'])
|
||||
->get(['transaction_journals.*']);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateTransferCurrency($journal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*/
|
||||
private function updateTransferCurrency(TransactionJournal $transfer): void
|
||||
{
|
||||
$sourceTransaction = $this->getSourceTransaction($transfer);
|
||||
$destTransaction = $this->getDestinationTransaction($transfer);
|
||||
|
||||
if (null === $sourceTransaction) {
|
||||
$this->info(sprintf('Source transaction for journal #%d is null.', $transfer->id));
|
||||
|
||||
return;
|
||||
}
|
||||
if (null === $destTransaction) {
|
||||
$this->info(sprintf('Destination transaction for journal #%d is null.', $transfer->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->updateTransactionCurrency($transfer, $sourceTransaction, $destTransaction);
|
||||
$this->updateJournalCurrency($transfer, $sourceTransaction);
|
||||
}
|
||||
}
|
@@ -55,6 +55,7 @@ class MigrateAttachments extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
@@ -84,7 +85,8 @@ class MigrateAttachments extends Command
|
||||
Log::debug(sprintf('Migrated attachment #%s description to note #%d', $att->id, $note->id));
|
||||
}
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Migrated attachment notes in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
|
@@ -56,6 +56,7 @@ class MigrateNotes extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
@@ -82,7 +83,8 @@ class MigrateNotes extends Command
|
||||
Log::error(sprintf('Could not delete old meta entry #%d: %s', $meta->id, $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Migrated notes in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
|
@@ -21,14 +21,15 @@
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use DB;
|
||||
use Exception;
|
||||
use FireflyIII\Factory\TransactionJournalFactory;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -84,6 +85,7 @@ class MigrateToGroups extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isMigrated() && true !== $this->option('force')) {
|
||||
$this->info('Database already seems to be migrated.');
|
||||
|
||||
@@ -95,9 +97,14 @@ class MigrateToGroups extends Command
|
||||
|
||||
Log::debug('---- start group migration ----');
|
||||
$this->makeGroupsFromSplitJournals();
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Migrate split journals to groups in %s seconds.', $end));
|
||||
|
||||
$start = microtime(true);
|
||||
$this->makeGroupsFromAll();
|
||||
Log::debug('---- end group migration ----');
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Migrate all journals to groups in %s seconds.', $end));
|
||||
$this->markAsMigrated();
|
||||
|
||||
return 0;
|
||||
@@ -105,15 +112,42 @@ class MigrateToGroups extends Command
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Transaction $transaction
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function giveGroup(TransactionJournal $journal): void
|
||||
private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction
|
||||
{
|
||||
$group = new TransactionGroup;
|
||||
$group->title = null;
|
||||
$group->user_id = $journal->user_id;
|
||||
$group->save();
|
||||
$journal->transaction_group_id = $group->id;
|
||||
$journal->save();
|
||||
$set = $journal->transactions->filter(
|
||||
function (Transaction $subject) use ($transaction) {
|
||||
return $transaction->amount * -1 === (float)$subject->amount && $transaction->identifier === $subject->identifier;
|
||||
}
|
||||
);
|
||||
|
||||
return $set->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getDestinationTransactions(TransactionJournal $journal): Collection
|
||||
{
|
||||
return $journal->transactions->filter(
|
||||
function (Transaction $transaction) {
|
||||
return $transaction->amount > 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*/
|
||||
private function giveGroup(array $array): void
|
||||
{
|
||||
$groupId = DB::table('transaction_groups')->insertGetId(['title' => null, 'user_id' => $array['user_id']]);
|
||||
DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,26 +169,26 @@ class MigrateToGroups extends Command
|
||||
private function makeGroupsFromAll(): void
|
||||
{
|
||||
$orphanedJournals = $this->journalRepository->getJournalsWithoutGroup();
|
||||
if ($orphanedJournals->count() > 0) {
|
||||
Log::debug(sprintf('Going to convert %d transactions. Please hold..', $orphanedJournals->count()));
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($orphanedJournals as $journal) {
|
||||
$this->giveGroup($journal);
|
||||
$count = count($orphanedJournals);
|
||||
if ($count > 0) {
|
||||
Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $count));
|
||||
$this->line(sprintf('Going to convert %d transaction journals. Please hold..', $count));
|
||||
/** @var array $journal */
|
||||
foreach ($orphanedJournals as $array) {
|
||||
$this->giveGroup($array);
|
||||
}
|
||||
}
|
||||
if (0 === $orphanedJournals->count()) {
|
||||
$this->info('No need to convert transactions.');
|
||||
if (0 === $count) {
|
||||
$this->info('No need to convert transaction journals.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function makeGroupsFromSplitJournals(): void
|
||||
{
|
||||
$splitJournals = $this->journalRepository->getSplitJournals();
|
||||
|
||||
if ($splitJournals->count() > 0) {
|
||||
$this->info(sprintf('Going to convert %d split transaction(s). Please hold..', $splitJournals->count()));
|
||||
/** @var TransactionJournal $journal */
|
||||
@@ -163,7 +197,7 @@ class MigrateToGroups extends Command
|
||||
}
|
||||
}
|
||||
if (0 === $splitJournals->count()) {
|
||||
$this->info('Found no split transactions. Nothing to do.');
|
||||
$this->info('Found no split transaction journals. Nothing to do.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +219,7 @@ class MigrateToGroups extends Command
|
||||
$this->journalRepository->setUser($journal->user);
|
||||
$this->journalFactory->setUser($journal->user);
|
||||
|
||||
$data = [
|
||||
$data = [
|
||||
// mandatory fields.
|
||||
'type' => strtolower($journal->transactionType->type),
|
||||
'date' => $journal->date,
|
||||
@@ -193,16 +227,40 @@ class MigrateToGroups extends Command
|
||||
'group_title' => $journal->description,
|
||||
'transactions' => [],
|
||||
];
|
||||
$destTransactions = $this->getDestinationTransactions($journal);
|
||||
$budgetId = $this->journalRepository->getJournalBudgetId($journal);
|
||||
$categoryId = $this->journalRepository->getJournalCategoryId($journal);
|
||||
$notes = $this->journalRepository->getNoteText($journal);
|
||||
$tags = $this->journalRepository->getTags($journal);
|
||||
$internalRef = $this->journalRepository->getMetaField($journal, 'internal-reference');
|
||||
$sepaCC = $this->journalRepository->getMetaField($journal, 'sepa-cc');
|
||||
$sepaCtOp = $this->journalRepository->getMetaField($journal, 'sepa-ct-op');
|
||||
$sepaCtId = $this->journalRepository->getMetaField($journal, 'sepa-ct-id');
|
||||
$sepaDb = $this->journalRepository->getMetaField($journal, 'sepa-db');
|
||||
$sepaCountry = $this->journalRepository->getMetaField($journal, 'sepa-country');
|
||||
$sepaEp = $this->journalRepository->getMetaField($journal, 'sepa-ep');
|
||||
$sepaCi = $this->journalRepository->getMetaField($journal, 'sepa-ci');
|
||||
$sepaBatchId = $this->journalRepository->getMetaField($journal, 'sepa-batch-id');
|
||||
$externalId = $this->journalRepository->getMetaField($journal, 'external-id');
|
||||
$originalSource = $this->journalRepository->getMetaField($journal, 'original-source');
|
||||
$recurrenceId = $this->journalRepository->getMetaField($journal, 'recurrence_id');
|
||||
$bunq = $this->journalRepository->getMetaField($journal, 'bunq_payment_id');
|
||||
$hash = $this->journalRepository->getMetaField($journal, 'importHash');
|
||||
$hashTwo = $this->journalRepository->getMetaField($journal, 'importHashV2');
|
||||
$interestDate = $this->journalRepository->getMetaDate($journal, 'interest_date');
|
||||
$bookDate = $this->journalRepository->getMetaDate($journal, 'book_date');
|
||||
$processDate = $this->journalRepository->getMetaDate($journal, 'process_date');
|
||||
$dueDate = $this->journalRepository->getMetaDate($journal, 'due_date');
|
||||
$paymentDate = $this->journalRepository->getMetaDate($journal, 'payment_date');
|
||||
$invoiceDate = $this->journalRepository->getMetaDate($journal, 'invoice_date');
|
||||
|
||||
$transactions = $journal->transactions()->where('amount', '>', 0)->get();
|
||||
Log::debug(sprintf('Will use %d positive transactions to create a new group.', $transactions->count()));
|
||||
|
||||
Log::debug(sprintf('Will use %d positive transactions to create a new group.', $destTransactions->count()));
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
foreach ($destTransactions as $transaction) {
|
||||
Log::debug(sprintf('Now going to add transaction #%d to the array.', $transaction->id));
|
||||
$budgetId = $this->journalRepository->getJournalBudgetId($journal);
|
||||
$categoryId = $this->journalRepository->getJournalCategoryId($journal);
|
||||
$opposingTr = $this->journalRepository->findOpposingTransaction($transaction);
|
||||
$opposingTr = $this->findOpposingTransaction($journal, $transaction);
|
||||
|
||||
if (null === $opposingTr) {
|
||||
$this->error(
|
||||
@@ -225,29 +283,29 @@ class MigrateToGroups extends Command
|
||||
'budget_id' => $budgetId,
|
||||
'category_id' => $categoryId,
|
||||
'bill_id' => $journal->bill_id,
|
||||
'notes' => $this->journalRepository->getNoteText($journal),
|
||||
'tags' => $this->journalRepository->getTags($journal),
|
||||
'internal_reference' => $this->journalRepository->getMetaField($journal, 'internal-reference'),
|
||||
'sepa-cc' => $this->journalRepository->getMetaField($journal, 'sepa-cc'),
|
||||
'sepa-ct-op' => $this->journalRepository->getMetaField($journal, 'sepa-ct-op'),
|
||||
'sepa-ct-id' => $this->journalRepository->getMetaField($journal, 'sepa-ct-id'),
|
||||
'sepa-db' => $this->journalRepository->getMetaField($journal, 'sepa-db'),
|
||||
'sepa-country' => $this->journalRepository->getMetaField($journal, 'sepa-country'),
|
||||
'sepa-ep' => $this->journalRepository->getMetaField($journal, 'sepa-ep'),
|
||||
'sepa-ci' => $this->journalRepository->getMetaField($journal, 'sepa-ci'),
|
||||
'sepa-batch-id' => $this->journalRepository->getMetaField($journal, 'sepa-batch-id'),
|
||||
'external_id' => $this->journalRepository->getMetaField($journal, 'external-id'),
|
||||
'original-source' => $this->journalRepository->getMetaField($journal, 'original-source'),
|
||||
'recurrence_id' => $this->journalRepository->getMetaField($journal, 'recurrence_id'),
|
||||
'bunq_payment_id' => $this->journalRepository->getMetaField($journal, 'bunq_payment_id'),
|
||||
'importHash' => $this->journalRepository->getMetaField($journal, 'importHash'),
|
||||
'importHashV2' => $this->journalRepository->getMetaField($journal, 'importHashV2'),
|
||||
'interest_date' => $this->journalRepository->getMetaDate($journal, 'interest_date'),
|
||||
'book_date' => $this->journalRepository->getMetaDate($journal, 'book_date'),
|
||||
'process_date' => $this->journalRepository->getMetaDate($journal, 'process_date'),
|
||||
'due_date' => $this->journalRepository->getMetaDate($journal, 'due_date'),
|
||||
'payment_date' => $this->journalRepository->getMetaDate($journal, 'payment_date'),
|
||||
'invoice_date' => $this->journalRepository->getMetaDate($journal, 'invoice_date'),
|
||||
'notes' => $notes,
|
||||
'tags' => $tags,
|
||||
'internal_reference' => $internalRef,
|
||||
'sepa-cc' => $sepaCC,
|
||||
'sepa-ct-op' => $sepaCtOp,
|
||||
'sepa-ct-id' => $sepaCtId,
|
||||
'sepa-db' => $sepaDb,
|
||||
'sepa-country' => $sepaCountry,
|
||||
'sepa-ep' => $sepaEp,
|
||||
'sepa-ci' => $sepaCi,
|
||||
'sepa-batch-id' => $sepaBatchId,
|
||||
'external_id' => $externalId,
|
||||
'original-source' => $originalSource,
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'bunq_payment_id' => $bunq,
|
||||
'importHash' => $hash,
|
||||
'importHashV2' => $hashTwo,
|
||||
'interest_date' => $interestDate,
|
||||
'book_date' => $bookDate,
|
||||
'process_date' => $processDate,
|
||||
'due_date' => $dueDate,
|
||||
'payment_date' => $paymentDate,
|
||||
'invoice_date' => $invoiceDate,
|
||||
];
|
||||
|
||||
$data['transactions'][] = $tArray;
|
||||
@@ -260,8 +318,8 @@ class MigrateToGroups extends Command
|
||||
$this->service->destroy($journal);
|
||||
|
||||
// report on result:
|
||||
Log::debug(sprintf('Migrated journal #%d into these journals: %s', $journal->id, implode(', ', $result->pluck('id')->toArray())));
|
||||
$this->line(sprintf('Migrated journal #%d into these journals: %s', $journal->id, implode(', ', $result->pluck('id')->toArray())));
|
||||
Log::debug(sprintf('Migrated journal #%d into these journals: #%s', $journal->id, implode(', #', $result->pluck('id')->toArray())));
|
||||
$this->line(sprintf('Migrated journal #%d into these journals: #%s', $journal->id, implode(', #', $result->pluck('id')->toArray())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -64,6 +64,8 @@ class MigrateToRules extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
@@ -111,11 +113,13 @@ class MigrateToRules extends Command
|
||||
|
||||
// loop bills.
|
||||
$order = 1;
|
||||
$count = 0;
|
||||
/** @var Collection $collection */
|
||||
$collection = $user->bills()->get();
|
||||
/** @var Bill $bill */
|
||||
foreach ($collection as $bill) {
|
||||
if ('MIGRATED_TO_RULES' !== $bill->match) {
|
||||
$count++;
|
||||
$rule = Rule::create(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
@@ -211,8 +215,15 @@ class MigrateToRules extends Command
|
||||
$bill->save();
|
||||
}
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->info(sprintf('Migrated %d bills for user %s', $count, $user->email));
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info(sprintf('Bills are correct for user %s.', $user->email));
|
||||
}
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified and fixed bills in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
|
@@ -61,6 +61,7 @@ class TransactionIdentifier extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->warn('This command has already been executed.');
|
||||
|
||||
@@ -86,7 +87,8 @@ class TransactionIdentifier extends Command
|
||||
foreach ($journalIds as $journalId) {
|
||||
$this->updateJournalidentifiers((int)$journalId);
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified and fixed transaction identifiers in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
|
@@ -21,6 +21,8 @@
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
use Artisan;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
|
Reference in New Issue
Block a user