mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-15 08:35:00 +00:00
Fix a few commands.
This commit is contained in:
48
.ci/php-cs-fixer/composer.lock
generated
48
.ci/php-cs-fixer/composer.lock
generated
@@ -677,16 +677,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "5.0.1",
|
||||
"version": "5.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02"
|
||||
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/aae9a0a43bff37bd5d8d0311426c87bf36153f02",
|
||||
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
|
||||
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -732,7 +732,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
||||
"security": "https://github.com/sebastianbergmann/diff/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.1"
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -740,20 +740,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-03-23T05:12:41+00:00"
|
||||
"time": "2023-05-01T07:48:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v6.2.8",
|
||||
"version": "v6.2.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b"
|
||||
"reference": "12288d9f4500f84a4d02254d4aa968b15488476f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b",
|
||||
"reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/12288d9f4500f84a4d02254d4aa968b15488476f",
|
||||
"reference": "12288d9f4500f84a4d02254d4aa968b15488476f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -820,7 +820,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v6.2.8"
|
||||
"source": "https://github.com/symfony/console/tree/v6.2.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -836,7 +836,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-03-29T21:42:15+00:00"
|
||||
"time": "2023-04-28T13:37:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@@ -1069,16 +1069,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v6.2.7",
|
||||
"version": "v6.2.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "82b6c62b959f642d000456f08c6d219d749215b3"
|
||||
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3",
|
||||
"reference": "82b6c62b959f642d000456f08c6d219d749215b3",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
|
||||
"reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1112,7 +1112,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.2.7"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v6.2.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1128,7 +1128,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-14T08:44:56+00:00"
|
||||
"time": "2023-04-18T13:46:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
@@ -1755,16 +1755,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v6.2.8",
|
||||
"version": "v6.2.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "75ed64103df4f6615e15a7fe38b8111099f47416"
|
||||
"reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416",
|
||||
"reference": "75ed64103df4f6615e15a7fe38b8111099f47416",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e",
|
||||
"reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1796,7 +1796,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v6.2.8"
|
||||
"source": "https://github.com/symfony/process/tree/v6.2.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1812,7 +1812,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-03-09T16:20:02+00:00"
|
||||
"time": "2023-04-18T13:56:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
|
@@ -177,25 +177,6 @@ class CorrectAmounts extends Command
|
||||
$this->line(sprintf('Corrected %d currency exchange rate(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function fixRepetitions(): void
|
||||
{
|
||||
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All piggy bank repetition amounts are positive.');
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBankRepetition $item */
|
||||
foreach ($set as $item) {
|
||||
$item->currentamount = app('steam')->positive((string)$item->currentamount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
@@ -237,6 +218,25 @@ class CorrectAmounts extends Command
|
||||
$this->line(sprintf('Corrected %d recurring transaction amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function fixRepetitions(): void
|
||||
{
|
||||
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All piggy bank repetition amounts are positive.');
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBankRepetition $item */
|
||||
foreach ($set as $item) {
|
||||
$item->currentamount = app('steam')->positive((string)$item->currentamount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
@@ -83,7 +83,7 @@ class CorrectDatabase extends Command
|
||||
'firefly-iii:fix-frontpage-accounts',
|
||||
// new!
|
||||
'firefly-iii:unify-group-accounts',
|
||||
'firefly-iii:trigger-credit-recalculation'
|
||||
'firefly-iii:trigger-credit-recalculation',
|
||||
];
|
||||
foreach ($commands as $command) {
|
||||
$this->line(sprintf('Now executing command "%s"', $command));
|
||||
|
@@ -33,8 +33,8 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Illuminate\Console\Command;
|
||||
use JsonException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use JsonException;
|
||||
|
||||
/**
|
||||
* Class CorrectOpeningBalanceCurrencies
|
||||
|
@@ -61,6 +61,33 @@ class DeleteEmptyJournals extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function deleteEmptyJournals(): void
|
||||
{
|
||||
$start = microtime(true);
|
||||
$count = 0;
|
||||
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->groupBy('transaction_journals.id')
|
||||
->whereNull('transactions.transaction_journal_id')
|
||||
->get(['transaction_journals.id']);
|
||||
|
||||
foreach ($set as $entry) {
|
||||
try {
|
||||
TransactionJournal::find($entry->id)->delete();
|
||||
} catch (QueryException $e) {
|
||||
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
|
||||
}
|
||||
|
||||
|
||||
$this->info(sprintf('Deleted empty transaction journal #%d', $entry->id));
|
||||
++$count;
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('No empty transaction journals.');
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified empty journals in %s seconds', $end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete transactions and their journals if they have an uneven number of transactions.
|
||||
*/
|
||||
@@ -91,31 +118,4 @@ class DeleteEmptyJournals extends Command
|
||||
$this->info('No uneven transaction journals.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteEmptyJournals(): void
|
||||
{
|
||||
$start = microtime(true);
|
||||
$count = 0;
|
||||
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->groupBy('transaction_journals.id')
|
||||
->whereNull('transactions.transaction_journal_id')
|
||||
->get(['transaction_journals.id']);
|
||||
|
||||
foreach ($set as $entry) {
|
||||
try {
|
||||
TransactionJournal::find($entry->id)->delete();
|
||||
} catch (QueryException $e) {
|
||||
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
|
||||
}
|
||||
|
||||
|
||||
$this->info(sprintf('Deleted empty transaction journal #%d', $entry->id));
|
||||
++$count;
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('No empty transaction journals.');
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified empty journals in %s seconds', $end));
|
||||
}
|
||||
}
|
||||
|
@@ -27,7 +27,6 @@ use Exception;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
@@ -66,6 +65,38 @@ class DeleteOrphanedTransactions extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function deleteFromOrphanedAccounts(): void
|
||||
{
|
||||
$set
|
||||
= Transaction::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
||||
->whereNotNull('accounts.deleted_at')
|
||||
->get(['transactions.*']);
|
||||
$count = 0;
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
// delete journals
|
||||
$journal = TransactionJournal::find((int)$transaction->transaction_journal_id);
|
||||
if ($journal) {
|
||||
$journal->delete();
|
||||
}
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteOrphanedJournals(): void
|
||||
{
|
||||
$set = TransactionJournal::leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
|
||||
@@ -129,36 +160,4 @@ class DeleteOrphanedTransactions extends Command
|
||||
$this->info('No orphaned transactions.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function deleteFromOrphanedAccounts(): void
|
||||
{
|
||||
$set
|
||||
= Transaction::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
||||
->whereNotNull('accounts.deleted_at')
|
||||
->get(['transactions.*']);
|
||||
$count = 0;
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
// delete journals
|
||||
$journal = TransactionJournal::find((int)$transaction->transaction_journal_id);
|
||||
if ($journal) {
|
||||
$journal->delete();
|
||||
}
|
||||
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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -69,30 +69,38 @@ class FixIbans extends Command
|
||||
{
|
||||
$set = [];
|
||||
/** @var Account $account */
|
||||
foreach($accounts as $account) {
|
||||
foreach ($accounts as $account) {
|
||||
$userId = (int)$account->user_id;
|
||||
$set[$userId] = $set[$userId] ?? [];
|
||||
$iban = (string)$account->iban;
|
||||
if('' === $iban) {
|
||||
if ('' === $iban) {
|
||||
continue;
|
||||
}
|
||||
$type = $account->accountType->type;
|
||||
if(in_array($type, [AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], true)) {
|
||||
if (in_array($type, [AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], true)) {
|
||||
$type = 'liabilities';
|
||||
}
|
||||
if(array_key_exists($iban, $set[$userId])) {
|
||||
if (array_key_exists($iban, $set[$userId])) {
|
||||
// iban already in use! two exceptions exist:
|
||||
if(
|
||||
if (
|
||||
!(AccountType::EXPENSE === $set[$userId][$iban] && AccountType::REVENUE === $type) && // allowed combination
|
||||
!(AccountType::REVENUE === $set[$userId][$iban] && AccountType::EXPENSE === $type) // also allowed combination.
|
||||
) {
|
||||
$this->line(sprintf('IBAN "%s" is used more than once and will be removed from %s #%d ("%s")', $iban, $account->accountType->type, $account->id, $account->name));
|
||||
$this->line(
|
||||
sprintf(
|
||||
'IBAN "%s" is used more than once and will be removed from %s #%d ("%s")',
|
||||
$iban,
|
||||
$account->accountType->type,
|
||||
$account->id,
|
||||
$account->name
|
||||
)
|
||||
);
|
||||
$account->iban = null;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
if(!array_key_exists($iban, $set[$userId])) {
|
||||
if (!array_key_exists($iban, $set[$userId])) {
|
||||
$set[$userId][$iban] = $type;
|
||||
}
|
||||
}
|
||||
|
@@ -70,19 +70,6 @@ class FixRecurringTransactions extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->recurringRepos = app(RecurringRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -95,19 +82,6 @@ class FixRecurringTransactions extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function processUser(User $user): void
|
||||
{
|
||||
$this->recurringRepos->setUser($user);
|
||||
$recurrences = $this->recurringRepos->get();
|
||||
/** @var Recurrence $recurrence */
|
||||
foreach ($recurrences as $recurrence) {
|
||||
$this->processRecurrence($recurrence);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Recurrence $recurrence
|
||||
*/
|
||||
@@ -140,4 +114,30 @@ class FixRecurringTransactions extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function processUser(User $user): void
|
||||
{
|
||||
$this->recurringRepos->setUser($user);
|
||||
$recurrences = $this->recurringRepos->get();
|
||||
/** @var Recurrence $recurrence */
|
||||
foreach ($recurrences as $recurrence) {
|
||||
$this->processRecurrence($recurrence);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->recurringRepos = app(RecurringRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
}
|
||||
}
|
||||
|
@@ -78,6 +78,19 @@ class FixTransactionTypes extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $expectedType
|
||||
*/
|
||||
private function changeJournal(TransactionJournal $journal, string $expectedType): void
|
||||
{
|
||||
$type = TransactionType::whereType($expectedType)->first();
|
||||
if (null !== $type) {
|
||||
$journal->transaction_type_id = $type->id;
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all transaction journals.
|
||||
*
|
||||
@@ -125,36 +138,6 @@ class FixTransactionTypes extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @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('300001: Journal #%d has no source transaction.', $journal->id));
|
||||
}
|
||||
if (1 !== $collection->count()) {
|
||||
throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $collection->first();
|
||||
/** @var Account|null $account */
|
||||
$account = $transaction->account;
|
||||
if (null === $account) {
|
||||
throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
@@ -187,14 +170,31 @@ class FixTransactionTypes extends Command
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $expectedType
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function changeJournal(TransactionJournal $journal, string $expectedType): void
|
||||
private function getSourceAccount(TransactionJournal $journal): Account
|
||||
{
|
||||
$type = TransactionType::whereType($expectedType)->first();
|
||||
if (null !== $type) {
|
||||
$journal->transaction_type_id = $type->id;
|
||||
$journal->save();
|
||||
$collection = $journal->transactions->filter(
|
||||
static function (Transaction $transaction) {
|
||||
return $transaction->amount < 0;
|
||||
}
|
||||
);
|
||||
if (0 === $collection->count()) {
|
||||
throw new FireflyException(sprintf('300001: Journal #%d has no source transaction.', $journal->id));
|
||||
}
|
||||
if (1 !== $collection->count()) {
|
||||
throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $collection->first();
|
||||
/** @var Account|null $account */
|
||||
$account = $transaction->account;
|
||||
if (null === $account) {
|
||||
throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
|
@@ -39,17 +39,6 @@ class TriggerCreditCalculation extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function processAccounts(): void
|
||||
{
|
||||
$accounts = Account::leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
foreach ($accounts as $account) {
|
||||
Log::debug(sprintf('Processing account #%d ("%s")', $account->id, $account->name));
|
||||
$this->processAccount($account);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return void
|
||||
@@ -61,4 +50,15 @@ class TriggerCreditCalculation extends Command
|
||||
$object->setAccount($account);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function processAccounts(): void
|
||||
{
|
||||
$accounts = Account::leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
foreach ($accounts as $account) {
|
||||
Log::debug(sprintf('Processing account #%d ("%s")', $account->id, $account->name));
|
||||
$this->processAccount($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -51,39 +51,6 @@ class CreateGroupMemberships extends Command
|
||||
*/
|
||||
protected $signature = 'firefly-iii:create-group-memberships';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
$this->createGroupMemberships();
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Validated group memberships in %s seconds.', $end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createGroupMemberships(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
Log::debug(sprintf('Manage group memberships for user #%d', $user->id));
|
||||
self::createGroupMembership($user);
|
||||
Log::debug(sprintf('Done with user #%d', $user->id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO move to helper.
|
||||
* @param User $user
|
||||
@@ -125,4 +92,37 @@ class CreateGroupMemberships extends Command
|
||||
|
||||
Log::debug(sprintf('User #%d now has main group.', $user->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
$this->createGroupMemberships();
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Validated group memberships in %s seconds.', $end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createGroupMemberships(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
Log::debug(sprintf('Manage group memberships for user #%d', $user->id));
|
||||
self::createGroupMembership($user);
|
||||
Log::debug(sprintf('Done with user #%d', $user->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -67,6 +67,51 @@ class ReportEmptyObjects extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on accounts with no transactions.
|
||||
*/
|
||||
private function reportAccounts(): void
|
||||
{
|
||||
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
|
||||
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
|
||||
->whereNull('transactions.account_id')
|
||||
->get(
|
||||
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
|
||||
);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'User #%d (%s) has account #%d ("%s") which has no transactions.';
|
||||
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on budgets with no budget limits (which makes them pointless).
|
||||
*/
|
||||
private function reportBudgetLimits(): void
|
||||
{
|
||||
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
|
||||
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
|
||||
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
|
||||
->whereNull('budget_limits.id')
|
||||
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
|
||||
|
||||
/** @var Budget $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = sprintf(
|
||||
'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->id,
|
||||
$entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report on budgets with no transactions or journals.
|
||||
*/
|
||||
@@ -141,49 +186,4 @@ class ReportEmptyObjects extends Command
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on accounts with no transactions.
|
||||
*/
|
||||
private function reportAccounts(): void
|
||||
{
|
||||
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
|
||||
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
|
||||
->whereNull('transactions.account_id')
|
||||
->get(
|
||||
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
|
||||
);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'User #%d (%s) has account #%d ("%s") which has no transactions.';
|
||||
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on budgets with no budget limits (which makes them pointless).
|
||||
*/
|
||||
private function reportBudgetLimits(): void
|
||||
{
|
||||
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
|
||||
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
|
||||
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
|
||||
->whereNull('budget_limits.id')
|
||||
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
|
||||
|
||||
/** @var Budget $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = sprintf(
|
||||
'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->id,
|
||||
$entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -61,7 +61,7 @@ class ReportIntegrity extends Command
|
||||
'firefly-iii:report-empty-objects',
|
||||
'firefly-iii:report-sum',
|
||||
'firefly-iii:restore-oauth-keys',
|
||||
'firefly-iii:upgrade-group-information'
|
||||
'firefly-iii:upgrade-group-information',
|
||||
];
|
||||
foreach ($commands as $command) {
|
||||
$this->line(sprintf('Now executing %s', $command));
|
||||
|
@@ -58,6 +58,38 @@ class RestoreOAuthKeys extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function generateKeys(): void
|
||||
{
|
||||
OAuthKeys::generateKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function keysInDatabase(): bool
|
||||
{
|
||||
return OAuthKeys::keysInDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function keysOnDrive(): bool
|
||||
{
|
||||
return OAuthKeys::hasKeyFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function restoreKeysFromDB(): bool
|
||||
{
|
||||
return OAuthKeys::restoreKeysFromDB();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -97,30 +129,6 @@ class RestoreOAuthKeys extends Command
|
||||
$this->line('OAuth keys are OK');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function keysInDatabase(): bool
|
||||
{
|
||||
return OAuthKeys::keysInDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function keysOnDrive(): bool
|
||||
{
|
||||
return OAuthKeys::hasKeyFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function generateKeys(): void
|
||||
{
|
||||
OAuthKeys::generateKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -128,12 +136,4 @@ class RestoreOAuthKeys extends Command
|
||||
{
|
||||
OAuthKeys::storeKeysInDB();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function restoreKeysFromDB(): bool
|
||||
{
|
||||
return OAuthKeys::restoreKeysFromDB();
|
||||
}
|
||||
}
|
||||
|
@@ -25,35 +25,62 @@ namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AutoBudget;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\PiggyBankRepetition;
|
||||
use FireflyIII\Models\RecurrenceTransaction;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class ForceDecimalSize
|
||||
*
|
||||
* This command was inspired by https://github.com/elliot-gh. It will check all amount fields
|
||||
* and their values and correct them to the correct number of decimal places. This fixes issues where
|
||||
* Firefly III would store 0.01 as 0.01000000000000000020816681711721685132943093776702880859375.
|
||||
*/
|
||||
class ForceDecimalSize extends Command
|
||||
{
|
||||
use VerifiesAccessToken;
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:force-decimal-size
|
||||
|
||||
protected $description = 'This command resizes DECIMAL columns in MySQL or PostgreSQL and correct amounts (only MySQL).';
|
||||
protected $signature = 'firefly-iii:force-decimal-size
|
||||
{--user=1 : The user ID.}
|
||||
{--token= : The user\'s access token.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This command resizes DECIMAL columns in MySQL or PostgreSQL.';
|
||||
private array $decimals = [];
|
||||
private array $tables = [
|
||||
'accounts' => ['virtual_balance'],
|
||||
'auto_budgets' => ['amount'],
|
||||
'available_budgets' => ['amount'],
|
||||
'bills' => ['amount_min', 'amount_max'],
|
||||
'budget_limits' => ['amount'],
|
||||
'currency_exchange_rates' => ['rate', 'user_rate'],
|
||||
'limit_repetitions' => ['amount'],
|
||||
'piggy_bank_events' => ['amount'],
|
||||
'piggy_bank_repetitions' => ['currentamount'],
|
||||
'piggy_banks' => ['targetamount'],
|
||||
'recurrences_transactions' => ['amount', 'foreign_amount'],
|
||||
'transactions' => ['amount', 'foreign_amount'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
Log::debug('Now in ForceDecimalSize::handle()');
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->error('Invalid access token.');
|
||||
|
||||
@@ -66,37 +93,560 @@ class ForceDecimalSize extends Command
|
||||
if (true === $question) {
|
||||
$user = $this->getUser();
|
||||
Log::channel('audit')->info(sprintf('User #%d ("%s") forced DECIMAL size.', $user->id, $user->email));
|
||||
$this->correctAmounts();
|
||||
$this->updateDecimals();
|
||||
return 0;
|
||||
}
|
||||
$this->line('Done!');
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function updateDecimals(): void
|
||||
/**
|
||||
* This method loops over all accounts and validates the amounts.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctAccountAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
$this->info('Going to force the size of DECIMAL columns. Please hold.');
|
||||
$tables = [
|
||||
'accounts' => ['virtual_balance'],
|
||||
'auto_budgets' => ['amount'],
|
||||
'available_budgets' => ['amount'],
|
||||
'bills' => ['amount_min', 'amount_max'],
|
||||
'budget_limits' => ['amount'],
|
||||
'currency_exchange_rates' => ['rate', 'user_rate'],
|
||||
'limit_repetitions' => ['amount'],
|
||||
'piggy_bank_events' => ['amount'],
|
||||
'piggy_bank_repetitions' => ['currentamount'],
|
||||
'piggy_banks' => ['targetamount'],
|
||||
'recurrences_transactions' => ['amount', 'foreign_amount'],
|
||||
'transactions' => ['amount', 'foreign_amount'],
|
||||
];
|
||||
/** @var Builder $query */
|
||||
$query = Account::leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
|
||||
->where('account_meta.name', 'currency_id')
|
||||
->where('account_meta.data', json_encode((string)$currency->id));
|
||||
$query->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(accounts.%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['accounts.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All accounts in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var Account $account */
|
||||
foreach ($result as $account) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $account->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Account #%d has %s with value "%s", this has been corrected to "%s".', $account->id, $field, $value, $correct));
|
||||
Account::find($account->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a basic check can be done or if it needs to be complicated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctAmounts(): void
|
||||
{
|
||||
if ('mysql' !== config('database.default')) {
|
||||
$this->line('Skip correcting amounts...');
|
||||
return;
|
||||
}
|
||||
$this->correctAmountsByCurrency();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all auto budgets in currency $currency.
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctAutoBudgetAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = AutoBudget::where('transaction_currency_id', $currency->id)->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All auto budgets in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var AutoBudget $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Auto budget #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
AutoBudget::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loops all enabled currencies and then calls the method that will fix all objects in this currency.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctAmountsByCurrency(): void
|
||||
{
|
||||
$this->line('Going to correct amounts, using the SLOW lane.');
|
||||
/** @var Collection $enabled */
|
||||
$enabled = TransactionCurrency::whereEnabled(1)->get();
|
||||
/** @var TransactionCurrency $currency */
|
||||
foreach ($enabled as $currency) {
|
||||
$this->correctByCurrency($currency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all available budgets in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctAvailableBudgetAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = AvailableBudget::where('transaction_currency_id', $currency->id)->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All available budgets in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var AvailableBudget $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Available budget #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
AvailableBudget::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all bills in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctBillAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = Bill::where('transaction_currency_id', $currency->id)->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All bills in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var Bill $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Bill #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
Bill::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all budget limits in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctBudgetLimitAmounts(TransactionCurrency $currency, array $fields)
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = BudgetLimit::where('transaction_currency_id', $currency->id)->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All budget limits in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var BudgetLimit $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Budget limit #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
BudgetLimit::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loops the available tables that may need fixing, and calls for the right method that can fix them.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @return void
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function correctByCurrency(TransactionCurrency $currency): void
|
||||
{
|
||||
$this->line(sprintf('Going to correct amounts in currency %s ("%s").', $currency->code, $currency->name));
|
||||
/**
|
||||
* @var string $name
|
||||
* @var array $fields
|
||||
*/
|
||||
foreach($tables as $name => $fields) {
|
||||
foreach ($this->tables as $name => $fields) {
|
||||
switch ($name) {
|
||||
default:
|
||||
$message = sprintf('Cannot handle table "%s"', $name);
|
||||
$this->line($message);
|
||||
throw new FireflyException($message);
|
||||
break;
|
||||
case 'accounts':
|
||||
$this->correctAccountAmounts($currency, $fields);
|
||||
break;
|
||||
case 'auto_budgets':
|
||||
$this->correctAutoBudgetAmounts($currency, $fields);
|
||||
break;
|
||||
case 'available_budgets':
|
||||
$this->correctAvailableBudgetAmounts($currency, $fields);
|
||||
break;
|
||||
case 'bills':
|
||||
$this->correctBillAmounts($currency, $fields);
|
||||
break;
|
||||
case 'budget_limits':
|
||||
$this->correctBudgetLimitAmounts($currency, $fields);
|
||||
break;
|
||||
case 'currency_exchange_rates':
|
||||
case 'limit_repetitions':
|
||||
// do nothing
|
||||
break;
|
||||
case 'piggy_bank_events':
|
||||
$this->correctPiggyEventAmounts($currency, $fields);
|
||||
break;
|
||||
case 'piggy_bank_repetitions':
|
||||
$this->correctPiggyRepetitionAmounts($currency, $fields);
|
||||
break;
|
||||
case 'piggy_banks':
|
||||
$this->correctPiggyAmounts($currency, $fields);
|
||||
break;
|
||||
case 'recurrences_transactions':
|
||||
$this->correctRecurringTransactionAmounts($currency, $fields);
|
||||
break;
|
||||
case 'transactions':
|
||||
$this->correctTransactionAmounts($currency);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all piggy banks in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctPiggyAmounts(TransactionCurrency $currency, array $fields)
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = PiggyBank::leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id')
|
||||
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
|
||||
->where('account_meta.name', 'currency_id')
|
||||
->where('account_meta.data', json_encode((string)$currency->id))
|
||||
->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(piggy_banks.%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['piggy_banks.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All piggy banks in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBank $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Piggy bank #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
PiggyBank::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all piggy bank events in currency $currency.
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctPiggyEventAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = PiggyBankEvent::leftJoin('piggy_banks', 'piggy_bank_events.piggy_bank_id', '=', 'piggy_banks.id')
|
||||
->leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id')
|
||||
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
|
||||
->where('account_meta.name', 'currency_id')
|
||||
->where('account_meta.data', json_encode((string)$currency->id))
|
||||
->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(piggy_bank_events.%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['piggy_bank_events.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All piggy bank events in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBankEvent $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Piggy bank event #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
PiggyBankEvent::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all piggy bank repetitions in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctPiggyRepetitionAmounts(TransactionCurrency $currency, array $fields)
|
||||
{
|
||||
// select all piggy bank repetitions with this currency and issue.
|
||||
/** @var Builder $query */
|
||||
$query = PiggyBankRepetition::leftJoin('piggy_banks', 'piggy_bank_repetitions.piggy_bank_id', '=', 'piggy_banks.id')
|
||||
->leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id')
|
||||
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
|
||||
->where('account_meta.name', 'currency_id')
|
||||
->where('account_meta.data', json_encode((string)$currency->id))
|
||||
->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(piggy_bank_repetitions.%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['piggy_bank_repetitions.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All piggy bank repetitions in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBankRepetition $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Piggy bank repetition #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
PiggyBankRepetition::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all recurring transactions in currency $currency.
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
private function correctRecurringTransactionAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = RecurrenceTransaction::where('transaction_currency_id', $currency->id)->where(static function (Builder $q) use ($fields, $currency) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(%s AS CHAR)', $field)),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All recurring transactions in %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var RecurrenceTransaction $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Recurring transaction #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
RecurrenceTransaction::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all transactions in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @return void
|
||||
*/
|
||||
private function correctTransactionAmounts(TransactionCurrency $currency): void
|
||||
{
|
||||
// select all transactions with this currency and issue.
|
||||
/** @var Builder $query */
|
||||
$query = Transaction::where('transaction_currency_id', $currency->id)->where(
|
||||
DB::raw('CAST(amount AS CHAR)'),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All transactions in %s', $currency->code));
|
||||
}
|
||||
/** @var Transaction $item */
|
||||
foreach ($result as $item) {
|
||||
$value = $item->amount;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct));
|
||||
Transaction::find($item->id)->update(['amount' => $correct]);
|
||||
}
|
||||
|
||||
// select all transactions with this FOREIGN currency and issue.
|
||||
/** @var Builder $query */
|
||||
$query = Transaction::where('foreign_currency_id', $currency->id)->where(
|
||||
DB::raw('CAST(foreign_amount AS CHAR)'),
|
||||
'REGEXP',
|
||||
DB::raw(sprintf('\'\\\\.[\\\\d]{%d}[1-9]+\'', $currency->decimal_places))
|
||||
);
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All transactions in foreign currency %s', $currency->code));
|
||||
return;
|
||||
}
|
||||
/** @var Transaction $item */
|
||||
foreach ($result as $item) {
|
||||
$value = $item->foreign_amount;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct));
|
||||
Transaction::find($item->id)->update(['foreign_amount' => $correct]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function updateDecimals(): void
|
||||
{
|
||||
$this->info('Going to force the size of DECIMAL columns. Please hold.');
|
||||
|
||||
/**
|
||||
* @var string $name
|
||||
* @var array $fields
|
||||
*/
|
||||
foreach ($this->tables as $name => $fields) {
|
||||
/** @var string $field */
|
||||
foreach($fields as $field) {
|
||||
foreach ($fields as $field) {
|
||||
$this->line(sprintf('Updating table "%s", field "%s"...', $name, $field));
|
||||
$query = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s DECIMAL(32, 12);', $name, $field, $field);
|
||||
DB::select($query);
|
||||
|
@@ -35,6 +35,12 @@ class ForceMigration extends Command
|
||||
{
|
||||
use VerifiesAccessToken;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This command will force-run all database migrations.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
@@ -44,13 +50,6 @@ class ForceMigration extends Command
|
||||
{--user=1 : The user ID.}
|
||||
{--token= : The user\'s access token.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This command will force-run all database migrations.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* @throws FireflyException
|
||||
|
@@ -66,52 +66,6 @@ class UpgradeFireflyInstructions extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render upgrade instructions.
|
||||
*/
|
||||
private function updateInstructions(): void
|
||||
{
|
||||
/** @var string $version */
|
||||
$version = config('firefly.version');
|
||||
$config = config('upgrade.text.upgrade');
|
||||
$text = '';
|
||||
foreach (array_keys($config) as $compare) {
|
||||
// if string starts with:
|
||||
if (\str_starts_with($version, $compare)) {
|
||||
$text = $config[$compare];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->showLine();
|
||||
$this->boxed('');
|
||||
if (null === $text || '' === $text) {
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
|
||||
$this->boxedInfo('There are no extra upgrade instructions.');
|
||||
$this->boxed('Firefly III should be ready for use.');
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version));
|
||||
$this->boxedInfo($text);
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a line.
|
||||
*/
|
||||
private function showLine(): void
|
||||
{
|
||||
$line = '+';
|
||||
$line .= str_repeat('-', 78);
|
||||
$line .= '+';
|
||||
$this->line($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a nice box.
|
||||
*
|
||||
@@ -170,4 +124,49 @@ class UpgradeFireflyInstructions extends Command
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a line.
|
||||
*/
|
||||
private function showLine(): void
|
||||
{
|
||||
$line = '+';
|
||||
$line .= str_repeat('-', 78);
|
||||
$line .= '+';
|
||||
$this->line($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render upgrade instructions.
|
||||
*/
|
||||
private function updateInstructions(): void
|
||||
{
|
||||
/** @var string $version */
|
||||
$version = config('firefly.version');
|
||||
$config = config('upgrade.text.upgrade');
|
||||
$text = '';
|
||||
foreach (array_keys($config) as $compare) {
|
||||
// if string starts with:
|
||||
if (\str_starts_with($version, $compare)) {
|
||||
$text = $config[$compare];
|
||||
}
|
||||
}
|
||||
|
||||
$this->showLine();
|
||||
$this->boxed('');
|
||||
if (null === $text || '' === $text) {
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
|
||||
$this->boxedInfo('There are no extra upgrade instructions.');
|
||||
$this->boxed('Firefly III should be ready for use.');
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version));
|
||||
$this->boxedInfo($text);
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
}
|
||||
}
|
||||
|
@@ -31,8 +31,8 @@ use FireflyIII\Support\Cronjobs\BillWarningCronjob;
|
||||
use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob;
|
||||
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
||||
use Illuminate\Console\Command;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
@@ -123,62 +123,6 @@ class Cron extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*/
|
||||
private function exchangeRatesCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$exchangeRates = new ExchangeRatesCronjob();
|
||||
$exchangeRates->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$exchangeRates->setDate($date);
|
||||
}
|
||||
|
||||
$exchangeRates->fire();
|
||||
|
||||
if ($exchangeRates->jobErrored) {
|
||||
$this->error(sprintf('Error in "exchange rates" cron: %s', $exchangeRates->message));
|
||||
}
|
||||
if ($exchangeRates->jobFired) {
|
||||
$this->line(sprintf('"Exchange rates" cron fired: %s', $exchangeRates->message));
|
||||
}
|
||||
if ($exchangeRates->jobSucceeded) {
|
||||
$this->info(sprintf('"Exchange rates" cron ran with success: %s', $exchangeRates->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws FireflyException
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
$recurring->fire();
|
||||
if ($recurring->jobErrored) {
|
||||
$this->error(sprintf('Error in "create recurring transactions" cron: %s', $recurring->message));
|
||||
}
|
||||
if ($recurring->jobFired) {
|
||||
$this->line(sprintf('"Create recurring transactions" cron fired: %s', $recurring->message));
|
||||
}
|
||||
if ($recurring->jobSucceeded) {
|
||||
$this->info(sprintf('"Create recurring transactions" cron ran with success: %s', $recurring->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
@@ -234,4 +178,60 @@ class Cron extends Command
|
||||
$this->info(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*/
|
||||
private function exchangeRatesCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$exchangeRates = new ExchangeRatesCronjob();
|
||||
$exchangeRates->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$exchangeRates->setDate($date);
|
||||
}
|
||||
|
||||
$exchangeRates->fire();
|
||||
|
||||
if ($exchangeRates->jobErrored) {
|
||||
$this->error(sprintf('Error in "exchange rates" cron: %s', $exchangeRates->message));
|
||||
}
|
||||
if ($exchangeRates->jobFired) {
|
||||
$this->line(sprintf('"Exchange rates" cron fired: %s', $exchangeRates->message));
|
||||
}
|
||||
if ($exchangeRates->jobSucceeded) {
|
||||
$this->info(sprintf('"Exchange rates" cron ran with success: %s', $exchangeRates->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws FireflyException
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
$recurring->fire();
|
||||
if ($recurring->jobErrored) {
|
||||
$this->error(sprintf('Error in "create recurring transactions" cron: %s', $recurring->message));
|
||||
}
|
||||
if ($recurring->jobFired) {
|
||||
$this->line(sprintf('"Create recurring transactions" cron fired: %s', $recurring->message));
|
||||
}
|
||||
if ($recurring->jobSucceeded) {
|
||||
$this->info(sprintf('"Create recurring transactions" cron ran with success: %s', $recurring->message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -96,20 +96,6 @@ class AccountCurrencies extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -128,50 +114,23 @@ class AccountCurrencies extends Command
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function updateAccountCurrencies(): void
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
Log::debug('Now in updateAccountCurrencies()');
|
||||
$users = $this->userRepos->all();
|
||||
$defaultCurrencyCode = (string)config('firefly.default_currency', 'EUR');
|
||||
Log::debug(sprintf('Default currency is %s', $defaultCurrencyCode));
|
||||
foreach ($users as $user) {
|
||||
$this->updateCurrenciesForUser($user, $defaultCurrencyCode);
|
||||
}
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param string $systemCurrencyCode
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @throws FireflyException
|
||||
|
||||
*/
|
||||
private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void
|
||||
private function stupidLaravel(): 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|null $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);
|
||||
}
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,8 +196,49 @@ class AccountCurrencies extends Command
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
private function updateAccountCurrencies(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
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
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
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|null $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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -73,30 +73,6 @@ class AppendBudgetLimitPeriods extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function theresNoLimit(): void
|
||||
{
|
||||
$limits = BudgetLimit::whereNull('period')->get();
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($limits as $limit) {
|
||||
$this->fixLimit($limit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $limit
|
||||
*/
|
||||
@@ -182,6 +158,18 @@ class AppendBudgetLimitPeriods extends Command
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -189,4 +177,16 @@ class AppendBudgetLimitPeriods extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function theresNoLimit(): void
|
||||
{
|
||||
$limits = BudgetLimit::whereNull('period')->get();
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($limits as $limit) {
|
||||
$this->fixLimit($limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -87,15 +87,39 @@ class BackToJournals extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @return array
|
||||
*/
|
||||
private function isMigrated(): bool
|
||||
private function getIdsForBudgets(): array
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false);
|
||||
$transactions = DB::table('budget_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')->whereIn('transactions.id', $chunk)->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForCategories(): array
|
||||
{
|
||||
$transactions = DB::table('category_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')
|
||||
->whereIn('transactions.id', $chunk)
|
||||
->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,6 +134,26 @@ class BackToJournals extends Command
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isMigrated(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -143,23 +187,6 @@ class BackToJournals extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForBudgets(): array
|
||||
{
|
||||
$transactions = DB::table('budget_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')->whereIn('transactions.id', $chunk)->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
@@ -217,25 +244,6 @@ class BackToJournals extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForCategories(): array
|
||||
{
|
||||
$transactions = DB::table('category_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')
|
||||
->whereIn('transactions.id', $chunk)
|
||||
->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
@@ -265,12 +273,4 @@ class BackToJournals extends Command
|
||||
$journal->categories()->sync([(int)$category->id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -87,6 +87,81 @@ class DecryptDatabase extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
*/
|
||||
private function decryptField(string $table, string $field): void
|
||||
{
|
||||
$rows = DB::table($table)->get(['id', $field]);
|
||||
/** @var stdClass $row */
|
||||
foreach ($rows as $row) {
|
||||
$this->decryptRow($table, $field, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $value
|
||||
*/
|
||||
private function decryptPreferencesRow(int $id, string $value): void
|
||||
{
|
||||
// try to json_decrypt the value.
|
||||
try {
|
||||
$newValue = json_decode($value, true, 512, JSON_THROW_ON_ERROR) ?? $value;
|
||||
} catch (JsonException $e) {
|
||||
$message = sprintf('Could not JSON decode preference row #%d: %s. This does not have to be a problem.', $id, $e->getMessage());
|
||||
$this->error($message);
|
||||
app('log')->warning($message);
|
||||
app('log')->warning($value);
|
||||
app('log')->warning($e->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Preference $object */
|
||||
$object = Preference::find((int)$id);
|
||||
if (null !== $object) {
|
||||
$object->data = $newValue;
|
||||
$object->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param stdClass $row
|
||||
*/
|
||||
private function decryptRow(string $table, string $field, stdClass $row): void
|
||||
{
|
||||
$original = $row->$field;
|
||||
if (null === $original) {
|
||||
return;
|
||||
}
|
||||
$id = (int)$row->id;
|
||||
$value = '';
|
||||
|
||||
try {
|
||||
$value = $this->tryDecrypt($original);
|
||||
} catch (FireflyException $e) {
|
||||
$message = sprintf('Could not decrypt field "%s" in row #%d of table "%s": %s', $field, $id, $table, $e->getMessage());
|
||||
$this->error($message);
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
|
||||
// A separate routine for preferences table:
|
||||
if ('preferences' === $table) {
|
||||
$this->decryptPreferencesRow($id, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value !== $original) {
|
||||
DB::table($table)->where('id', $id)->update([$field => $value]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param array $fields
|
||||
@@ -132,54 +207,6 @@ class DecryptDatabase extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
*/
|
||||
private function decryptField(string $table, string $field): void
|
||||
{
|
||||
$rows = DB::table($table)->get(['id', $field]);
|
||||
/** @var stdClass $row */
|
||||
foreach ($rows as $row) {
|
||||
$this->decryptRow($table, $field, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param stdClass $row
|
||||
*/
|
||||
private function decryptRow(string $table, string $field, stdClass $row): void
|
||||
{
|
||||
$original = $row->$field;
|
||||
if (null === $original) {
|
||||
return;
|
||||
}
|
||||
$id = (int)$row->id;
|
||||
$value = '';
|
||||
|
||||
try {
|
||||
$value = $this->tryDecrypt($original);
|
||||
} catch (FireflyException $e) {
|
||||
$message = sprintf('Could not decrypt field "%s" in row #%d of table "%s": %s', $field, $id, $table, $e->getMessage());
|
||||
$this->error($message);
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
|
||||
// A separate routine for preferences table:
|
||||
if ('preferences' === $table) {
|
||||
$this->decryptPreferencesRow($id, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value !== $original) {
|
||||
DB::table($table)->where('id', $id)->update([$field => $value]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to decrypt data. Will only throw an exception when the MAC is invalid.
|
||||
*
|
||||
@@ -200,31 +227,4 @@ class DecryptDatabase extends Command
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $value
|
||||
*/
|
||||
private function decryptPreferencesRow(int $id, string $value): void
|
||||
{
|
||||
// try to json_decrypt the value.
|
||||
try {
|
||||
$newValue = json_decode($value, true, 512, JSON_THROW_ON_ERROR) ?? $value;
|
||||
} catch (JsonException $e) {
|
||||
$message = sprintf('Could not JSON decode preference row #%d: %s. This does not have to be a problem.', $id, $e->getMessage());
|
||||
$this->error($message);
|
||||
app('log')->warning($message);
|
||||
app('log')->warning($value);
|
||||
app('log')->warning($e->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Preference $object */
|
||||
$object = Preference::find((int)$id);
|
||||
if (null !== $object) {
|
||||
$object->data = $newValue;
|
||||
$object->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -102,20 +102,11 @@ class MigrateRecurrenceMeta extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws JsonException
|
||||
*
|
||||
*/
|
||||
private function migrateMetaData(): int
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
$count = 0;
|
||||
// get all recurrence meta data:
|
||||
$collection = RecurrenceMeta::with('recurrence')->get();
|
||||
/** @var RecurrenceMeta $meta */
|
||||
foreach ($collection as $meta) {
|
||||
$count += $this->migrateEntry($meta);
|
||||
}
|
||||
|
||||
return $count;
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,10 +146,19 @@ class MigrateRecurrenceMeta extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return int
|
||||
* @throws JsonException
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -78,6 +78,14 @@ class MigrateRecurrenceType extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function getInvalidType(): TransactionType
|
||||
{
|
||||
return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -96,15 +104,9 @@ class MigrateRecurrenceType extends Command
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function migrateTypes(): void
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
$set = Recurrence::get();
|
||||
/** @var Recurrence $recurrence */
|
||||
foreach ($set as $recurrence) {
|
||||
if ($recurrence->transactionType->type !== TransactionType::INVALID) {
|
||||
$this->migrateRecurrence($recurrence);
|
||||
}
|
||||
}
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
private function migrateRecurrence(Recurrence $recurrence): void
|
||||
@@ -124,16 +126,14 @@ class MigrateRecurrenceType extends Command
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function getInvalidType(): TransactionType
|
||||
private function migrateTypes(): void
|
||||
{
|
||||
return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
$set = Recurrence::get();
|
||||
/** @var Recurrence $recurrence */
|
||||
foreach ($set as $recurrence) {
|
||||
if ($recurrence->transactionType->type !== TransactionType::INVALID) {
|
||||
$this->migrateRecurrence($recurrence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -75,6 +75,16 @@ class MigrateTagLocations extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasLocationDetails(Tag $tag): bool
|
||||
{
|
||||
return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -90,25 +100,12 @@ class MigrateTagLocations extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
private function migrateTagLocations(): void
|
||||
{
|
||||
$tags = Tag::get();
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
if ($this->hasLocationDetails($tag)) {
|
||||
$this->migrateLocationDetails($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasLocationDetails(Tag $tag): bool
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel;
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,11 +126,14 @@ class MigrateTagLocations extends Command
|
||||
$tag->save();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
private function migrateTagLocations(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
$tags = Tag::get();
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
if ($this->hasLocationDetails($tag)) {
|
||||
$this->migrateLocationDetails($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -105,22 +105,6 @@ class MigrateToRules extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
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
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -137,38 +121,11 @@ class MigrateToRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate bills to new rule structure for a specific user.
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function migrateUser(User $user): void
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
$this->ruleGroupRepository->setUser($user);
|
||||
$this->billRepository->setUser($user);
|
||||
$this->ruleRepository->setUser($user);
|
||||
|
||||
/** @var Preference $lang */
|
||||
$lang = app('preferences')->getForUser($user, 'language', 'en_US');
|
||||
$groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data);
|
||||
$ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle);
|
||||
|
||||
if (null === $ruleGroup) {
|
||||
$ruleGroup = $this->ruleGroupRepository->store(
|
||||
[
|
||||
'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data),
|
||||
'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data),
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$bills = $this->billRepository->getBills();
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$this->migrateBill($ruleGroup, $bill, $lang);
|
||||
}
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,10 +200,53 @@ class MigrateToRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@@ -107,63 +107,6 @@ class TransactionIdentifier extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param TransactionJournal $transactionJournal
|
||||
*/
|
||||
private function updateJournalIdentifiers(TransactionJournal $transactionJournal): void
|
||||
{
|
||||
$identifier = 0;
|
||||
$exclude = []; // transactions already processed.
|
||||
$transactions = $transactionJournal->transactions()->where('amount', '>', 0)->get();
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$opposing = $this->findOpposing($transaction, $exclude);
|
||||
if (null !== $opposing) {
|
||||
// give both a new identifier:
|
||||
$transaction->identifier = $identifier;
|
||||
$opposing->identifier = $identifier;
|
||||
$transaction->save();
|
||||
$opposing->save();
|
||||
$exclude[] = $transaction->id;
|
||||
$exclude[] = $opposing->id;
|
||||
$this->count++;
|
||||
}
|
||||
++$identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param array $exclude
|
||||
@@ -194,6 +137,21 @@ class TransactionIdentifier extends Command
|
||||
return $opposing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -201,4 +159,46 @@ class TransactionIdentifier extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param TransactionJournal $transactionJournal
|
||||
*/
|
||||
private function updateJournalIdentifiers(TransactionJournal $transactionJournal): void
|
||||
{
|
||||
$identifier = 0;
|
||||
$exclude = []; // transactions already processed.
|
||||
$transactions = $transactionJournal->transactions()->where('amount', '>', 0)->get();
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$opposing = $this->findOpposing($transaction, $exclude);
|
||||
if (null !== $opposing) {
|
||||
// give both a new identifier:
|
||||
$transaction->identifier = $identifier;
|
||||
$opposing->identifier = $identifier;
|
||||
$transaction->save();
|
||||
$opposing->save();
|
||||
$exclude[] = $transaction->id;
|
||||
$exclude[] = $opposing->id;
|
||||
$this->count++;
|
||||
}
|
||||
++$identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ class UpgradeDatabase extends Command
|
||||
private function callInitialCommands(): void
|
||||
{
|
||||
$this->line('Now seeding the database...');
|
||||
$this->call('migrate', ['--seed' => true, '--force' => true,'--no-interaction' => true]);
|
||||
$this->call('migrate', ['--seed' => true, '--force' => true, '--no-interaction' => true]);
|
||||
|
||||
$this->line('Fix PostgreSQL sequences.');
|
||||
$this->call('firefly-iii:fix-pgsql-sequences');
|
||||
|
@@ -82,79 +82,6 @@ class UpgradeLiabilities extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
Log::debug('Upgrading liabilities.');
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
Log::debug(sprintf('Upgrading liabilities for user #%d', $user->id));
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
Log::debug(sprintf('Upgrade liability #%d', $account->id));
|
||||
|
||||
// get opening balance, and correct if necessary.
|
||||
$openingBalance = $repository->getOpeningBalance($account);
|
||||
if (null !== $openingBalance) {
|
||||
// correct if necessary
|
||||
$this->correctOpeningBalance($account, $openingBalance);
|
||||
}
|
||||
|
||||
// add liability direction property (if it does not yet exist!)
|
||||
$value = $repository->getMetaValue($account, 'liability_direction');
|
||||
if (null === $value) {
|
||||
/** @var AccountMetaFactory $factory */
|
||||
$factory = app(AccountMetaFactory::class);
|
||||
$factory->crud($account, 'liability_direction', 'debit');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionJournal $openingBalance
|
||||
@@ -185,9 +112,9 @@ class UpgradeLiabilities extends Command
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $journal): ?Transaction
|
||||
private function getDestinationTransaction(TransactionJournal $journal): ?Transaction
|
||||
{
|
||||
return $journal->transactions()->where('amount', '<', 0)->first();
|
||||
return $journal->transactions()->where('amount', '>', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,9 +122,24 @@ class UpgradeLiabilities extends Command
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getDestinationTransaction(TransactionJournal $journal): ?Transaction
|
||||
private function getSourceTransaction(TransactionJournal $journal): ?Transaction
|
||||
{
|
||||
return $journal->transactions()->where('amount', '>', 0)->first();
|
||||
return $journal->transactions()->where('amount', '<', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,4 +149,62 @@ class UpgradeLiabilities extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
Log::debug(sprintf('Upgrading liabilities for user #%d', $user->id));
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
Log::debug('Upgrading liabilities.');
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
Log::debug(sprintf('Upgrade liability #%d', $account->id));
|
||||
|
||||
// get opening balance, and correct if necessary.
|
||||
$openingBalance = $repository->getOpeningBalance($account);
|
||||
if (null !== $openingBalance) {
|
||||
// correct if necessary
|
||||
$this->correctOpeningBalance($account, $openingBalance);
|
||||
}
|
||||
|
||||
// add liability direction property (if it does not yet exist!)
|
||||
$value = $repository->getMetaValue($account, 'liability_direction');
|
||||
if (null === $value) {
|
||||
/** @var AccountMetaFactory $factory */
|
||||
$factory = app(AccountMetaFactory::class);
|
||||
$factory->crud($account, 'liability_direction', 'debit');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -83,115 +83,6 @@ class UpgradeLiabilitiesEight extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
Log::debug('Upgrading liabilities.');
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
Log::debug(sprintf('Upgrading liabilities for user #%d', $user->id));
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
Log::debug(sprintf('Upgrade liability #%d ("%s")', $account->id, $account->name));
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
|
||||
$direction = $repository->getMetaValue($account, 'liability_direction');
|
||||
if ('debit' === $direction) {
|
||||
Log::debug('Direction is debit ("I owe this amount"), so no need to upgrade.');
|
||||
}
|
||||
if ('credit' === $direction && $this->hasBadOpening($account)) {
|
||||
$this->deleteCreditTransaction($account);
|
||||
$this->reverseOpeningBalance($account);
|
||||
$this->line(sprintf('Corrected opening balance for liability #%d ("%s")', $account->id, $account->name));
|
||||
}
|
||||
if ('credit' === $direction) {
|
||||
$count = $this->deleteTransactions($account);
|
||||
if ($count > 0) {
|
||||
$this->line(sprintf('Removed %d old format transaction(s) for liability #%d ("%s")', $count, $account->id, $account->name));
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Done upgrading liability #%d ("%s")', $account->id, $account->name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return bool
|
||||
*/
|
||||
private function hasBadOpening(Account $account): bool
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
$liabilityType = TransactionType::whereType(TransactionType::LIABILITY_CREDIT)->first();
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $openingJournal) {
|
||||
Log::debug('Account has no opening balance and can be skipped.');
|
||||
return false;
|
||||
}
|
||||
$liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $liabilityType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $liabilityJournal) {
|
||||
Log::debug('Account has no liability credit and can be skipped.');
|
||||
return false;
|
||||
}
|
||||
if (!$openingJournal->date->isSameDay($liabilityJournal->date)) {
|
||||
Log::debug('Account has opening/credit not on the same day.');
|
||||
return false;
|
||||
}
|
||||
Log::debug('Account has bad opening balance data.');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return void
|
||||
@@ -214,35 +105,6 @@ class UpgradeLiabilitiesEight extends Command
|
||||
Log::debug('No liability credit journal found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return void
|
||||
*/
|
||||
private function reverseOpeningBalance(Account $account): void
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
/** @var TransactionJournal $openingJournal */
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
/** @var Transaction|null $source */
|
||||
$source = $openingJournal->transactions()->where('amount', '<', 0)->first();
|
||||
/** @var Transaction|null $dest */
|
||||
$dest = $openingJournal->transactions()->where('amount', '>', 0)->first();
|
||||
if ($source && $dest) {
|
||||
$sourceId = $source->account_id;
|
||||
$destId = $dest->account_id;
|
||||
$dest->account_id = $sourceId;
|
||||
$source->account_id = $destId;
|
||||
$source->save();
|
||||
$dest->save();
|
||||
Log::debug(sprintf('Opening balance transaction journal #%d reversed.', $openingJournal->id));
|
||||
return;
|
||||
}
|
||||
Log::warning('Did not find opening balance.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $account
|
||||
* @return int
|
||||
@@ -293,6 +155,54 @@ class UpgradeLiabilitiesEight extends Command
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return bool
|
||||
*/
|
||||
private function hasBadOpening(Account $account): bool
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
$liabilityType = TransactionType::whereType(TransactionType::LIABILITY_CREDIT)->first();
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $openingJournal) {
|
||||
Log::debug('Account has no opening balance and can be skipped.');
|
||||
return false;
|
||||
}
|
||||
$liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $liabilityType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $liabilityJournal) {
|
||||
Log::debug('Account has no liability credit and can be skipped.');
|
||||
return false;
|
||||
}
|
||||
if (!$openingJournal->date->isSameDay($liabilityJournal->date)) {
|
||||
Log::debug('Account has opening/credit not on the same day.');
|
||||
return false;
|
||||
}
|
||||
Log::debug('Account has bad opening balance data.');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -300,4 +210,94 @@ class UpgradeLiabilitiesEight extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @return void
|
||||
*/
|
||||
private function reverseOpeningBalance(Account $account): void
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
/** @var TransactionJournal $openingJournal */
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
/** @var Transaction|null $source */
|
||||
$source = $openingJournal->transactions()->where('amount', '<', 0)->first();
|
||||
/** @var Transaction|null $dest */
|
||||
$dest = $openingJournal->transactions()->where('amount', '>', 0)->first();
|
||||
if ($source && $dest) {
|
||||
$sourceId = $source->account_id;
|
||||
$destId = $dest->account_id;
|
||||
$dest->account_id = $sourceId;
|
||||
$source->account_id = $destId;
|
||||
$source->save();
|
||||
$dest->save();
|
||||
Log::debug(sprintf('Opening balance transaction journal #%d reversed.', $openingJournal->id));
|
||||
return;
|
||||
}
|
||||
Log::warning('Did not find opening balance.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
Log::debug(sprintf('Upgrading liabilities for user #%d', $user->id));
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
Log::debug('Upgrading liabilities.');
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
Log::debug(sprintf('Upgrade liability #%d ("%s")', $account->id, $account->name));
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
|
||||
$direction = $repository->getMetaValue($account, 'liability_direction');
|
||||
if ('debit' === $direction) {
|
||||
Log::debug('Direction is debit ("I owe this amount"), so no need to upgrade.');
|
||||
}
|
||||
if ('credit' === $direction && $this->hasBadOpening($account)) {
|
||||
$this->deleteCreditTransaction($account);
|
||||
$this->reverseOpeningBalance($account);
|
||||
$this->line(sprintf('Corrected opening balance for liability #%d ("%s")', $account->id, $account->name));
|
||||
}
|
||||
if ('credit' === $direction) {
|
||||
$count = $this->deleteTransactions($account);
|
||||
if ($count > 0) {
|
||||
$this->line(sprintf('Removed %d old format transaction(s) for liability #%d ("%s")', $count, $account->id, $account->name));
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Done upgrading liability #%d ("%s")', $account->id, $account->name));
|
||||
}
|
||||
}
|
||||
|
@@ -146,7 +146,7 @@ class BudgetLimitHandler
|
||||
{
|
||||
if(0 === (int)$budgetLimit->id) {
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
$limitPeriod = Period::make(
|
||||
$budgetLimit->start_date,
|
||||
$budgetLimit->end_date,
|
||||
@@ -217,10 +217,10 @@ class BudgetLimitHandler
|
||||
if ($currentPeriod->equals($limitPeriod)) {
|
||||
$amount = 0 === (int)$budgetLimit->id ? '0' : $budgetLimit->amount;
|
||||
}
|
||||
if(0 === bccomp($amount,'0')) {
|
||||
if(0 === bccomp($amount, '0')) {
|
||||
Log::debug('Amount is zero, will not create AB.');
|
||||
}
|
||||
if(0 !== bccomp($amount,'0')) {
|
||||
if(0 !== bccomp($amount, '0')) {
|
||||
Log::debug(sprintf('Will create AB for period %s to %s', $current->format('Y-m-d'), $currentEnd->format('Y-m-d')));
|
||||
$availableBudget = new AvailableBudget(
|
||||
[
|
||||
|
@@ -70,6 +70,8 @@ class AutoBudget extends Model
|
||||
public const AUTO_BUDGET_RESET = 1;
|
||||
public const AUTO_BUDGET_ROLLOVER = 2;
|
||||
|
||||
protected $fillable = ['budget_id','amount','period'];
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
|
@@ -273,6 +273,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
if (10 === strlen($string)) {
|
||||
return Carbon::createFromFormat('Y-m-d', $string, config('app.timezone'));
|
||||
}
|
||||
return Carbon::createFromFormat('Y-m-d H:i:s', $string, config('app.timezone'));
|
||||
// 2022-01-01 01:01:01
|
||||
return Carbon::createFromFormat('Y-m-d H:i:s', substr($string, 0, 19), config('app.timezone'));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user