Update installation commands.

This commit is contained in:
James Cole 2021-03-12 06:30:40 +01:00
parent 748d61fb8f
commit a05d006fa7
No known key found for this signature in database
GPG Key ID: B5669F9493CDE38D
40 changed files with 1624 additions and 1470 deletions

View File

@ -89,6 +89,7 @@ class CorrectOpeningBalanceCurrencies extends Command
} }
Log::debug(sprintf('Done with %s', __METHOD__)); Log::debug(sprintf('Done with %s', __METHOD__));
return 0; return 0;
} }
@ -133,6 +134,7 @@ class CorrectOpeningBalanceCurrencies extends Command
$account = $transaction->account()->first(); $account = $transaction->account()->first();
if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) { 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)); Log::debug(sprintf('Account of transaction #%d is opposite of IB account (%s).', $transaction->id, $account->accountType()->first()->type));
return $account; return $account;
} }
} }
@ -143,6 +145,7 @@ class CorrectOpeningBalanceCurrencies extends Command
/** /**
* @param Account $account * @param Account $account
*
* @return TransactionCurrency * @return TransactionCurrency
* @throws JsonException * @throws JsonException
*/ */
@ -156,15 +159,16 @@ class CorrectOpeningBalanceCurrencies extends Command
} }
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
*
* @return int * @return int
*/ */
private function setCurrency(TransactionJournal $journal, TransactionCurrency $currency): int private function setCurrency(TransactionJournal $journal, TransactionCurrency $currency): int
{ {
Log::debug('Now in setCurrency'); Log::debug('Now in setCurrency');
$count = 0; $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)); 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->transaction_currency_id = $currency->id;
$journal->save(); $journal->save();
@ -173,8 +177,10 @@ class CorrectOpeningBalanceCurrencies extends Command
/** @var Transaction $transaction */ /** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) { foreach ($journal->transactions as $transaction) {
if ((int) $transaction->transaction_currency_id !== (int) $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)); 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->transaction_currency_id = $currency->id;
$transaction->save(); $transaction->save();
$count = 1; $count = 1;

View File

@ -50,8 +50,8 @@ class CreateAccessTokens extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @throws Exception
* @return int * @return int
* @throws Exception
*/ */
public function handle(): int public function handle(): int
{ {

View File

@ -50,9 +50,9 @@ class DeleteEmptyGroups extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @return int
* @throws Exception; * @throws Exception;
* *
* @return int
*/ */
public function handle(): int public function handle(): int
{ {

View File

@ -62,6 +62,38 @@ class DeleteEmptyJournals extends Command
return 0; 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 private function deleteEmptyJournals(): void
{ {
$start = microtime(true); $start = microtime(true);
@ -90,36 +122,4 @@ class DeleteEmptyJournals extends Command
$this->info(sprintf('Verified empty journals in %s seconds', $end)); $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.');
}
}
} }

View File

@ -52,8 +52,8 @@ class DeleteOrphanedTransactions extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @throws Exception
* @return int * @return int
* @throws Exception
*/ */
public function handle(): int public function handle(): int
{ {
@ -66,45 +66,6 @@ class DeleteOrphanedTransactions extends Command
return 0; 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 * @throws Exception
*/ */
@ -124,7 +85,7 @@ class DeleteOrphanedTransactions extends Command
); );
/** @var stdClass $entry */ /** @var stdClass $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
$transaction = Transaction::find((int) $entry->transaction_id); $transaction = Transaction::find((int)$entry->transaction_id);
$transaction->delete(); $transaction->delete();
$this->info( $this->info(
sprintf( sprintf(
@ -139,4 +100,43 @@ class DeleteOrphanedTransactions extends Command
$this->info('No orphaned transactions.'); $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.');
}
}
} }

View File

@ -64,35 +64,39 @@ class EnableCurrencies extends Command
/** @var Collection $meta */ /** @var Collection $meta */
$meta = AccountMeta::where('name', 'currency_id')->groupBy('data')->get(['data']); $meta = AccountMeta::where('name', 'currency_id')->groupBy('data')->get(['data']);
foreach ($meta as $entry) { foreach ($meta as $entry) {
$found[] = (int) $entry->data; $found[] = (int)$entry->data;
} }
// get all from journals: // get all from journals:
/** @var Collection $journals */ /** @var Collection $journals */
$journals = TransactionJournal::groupBy('transaction_currency_id')->get(['transaction_currency_id']); $journals = TransactionJournal::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($journals as $entry) { foreach ($journals as $entry) {
$found[] = (int) $entry->transaction_currency_id; $found[] = (int)$entry->transaction_currency_id;
} }
// get all from transactions // get all from transactions
/** @var Collection $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) { foreach ($transactions as $entry) {
$found[] = (int) $entry->transaction_currency_id; $found[] = (int)$entry->transaction_currency_id;
$found[] = (int) $entry->foreign_currency_id; $found[] = (int)$entry->foreign_currency_id;
} }
// get all from budget limits // get all from budget limits
/** @var Collection $limits */ /** @var Collection $limits */
$limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']); $limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($limits as $entry) { 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_unique($found));
$found = array_values(array_filter($found, function (int $currencyId) { $found = array_values(
return $currencyId !== 0; array_filter(
})); $found, function (int $currencyId) {
return $currencyId !== 0;
}
)
);
$message = sprintf('%d different currencies are currently in use.', count($found)); $message = sprintf('%d different currencies are currently in use.', count($found));
$this->info($message); $this->info($message);
Log::debug($message, $found); Log::debug($message, $found);

View File

@ -39,11 +39,13 @@ class FixAccountTypes extends Command
{ {
/** /**
* The console command description. * The console command description.
*
* @var string * @var string
*/ */
protected $description = 'Make sure all journals have the correct from/to account types.'; protected $description = 'Make sure all journals have the correct from/to account types.';
/** /**
* The name and signature of the console command. * The name and signature of the console command.
*
* @var string * @var string
*/ */
protected $signature = 'firefly-iii:fix-account-types'; protected $signature = 'firefly-iii:fix-account-types';
@ -54,6 +56,7 @@ class FixAccountTypes extends Command
/** /**
* Execute the console command. * Execute the console command.
*
* @return int * @return int
* @throws FireflyException * @throws FireflyException
*/ */
@ -84,11 +87,87 @@ class FixAccountTypes extends Command
return 0; 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 TransactionJournal $journal
* @param string $type * @param string $type
* @param Transaction $source * @param Transaction $source
* @param Transaction $dest * @param Transaction $dest
*
* @throws FireflyException * @throws FireflyException
*/ */
private function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void 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); $result = $this->factory->findOrCreate($dest->account->name, AccountType::EXPENSE);
$dest->account()->associate($result); $dest->account()->associate($result);
$dest->save(); $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); $this->info($message);
Log::debug($message); Log::debug($message);
$this->inspectJournal($journal); $this->inspectJournal($journal);
@ -145,7 +227,10 @@ class FixAccountTypes extends Command
$oldSource = $dest->account; $oldSource = $dest->account;
$source->account()->associate($result); $source->account()->associate($result);
$source->save(); $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); $this->info($message);
Log::debug($message); Log::debug($message);
$this->inspectJournal($journal); $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;
}
} }

View File

@ -62,12 +62,12 @@ class FixGroupAccounts extends Command
::groupBy('transaction_group_id') ::groupBy('transaction_group_id')
->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]); ->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
foreach ($res as $journal) { foreach ($res as $journal) {
if ((int) $journal->the_count > 1) { if ((int)$journal->the_count > 1) {
$groups[] = (int) $journal->transaction_group_id; $groups[] = (int)$journal->transaction_group_id;
} }
} }
$handler = new UpdatedGroupEventHandler; $handler = new UpdatedGroupEventHandler;
foreach($groups as $groupId) { foreach ($groups as $groupId) {
$group = TransactionGroup::find($groupId); $group = TransactionGroup::find($groupId);
$event = new UpdatedTransactionGroup($group); $event = new UpdatedTransactionGroup($group);
$handler->unifyAccounts($event); $handler->unifyAccounts($event);

View File

@ -69,7 +69,7 @@ class FixLongDescriptions extends Command
$groups = TransactionGroup::get(['id', 'title']); $groups = TransactionGroup::get(['id', 'title']);
/** @var TransactionGroup $group */ /** @var TransactionGroup $group */
foreach ($groups as $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->title = substr($group->title, 0, self::MAX_LENGTH);
$group->save(); $group->save();
$this->line(sprintf('Truncated description of transaction group #%d', $group->id)); $this->line(sprintf('Truncated description of transaction group #%d', $group->id));

View File

@ -73,18 +73,6 @@ class FixRecurringTransactions extends Command
return 0; 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 * 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 * 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); $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 * @param User $user
*/ */

View File

@ -79,19 +79,6 @@ class FixTransactionTypes extends Command
return 0; 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. * Collect all transaction journals.
* *
@ -120,7 +107,7 @@ class FixTransactionTypes extends Command
return false; 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) { if ($expectedType !== $type) {
$this->line(sprintf('Transaction journal #%d was of type "%s" but is corrected to "%s"', $journal->id, $type, $expectedType)); $this->line(sprintf('Transaction journal #%d was of type "%s" but is corrected to "%s"', $journal->id, $type, $expectedType));
$this->changeJournal($journal, $expectedType); $this->changeJournal($journal, $expectedType);
@ -134,8 +121,37 @@ class FixTransactionTypes extends Command
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* *
* @throws FireflyException
* @return Account * @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 private function getDestinationAccount(TransactionJournal $journal): Account
{ {
@ -162,30 +178,14 @@ class FixTransactionTypes extends Command
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* * @param string $expectedType
* @throws FireflyException
* @return Account
*/ */
private function getSourceAccount(TransactionJournal $journal): Account private function changeJournal(TransactionJournal $journal, string $expectedType): void
{ {
$collection = $journal->transactions->filter( $type = TransactionType::whereType($expectedType)->first();
static function (Transaction $transaction) { if (null !== $type) {
return $transaction->amount < 0; $journal->transaction_type_id = $type->id;
} $journal->save();
);
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;
} }
} }

View File

@ -56,10 +56,10 @@ class RemoveBills extends Command
$start = microtime(true); $start = microtime(true);
/** @var TransactionType $withdrawal */ /** @var TransactionType $withdrawal */
$withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); $withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
if(null === $withdrawal) { if (null === $withdrawal) {
return 0; 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 */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id)); $this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id));

View File

@ -147,108 +147,16 @@ class ExportData extends Command
} }
/** /**
* @param array $options * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* @param array $data * 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'); $this->journalRepository = app(JournalRepositoryInterface::class);
foreach ($data as $key => $content) { $this->accountRepository = app(AccountRepositoryInterface::class);
$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;
} }
/** /**
@ -283,16 +191,108 @@ class ExportData extends Command
} }
/** /**
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * @param string $field
* 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 * @return Carbon
* @throws Exception
*/ */
private function stupidLaravel(): void private function getDateParameter(string $field): Carbon
{ {
$this->journalRepository = app(JournalRepositoryInterface::class); $date = Carbon::now()->subYear();
$this->accountRepository = app(AccountRepositoryInterface::class); $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));
}
} }

View File

@ -68,51 +68,6 @@ class ReportEmptyObjects extends Command
return 0; 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. * Report on budgets with no transactions or journals.
*/ */
@ -188,4 +143,49 @@ class ReportEmptyObjects extends Command
$this->line($line); $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);
}
}
} }

View File

@ -70,7 +70,7 @@ class ReportSum extends Command
/** @var User $user */ /** @var User $user */
foreach ($userRepository->all() as $user) { foreach ($userRepository->all() as $user) {
$sum = (string) $user->transactions()->sum('amount'); $sum = (string)$user->transactions()->sum('amount');
if (0 !== bccomp($sum, '0')) { if (0 !== bccomp($sum, '0')) {
$message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum); $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum);
$this->error($message); $this->error($message);

View File

@ -59,38 +59,6 @@ class RestoreOAuthKeys extends Command
return 0; 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'); $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(); OAuthKeys::storeKeysInDB();
} }
/**
*
*/
private function restoreKeysFromDB(): void
{
OAuthKeys::restoreKeysFromDB();
}
} }

View File

@ -102,6 +102,7 @@ class ApplyRules extends Command
$result = $this->verifyInput(); $result = $this->verifyInput();
if (false === $result) { if (false === $result) {
app('telemetry')->feature('system.command.errored', $this->signature); app('telemetry')->feature('system.command.errored', $this->signature);
return 1; return 1;
} }
@ -121,6 +122,7 @@ class ApplyRules extends Command
$this->warn(' --all_rules'); $this->warn(' --all_rules');
app('telemetry')->feature('system.command.errored', $this->signature); app('telemetry')->feature('system.command.errored', $this->signature);
return 1; return 1;
} }
@ -132,7 +134,7 @@ class ApplyRules extends Command
// add the accounts as filter: // add the accounts as filter:
$filterAccountList = []; $filterAccountList = [];
foreach($this->accounts as $account) { foreach ($this->accounts as $account) {
$filterAccountList[] = $account->id; $filterAccountList[] = $account->id;
} }
$list = implode(',', $filterAccountList); $list = implode(',', $filterAccountList);
@ -157,49 +159,6 @@ class ApplyRules extends Command
return 0; 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 * 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 * 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) { foreach ($accountList as $accountId) {
$accountId = (int) $accountId; $accountId = (int)$accountId;
$account = $accountRepository->findNull($accountId); $account = $accountRepository->findNull($accountId);
if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) { if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) {
$finalList->push($account); $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 * @return bool
*/ */
@ -343,7 +266,7 @@ class ApplyRules extends Command
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
foreach ($ruleGroupList as $ruleGroupId) { foreach ($ruleGroupList as $ruleGroupId) {
$ruleGroup = $this->ruleGroupRepository->find((int) $ruleGroupId); $ruleGroup = $this->ruleGroupRepository->find((int)$ruleGroupId);
if ($ruleGroup->active) { if ($ruleGroup->active) {
$this->ruleGroupSelection[] = $ruleGroup->id; $this->ruleGroupSelection[] = $ruleGroup->id;
} }
@ -376,7 +299,7 @@ class ApplyRules extends Command
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
foreach ($ruleList as $ruleId) { foreach ($ruleList as $ruleId) {
$rule = $this->ruleRepository->find((int) $ruleId); $rule = $this->ruleRepository->find((int)$ruleId);
if (null !== $rule && $rule->active) { if (null !== $rule && $rule->active) {
$this->ruleSelection[] = $rule->id; $this->ruleSelection[] = $rule->id;
} }
@ -384,4 +307,83 @@ class ApplyRules extends Command
return true; 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;
}
} }

View File

@ -66,11 +66,11 @@ class Cron extends Command
$date = null; $date = null;
try { try {
$date = new Carbon($this->option('date')); $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'))); $this->error(sprintf('"%s" is not a valid date', $this->option('date')));
$e->getMessage(); $e->getMessage();
} }
$force = (bool) $this->option('force'); $force = (bool)$this->option('force');
/* /*
* Fire recurring transaction cron job. * 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.'); $this->info('More feedback on the cron jobs can be found in the log files.');
app('telemetry')->feature('system.command.executed', $this->signature); app('telemetry')->feature('system.command.executed', $this->signature);
return 0; 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 bool $force
* @param Carbon|null $date * @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 bool $force
* @param Carbon|null $date * @param Carbon|null $date

View File

@ -92,27 +92,6 @@ class AccountCurrencies extends Command
return 0; 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 * 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 * 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; $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 Account $account
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
@ -136,13 +175,13 @@ class AccountCurrencies extends Command
Log::debug(sprintf('Now in updateAccount(%d, %s)', $account->id, $currency->code)); Log::debug(sprintf('Now in updateAccount(%d, %s)', $account->id, $currency->code));
$this->accountRepos->setUser($account->user); $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)); Log::debug(sprintf('Account currency is #%d', $accountCurrency));
$openingBalance = $this->accountRepos->getOpeningBalance($account); $openingBalance = $this->accountRepos->getOpeningBalance($account);
$obCurrency = 0; $obCurrency = 0;
if (null !== $openingBalance) { if (null !== $openingBalance) {
$obCurrency = (int) $openingBalance->transaction_currency_id; $obCurrency = (int)$openingBalance->transaction_currency_id;
Log::debug('Account has opening balance.'); Log::debug('Account has opening balance.');
} }
Log::debug(sprintf('Account OB currency is #%d.', $obCurrency)); 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()'); app('fireflyconfig')->set(self::CONFIG_NAME, true);
$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);
}
} }
} }

View File

@ -79,7 +79,7 @@ class AppendBudgetLimitPeriods extends Command
$this->theresNoLimit(); $this->theresNoLimit();
$this->markAsExecuted(); $this->markAsExecuted();
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Fixed budget limits in %s seconds.', $end)); $this->info(sprintf('Fixed budget limits in %s seconds.', $end));
@ -100,6 +100,44 @@ class AppendBudgetLimitPeriods extends Command
return false; // @codeCoverageIgnore 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 * @param BudgetLimit $limit
* *
@ -160,37 +198,4 @@ class AppendBudgetLimitPeriods extends Command
{ {
app('fireflyconfig')->set(self::CONFIG_NAME, true); 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);
}
} }

View File

@ -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(); $configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false);
$array = []; if (null !== $configVar) {
$chunks = array_chunk($transactions, 500); return (bool)$configVar->data;
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
}
/**
* @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;
} }
/** /**
@ -129,33 +102,12 @@ class BackToJournals extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore 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 * @param TransactionJournal $journal
*/ */
@ -213,7 +185,7 @@ class BackToJournals extends Command
// both have a budget, but they don't match. // both have a budget, but they don't match.
if (null !== $budget && null !== $journalBudget && $budget->id !== $journalBudget->id) { if (null !== $budget && null !== $journalBudget && $budget->id !== $journalBudget->id) {
// sync to journal: // sync to journal:
$journal->budgets()->sync([(int) $budget->id]); $journal->budgets()->sync([(int)$budget->id]);
return; return;
} }
@ -221,7 +193,7 @@ class BackToJournals extends Command
// transaction has a budget, but the journal doesn't. // transaction has a budget, but the journal doesn't.
if (null !== $budget && null === $journalBudget) { if (null !== $budget && null === $journalBudget) {
// sync to journal: // 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 * @param TransactionJournal $journal
*/ */
@ -272,12 +264,20 @@ class BackToJournals extends Command
// both have a category, but they don't match. // both have a category, but they don't match.
if (null !== $category && null !== $journalCategory && $category->id !== $journalCategory->id) { if (null !== $category && null !== $journalCategory && $category->id !== $journalCategory->id) {
// sync to journal: // sync to journal:
$journal->categories()->sync([(int) $category->id]); $journal->categories()->sync([(int)$category->id]);
} }
// transaction has a category, but the journal doesn't. // transaction has a category, but the journal doesn't.
if (null !== $category && null === $journalCategory) { 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);
}
} }

View File

@ -103,7 +103,7 @@ class BudgetLimitCurrency extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore return false; // @codeCoverageIgnore

View File

@ -102,7 +102,7 @@ class CCLiabilities extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore return false; // @codeCoverageIgnore

View File

@ -72,7 +72,7 @@ class MigrateAttachments extends Command
foreach ($attachments as $att) { foreach ($attachments as $att) {
// move description: // move description:
$attDescription = (string) $att->description; $attDescription = (string)$att->description;
if ('' !== $attDescription) { if ('' !== $attDescription) {
// find or create note: // find or create note:
@ -112,7 +112,7 @@ class MigrateAttachments extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore return false; // @codeCoverageIgnore

View File

@ -111,7 +111,7 @@ class MigrateJournalNotes extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore return false; // @codeCoverageIgnore

View File

@ -85,19 +85,26 @@ class MigrateRecurrenceMeta extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore 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; app('fireflyconfig')->set(self::CONFIG_NAME, true);
// get all recurrence meta data:
$collection = RecurrenceMeta::with('recurrence')->get();
/** @var RecurrenceMeta $meta */
foreach ($collection as $meta) {
$count += $this->migrateEntry($meta);
}
return $count;
} }
} }

View File

@ -0,0 +1,109 @@
<?php
namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command;
/**
* Class MigrateRecurrenceType
*/
class MigrateRecurrenceType extends Command
{
public const CONFIG_NAME = '550_migrate_recurrence_type';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Migrate transaction type of recurring transaction.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:migrate-recurrence-type {--F|force : Force the execution of this command.}';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$start = microtime(true);
if ($this->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));
}
}

View File

@ -71,6 +71,30 @@ class MigrateTagLocations extends Command
return 0; 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 * @param Tag $tag
* *
@ -81,28 +105,6 @@ class MigrateTagLocations extends Command
return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel; 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 * @param Tag $tag
*/ */
@ -121,15 +123,12 @@ class MigrateTagLocations extends Command
$tag->save(); $tag->save();
} }
private function migrateTagLocations(): void /**
*
*/
private function markAsExecuted(): void
{ {
$tags = Tag::get(); app('fireflyconfig')->set(self::CONFIG_NAME, true);
/** @var Tag $tag */
foreach ($tags as $tag) {
if ($this->hasLocationDetails($tag)) {
$this->migrateLocationDetails($tag);
}
}
} }

View File

@ -112,122 +112,19 @@ class MigrateToGroups extends Command
} }
/** /**
* @param TransactionJournal $journal * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* @param Transaction $transaction * 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( $this->count = 0;
static function (Transaction $subject) use ($transaction) { $this->journalRepository = app(JournalRepositoryInterface::class);
$amount = (float) $transaction->amount * -1 === (float) $subject->amount; $this->service = app(JournalDestroyService::class);
$identifier = $transaction->identifier === $subject->identifier; $this->groupFactory = app(TransactionGroupFactory::class);
Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); $this->cliRepository = app(JournalCLIRepositoryInterface::class);
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++;
} }
/** /**
@ -237,32 +134,12 @@ class MigrateToGroups extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore 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 * @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); 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);
}
} }

View File

@ -67,8 +67,8 @@ class MigrateToRules extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @throws FireflyException
* @return int * @return int
* @throws FireflyException
*/ */
public function handle(): int public function handle(): int
{ {
@ -103,6 +103,22 @@ class MigrateToRules extends Command
return 0; 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 * @return bool
*/ */
@ -110,18 +126,46 @@ class MigrateToRules extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore 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, 'active' => true,
'strict' => false, 'strict' => false,
'stop_processing' => false, // field is no longer used. 'stop_processing' => false, // field is no longer used.
'title' => (string) trans('firefly.rule_for_bill_title', ['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), 'description' => (string)trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data),
'trigger' => 'store-journal', 'trigger' => 'store-journal',
'triggers' => [ '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); app('fireflyconfig')->set(self::CONFIG_NAME, true);
$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);
} }
} }

View File

@ -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; $this->count = 0;
if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { $this->accountCurrencies = [];
return null; // @codeCoverageIgnore $this->accountRepos = app(AccountRepositoryInterface::class);
} $this->currencyRepos = app(CurrencyRepositoryInterface::class);
if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { $this->journalRepos = app(JournalRepositoryInterface::class);
return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore $this->cliRepos = app(JournalCLIRepositoryInterface::class);
}
$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;
} }
/** /**
@ -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,]
);
/** /** @var TransactionJournal $journal */
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is foreach ($set as $journal) {
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should $this->updateJournalCurrency($journal);
* 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);
} }
/** /**
@ -256,20 +201,75 @@ class OtherCurrenciesCorrections extends Command
} }
/** /**
* This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for * Gets the transaction that determines the transaction that "leads" and will determine
* the accounts they are linked to. * the currency to be used by all transactions, and the journal itself.
* Both source and destination must match the respective currency preference of the related asset account. *
* So FF3 must verify all transactions. * @param TransactionJournal $journal
*
* @return Transaction|null
*/ */
private function updateOtherJournalsCurrencies(): void private function getLeadTransaction(TransactionJournal $journal): ?Transaction
{ {
$set = $this->cliRepos->getAllJournals( /** @var Transaction $lead */
[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,] $lead = null;
); switch ($journal->transactionType->type) {
default:
/** @var TransactionJournal $journal */ break;
foreach ($set as $journal) { case TransactionType::WITHDRAWAL:
$this->updateJournalCurrency($journal); $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);
} }
} }

View File

@ -102,7 +102,7 @@ class RenameAccountMeta extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; // @codeCoverageIgnore return false; // @codeCoverageIgnore

View File

@ -105,60 +105,6 @@ class TransactionIdentifier extends Command
return 0; 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 * 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 * 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; $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 * 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. * 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);
}
} }

View File

@ -56,12 +56,12 @@ class TransferCurrenciesCorrections extends Command
private JournalCLIRepositoryInterface $cliRepos; private JournalCLIRepositoryInterface $cliRepos;
private int $count; private int $count;
private ?Account $destinationAccount; private ?Account $destinationAccount;
private ?TransactionCurrency $destinationCurrency; private ?TransactionCurrency $destinationCurrency;
private ?Transaction $destinationTransaction; private ?Transaction $destinationTransaction;
private ?Account $sourceAccount; private ?Account $sourceAccount;
private ?TransactionCurrency $sourceCurrency; private ?TransactionCurrency $sourceCurrency;
private ?Transaction $sourceTransaction; private ?Transaction $sourceTransaction;
/** /**
* Execute the console command. * Execute the console command.
@ -99,6 +99,313 @@ class TransferCurrenciesCorrections extends Command
return 0; 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 * The destination transaction must have a currency. If not, it will be added by
* taking it from the destination account's preference. * 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 * The destination transaction must have the correct currency. If not, it will be set by
* taking it from the destination account's preference. * 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 * 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. * 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 * If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code
* taking it from the source account's preference. * to restore it.
*/ */
private function fixSourceUnmatchedCurrency(): void private function fixDestNullForeignAmount(): void
{ {
if (null !== $this->sourceCurrency if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) {
&& null === $this->sourceTransaction->foreign_amount $this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1');
&& (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id $this->destinationTransaction->save();
) {
$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->count++;
$this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id; Log::debug(
$this->sourceTransaction->save(); 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); 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);
}
} }

View File

@ -76,6 +76,7 @@ class UpgradeDatabase extends Command
'firefly-iii:rename-account-meta', 'firefly-iii:rename-account-meta',
'firefly-iii:migrate-recurrence-meta', 'firefly-iii:migrate-recurrence-meta',
'firefly-iii:migrate-tag-locations', 'firefly-iii:migrate-tag-locations',
'firefly-iii:migrate-recurrence-type',
// there are 16 verify commands. // there are 16 verify commands.
'firefly-iii:fix-piggies', 'firefly-iii:fix-piggies',
@ -117,9 +118,9 @@ class UpgradeDatabase extends Command
echo $result; echo $result;
} }
// set new DB 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'));
// index will set FF3 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; return 0;
} }

View File

@ -83,6 +83,7 @@ class InstallController extends Controller
'firefly-iii:rename-account-meta' => [], 'firefly-iii:rename-account-meta' => [],
'firefly-iii:migrate-recurrence-meta' => [], 'firefly-iii:migrate-recurrence-meta' => [],
'firefly-iii:migrate-tag-locations' => [], 'firefly-iii:migrate-tag-locations' => [],
'firefly-iii:migrate-recurrence-type' => [],
// verify commands // verify commands
'firefly-iii:fix-piggies' => [], 'firefly-iii:fix-piggies' => [],
@ -120,10 +121,10 @@ class InstallController extends Controller
public function index() public function index()
{ {
// index will set FF3 version. // 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. // 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'); return prefixView('install.index');
} }
@ -156,7 +157,7 @@ class InstallController extends Controller
*/ */
public function runCommand(Request $request): JsonResponse public function runCommand(Request $request): JsonResponse
{ {
$requestIndex = (int) $request->get('index'); $requestIndex = (int)$request->get('index');
$response = [ $response = [
'hasNextCommand' => false, 'hasNextCommand' => false,
'done' => true, 'done' => true,
@ -183,6 +184,7 @@ class InstallController extends Controller
if (false === $result) { if (false === $result) {
$response['errorMessage'] = $this->lastError; $response['errorMessage'] = $this->lastError;
$response['error'] = true; $response['error'] = true;
return response()->json($response); return response()->json($response);
} }
$index++; $index++;
@ -197,6 +199,7 @@ class InstallController extends Controller
/** /**
* @param string $command * @param string $command
* @param array $args * @param array $args
*
* @return bool * @return bool
*/ */
private function executeCommand(string $command, array $args): bool private function executeCommand(string $command, array $args): bool
@ -215,15 +218,17 @@ class InstallController extends Controller
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
if (strpos($e->getMessage(), 'open_basedir restriction in effect')) { if (strpos($e->getMessage(), 'open_basedir restriction in effect')) {
$this->lastError = self::BASEDIR_ERROR; $this->lastError = self::BASEDIR_ERROR;
return false; return false;
} }
$this->lastError = sprintf('%s %s', self::OTHER_ERROR, $e->getMessage()); $this->lastError = sprintf('%s %s', self::OTHER_ERROR, $e->getMessage());
return false; return false;
} }
// clear cache as well. // clear cache as well.
Cache::clear(); Cache::clear();
Preferences::mark(); Preferences::mark();
return true; return true;
} }
} }

View File

@ -74,6 +74,8 @@ use Illuminate\Support\Collection;
* @method static Builder|RecurrenceTransaction withTrashed() * @method static Builder|RecurrenceTransaction withTrashed()
* @method static Builder|RecurrenceTransaction withoutTrashed() * @method static Builder|RecurrenceTransaction withoutTrashed()
* @mixin Eloquent * @mixin Eloquent
* @property int|null $transaction_type_id
* @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionTypeId($value)
*/ */
class RecurrenceTransaction extends Model class RecurrenceTransaction extends Model
{ {

View File

@ -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 whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereWebhookMessageId($value) * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereWebhookMessageId($value)
* @mixin \Eloquent * @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 class WebhookAttempt extends Model
{ {

View File

@ -170,6 +170,7 @@
"@php artisan firefly-iii:rename-account-meta", "@php artisan firefly-iii:rename-account-meta",
"@php artisan firefly-iii:migrate-recurrence-meta", "@php artisan firefly-iii:migrate-recurrence-meta",
"@php artisan firefly-iii:migrate-tag-locations", "@php artisan firefly-iii:migrate-tag-locations",
"@php artisan firefly-iii:migrate-recurrence-type",
"@php artisan firefly-iii:fix-piggies", "@php artisan firefly-iii:fix-piggies",
"@php artisan firefly-iii:create-link-types", "@php artisan firefly-iii:create-link-types",

147
composer.lock generated
View File

@ -1356,16 +1356,16 @@
}, },
{ {
"name": "guzzlehttp/promises", "name": "guzzlehttp/promises",
"version": "1.4.0", "version": "1.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/promises.git", "url": "https://github.com/guzzle/promises.git",
"reference": "60d379c243457e073cff02bc323a2a86cb355631" "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d",
"reference": "60d379c243457e073cff02bc323a2a86cb355631", "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1405,9 +1405,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/guzzle/promises/issues", "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", "name": "guzzlehttp/psr7",
@ -1641,16 +1641,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v8.31.0", "version": "v8.32.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35" "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/2aa5c2488d25178ebc097052c7897a0e463ddc35", "url": "https://api.github.com/repos/laravel/framework/zipball/7c37b64f8153c16b6406f5c28cf37828ebbe8846",
"reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35", "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1805,7 +1805,7 @@
"issues": "https://github.com/laravel/framework/issues", "issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework" "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", "name": "laravel/passport",
@ -2792,16 +2792,16 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "2.45.1", "version": "2.46.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/briannesbitt/Carbon.git", "url": "https://github.com/briannesbitt/Carbon.git",
"reference": "528783b188bdb853eb21239b1722831e0f000a8d" "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/528783b188bdb853eb21239b1722831e0f000a8d", "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
"reference": "528783b188bdb853eb21239b1722831e0f000a8d", "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2881,7 +2881,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-11T18:30:17+00:00" "time": "2021-02-24T17:30:44+00:00"
}, },
{ {
"name": "nyholm/psr7", "name": "nyholm/psr7",
@ -4287,20 +4287,20 @@
}, },
{ {
"name": "swiftmailer/swiftmailer", "name": "swiftmailer/swiftmailer",
"version": "v6.2.6", "version": "v6.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git", "url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff" "reference": "15f7faf8508e04471f666633addacf54c0ab5933"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d2791ff0b73247cdc2096b14f5580aba40c12bff", "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/15f7faf8508e04471f666633addacf54c0ab5933",
"reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff", "reference": "15f7faf8508e04471f666633addacf54c0ab5933",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"egulias/email-validator": "^2.0", "egulias/email-validator": "^2.0|^3.1",
"php": ">=7.0.0", "php": ">=7.0.0",
"symfony/polyfill-iconv": "^1.0", "symfony/polyfill-iconv": "^1.0",
"symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-intl-idn": "^1.10",
@ -4346,7 +4346,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/swiftmailer/swiftmailer/issues", "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": [ "funding": [
{ {
@ -4358,20 +4358,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-03-05T12:08:49+00:00" "time": "2021-03-09T12:30:35+00:00"
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v5.2.4", "version": "v5.2.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556" "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79",
"reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4439,7 +4439,7 @@
"terminal" "terminal"
], ],
"support": { "support": {
"source": "https://github.com/symfony/console/tree/v5.2.4" "source": "https://github.com/symfony/console/tree/v5.2.5"
}, },
"funding": [ "funding": [
{ {
@ -4455,7 +4455,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-23T10:08:49+00:00" "time": "2021-03-06T13:42:15+00:00"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
@ -5037,16 +5037,16 @@
}, },
{ {
"name": "symfony/http-kernel", "name": "symfony/http-kernel",
"version": "v5.2.4", "version": "v5.2.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/http-kernel.git", "url": "https://github.com/symfony/http-kernel.git",
"reference": "c452dbe4f385f030c3957821bf921b13815d6140" "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/c452dbe4f385f030c3957821bf921b13815d6140", "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b8c63ef63c2364e174c3b3e0ba0bf83455f97f73",
"reference": "c452dbe4f385f030c3957821bf921b13815d6140", "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5129,7 +5129,7 @@
"description": "Provides a structured process for converting a Request into a Response", "description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/http-kernel/tree/v5.2.4" "source": "https://github.com/symfony/http-kernel/tree/v5.2.5"
}, },
"funding": [ "funding": [
{ {
@ -5145,20 +5145,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-03-04T18:05:55+00:00" "time": "2021-03-10T17:07:35+00:00"
}, },
{ {
"name": "symfony/mime", "name": "symfony/mime",
"version": "v5.2.4", "version": "v5.2.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/mime.git", "url": "https://github.com/symfony/mime.git",
"reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd" "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd", "url": "https://api.github.com/repos/symfony/mime/zipball/554ba128f1955038b45db5e1fa7e93bfc683b139",
"reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd", "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5169,12 +5169,13 @@
"symfony/polyfill-php80": "^1.15" "symfony/polyfill-php80": "^1.15"
}, },
"conflict": { "conflict": {
"egulias/email-validator": "~3.0.0",
"phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0", "phpdocumentor/type-resolver": "<1.4.0",
"symfony/mailer": "<4.4" "symfony/mailer": "<4.4"
}, },
"require-dev": { "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", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/dependency-injection": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0",
"symfony/property-access": "^4.4|^5.1", "symfony/property-access": "^4.4|^5.1",
@ -5211,7 +5212,7 @@
"mime-type" "mime-type"
], ],
"support": { "support": {
"source": "https://github.com/symfony/mime/tree/v5.2.4" "source": "https://github.com/symfony/mime/tree/v5.2.5"
}, },
"funding": [ "funding": [
{ {
@ -5227,7 +5228,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-15T18:55:04+00:00" "time": "2021-03-07T16:08:20+00:00"
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
@ -6362,16 +6363,16 @@
}, },
{ {
"name": "symfony/translation", "name": "symfony/translation",
"version": "v5.2.4", "version": "v5.2.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/translation.git", "url": "https://github.com/symfony/translation.git",
"reference": "74b0353ab34ff4cca827a2cf909e325d96815e60" "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/74b0353ab34ff4cca827a2cf909e325d96815e60", "url": "https://api.github.com/repos/symfony/translation/zipball/0947ab1e3aabd22a6bef393874b2555d2bb976da",
"reference": "74b0353ab34ff4cca827a2cf909e325d96815e60", "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6435,7 +6436,7 @@
"description": "Provides tools to internationalize your application", "description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/translation/tree/v5.2.4" "source": "https://github.com/symfony/translation/tree/v5.2.5"
}, },
"funding": [ "funding": [
{ {
@ -6451,7 +6452,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-03-04T15:41:09+00:00" "time": "2021-03-06T07:59:01+00:00"
}, },
{ {
"name": "symfony/translation-contracts", "name": "symfony/translation-contracts",
@ -6533,16 +6534,16 @@
}, },
{ {
"name": "symfony/var-dumper", "name": "symfony/var-dumper",
"version": "v5.2.4", "version": "v5.2.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/var-dumper.git", "url": "https://github.com/symfony/var-dumper.git",
"reference": "6a81fec0628c468cf6d5c87a4d003725e040e223" "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/6a81fec0628c468cf6d5c87a4d003725e040e223", "url": "https://api.github.com/repos/symfony/var-dumper/zipball/002ab5a36702adf0c9a11e6d8836623253e9045e",
"reference": "6a81fec0628c468cf6d5c87a4d003725e040e223", "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6601,7 +6602,7 @@
"dump" "dump"
], ],
"support": { "support": {
"source": "https://github.com/symfony/var-dumper/tree/v5.2.4" "source": "https://github.com/symfony/var-dumper/tree/v5.2.5"
}, },
"funding": [ "funding": [
{ {
@ -6617,7 +6618,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-18T23:11:19+00:00" "time": "2021-03-06T07:59:01+00:00"
}, },
{ {
"name": "tijsverkoyen/css-to-inline-styles", "name": "tijsverkoyen/css-to-inline-styles",
@ -6674,16 +6675,16 @@
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
"version": "v2.14.3", "version": "v2.14.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/Twig.git", "url": "https://github.com/twigphp/Twig.git",
"reference": "8bc568d460d88b25c00c046256ec14a787ea60d9" "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/8bc568d460d88b25c00c046256ec14a787ea60d9", "url": "https://api.github.com/repos/twigphp/Twig/zipball/0b4ba691fb99ec7952d25deb36c0a83061b93bbf",
"reference": "8bc568d460d88b25c00c046256ec14a787ea60d9", "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6737,7 +6738,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/twigphp/Twig/issues", "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": [ "funding": [
{ {
@ -6749,7 +6750,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-01-05T15:34:33+00:00" "time": "2021-03-10T10:05:55+00:00"
}, },
{ {
"name": "vlucas/phpdotenv", "name": "vlucas/phpdotenv",
@ -9105,16 +9106,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "0.12.80", "version": "0.12.81",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "c6a1b17f22ecf708d434d6bee05092647ec7e686" "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6a1b17f22ecf708d434d6bee05092647ec7e686", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0dd5b0ebeff568f7000022ea5f04aa86ad3124b8",
"reference": "c6a1b17f22ecf708d434d6bee05092647ec7e686", "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -9145,7 +9146,7 @@
"description": "PHPStan - PHP Static Analysis Tool", "description": "PHPStan - PHP Static Analysis Tool",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan/issues", "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": [ "funding": [
{ {
@ -9161,7 +9162,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-28T20:22:43+00:00" "time": "2021-03-08T22:03:02+00:00"
}, },
{ {
"name": "phpstan/phpstan-deprecation-rules", "name": "phpstan/phpstan-deprecation-rules",
@ -9691,12 +9692,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git", "url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "0745f820eed6cb92603ca44a9c137ff8ce315e86" "reference": "672ed7cb0191a12cf8b12b752c9ef74bb5d21cec"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0745f820eed6cb92603ca44a9c137ff8ce315e86", "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/672ed7cb0191a12cf8b12b752c9ef74bb5d21cec",
"reference": "0745f820eed6cb92603ca44a9c137ff8ce315e86", "reference": "672ed7cb0191a12cf8b12b752c9ef74bb5d21cec",
"shasum": "" "shasum": ""
}, },
"conflict": { "conflict": {
@ -9756,8 +9757,9 @@
"ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6", "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-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-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/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/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/platform-ui-assets-bundle": ">=4.2,<4.2.3",
"ezsystems/repository-forms": ">=2.3,<2.3.2.1", "ezsystems/repository-forms": ">=2.3,<2.3.2.1",
@ -9787,6 +9789,7 @@
"illuminate/view": ">=7,<7.1.2", "illuminate/view": ">=7,<7.1.2",
"ivankristianto/phpwhois": "<=4.3", "ivankristianto/phpwhois": "<=4.3",
"james-heinrich/getid3": "<1.9.9", "james-heinrich/getid3": "<1.9.9",
"joomla/archive": "<1.1.10",
"joomla/session": "<1.3.1", "joomla/session": "<1.3.1",
"jsmitty12/phpwhois": "<5.1", "jsmitty12/phpwhois": "<5.1",
"kazist/phpwhois": "<=4.2.6", "kazist/phpwhois": "<=4.2.6",
@ -9812,7 +9815,7 @@
"nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13",
"nystudio107/craft-seomatic": "<3.3", "nystudio107/craft-seomatic": "<3.3",
"nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "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/cms": "= 1.0.469|>=1.0.319,<1.0.469",
"october/october": ">=1.0.319,<1.0.466", "october/october": ">=1.0.319,<1.0.466",
"october/rain": "<1.0.472|>=1.1,<1.1.2", "october/rain": "<1.0.472|>=1.1,<1.1.2",
@ -10011,7 +10014,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-03-03T23:02:20+00:00" "time": "2021-03-11T18:09:51+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",