diff --git a/app/Console/Commands/Correction/CorrectDatabase.php b/app/Console/Commands/Correction/CorrectDatabase.php index b3c5e8c1e3..906d3beeb6 100644 --- a/app/Console/Commands/Correction/CorrectDatabase.php +++ b/app/Console/Commands/Correction/CorrectDatabase.php @@ -70,6 +70,7 @@ class CorrectDatabase extends Command 'firefly-iii:delete-empty-journals', 'firefly-iii:delete-empty-groups', 'firefly-iii:fix-account-types', + 'firefly-iii:fix-account-order', 'firefly-iii:rename-meta-fields', 'firefly-iii:fix-ob-currencies', 'firefly-iii:fix-long-descriptions', diff --git a/app/Console/Commands/Correction/FixAccountOrder.php b/app/Console/Commands/Correction/FixAccountOrder.php new file mode 100644 index 0000000000..9b93a6d6a8 --- /dev/null +++ b/app/Console/Commands/Correction/FixAccountOrder.php @@ -0,0 +1,94 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Console\Commands\Correction; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\User; +use Illuminate\Console\Command; + +/** + * Class FixAccountOrder + */ +class FixAccountOrder extends Command +{ + /** + * The console command description. + * + * @var string + */ + protected $description = 'Make sure account order is correct.'; + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'firefly-iii:fix-account-order'; + + private AccountRepositoryInterface $repository; + + /** + * Execute the console command. + * + * @throws FireflyException + * @return int + */ + public function handle(): int + { + $this->stupidLaravel(); + $start = microtime(true); + + $users = User::get(); + foreach ($users as $user) { + $this->repository->setUser($user); + $sets = [ + [AccountType::DEFAULT, AccountType::ASSET], + [AccountType::EXPENSE, AccountType::BENEFICIARY], + [AccountType::REVENUE], + [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE], + ]; + foreach ($sets as $set) { + $this->repository->resetAccountOrder($set); + } + } + + $end = round(microtime(true) - $start, 2); + $this->info(sprintf('Verifying account order took %s seconds', $end)); + + return 0; + } + + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->repository = app(AccountRepositoryInterface::class); + } +} diff --git a/app/Console/Commands/Upgrade/UpgradeDatabase.php b/app/Console/Commands/Upgrade/UpgradeDatabase.php index d1f744eedf..73a8a0d888 100644 --- a/app/Console/Commands/Upgrade/UpgradeDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradeDatabase.php @@ -89,6 +89,7 @@ class UpgradeDatabase extends Command 'firefly-iii:delete-empty-journals', 'firefly-iii:delete-empty-groups', 'firefly-iii:fix-account-types', + 'firefly-iii:fix-account-order', 'firefly-iii:rename-meta-fields', 'firefly-iii:fix-ob-currencies', 'firefly-iii:fix-long-descriptions', diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index c4e8254f0c..201bf61bd6 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -32,6 +32,7 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\View\View; +use Exception; /** * @@ -129,6 +130,7 @@ class IndexController extends Controller * @param Request $request * @param string $objectType * + * @throws Exception * @return Factory|View */ public function index(Request $request, string $objectType) @@ -138,10 +140,17 @@ class IndexController extends Controller return $this->emptyIndex($objectType); } - $objectType = $objectType ?? 'asset'; - $subTitle = (string) trans(sprintf('firefly.%s_accounts', $objectType)); - $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType)); - $types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType)); + // reset account order: + + $objectType = $objectType ?? 'asset'; + $subTitle = (string) trans(sprintf('firefly.%s_accounts', $objectType)); + $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType)); + $types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType)); + + if (1 === random_int(0, 20)) { + $this->repository->resetAccountOrder($types); + } + $collection = $this->repository->getActiveAccountsByType($types); $total = $collection->count(); $page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page'); diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index c2f2276f4c..028196155a 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -71,7 +71,7 @@ class InstallController extends Controller 'firefly-iii:restore-oauth-keys' => [], 'generate-keys' => [], // an exception :( - // there are 14 upgrade commands. + // upgrade commands 'firefly-iii:transaction-identifiers' => [], 'firefly-iii:migrate-to-groups' => [], 'firefly-iii:account-currencies' => [], @@ -87,7 +87,7 @@ class InstallController extends Controller 'firefly-iii:migrate-recurrence-meta' => [], 'firefly-iii:migrate-tag-locations' => [], - // there are 16 verify commands. + // verify commands 'firefly-iii:fix-piggies' => [], 'firefly-iii:create-link-types' => [], 'firefly-iii:create-access-tokens' => [], @@ -100,6 +100,7 @@ class InstallController extends Controller 'firefly-iii:delete-empty-journals' => [], 'firefly-iii:delete-empty-groups' => [], 'firefly-iii:fix-account-types' => [], + 'firefly-iii:fix-account-order' => [], 'firefly-iii:rename-meta-fields' => [], 'firefly-iii:fix-ob-currencies' => [], 'firefly-iii:fix-long-descriptions' => [], diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index f00974fd23..f1bd4621da 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -697,4 +697,20 @@ class AccountRepository implements AccountRepositoryInterface return TransactionCurrency::whereIn('id', $currencyIds)->get(); } + + /** + * @inheritDoc + */ + public function resetAccountOrder(array $types): void + { + $list = $this->getAccountsByType($types); + /** + * @var int $index + * @var Account $account + */ + foreach ($list as $index => $account) { + $account->order = $index + 1; + $account->save(); + } + } } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index f5cf03cfef..999cb6c5dd 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -47,6 +47,13 @@ interface AccountRepositoryInterface */ public function count(array $types): int; + /** + * Reset order types of the mentioned accounts. + * + * @param array $types + */ + public function resetAccountOrder(array $types): void; + /** * @param Account $account * diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index f91030ddc0..c8f5b2d21a 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -162,6 +162,27 @@ trait AccountServiceTrait return false; } + /** + * Returns true if the data in the array is submitted but empty. + * + * @param array $data + * + * @return bool + */ + public function isEmptyOBData(array $data): bool + { + if (!isset($data['opening_balance']) && !isset($data['opening_balance_date'])) { + // not set, so false. + return false; + } + // if isset, but is empty: + if ('' === $data['opening_balance'] && '' === $data['opening_balance_date']) { + return true; + } + + return false; + } + /** * @param Account $account * @param array $data diff --git a/app/Services/Internal/Update/AccountUpdateService.php b/app/Services/Internal/Update/AccountUpdateService.php index 98ac40418c..f68b29f373 100644 --- a/app/Services/Internal/Update/AccountUpdateService.php +++ b/app/Services/Internal/Update/AccountUpdateService.php @@ -115,12 +115,14 @@ class AccountUpdateService // if it can have a virtual balance, it can also have an opening balance. if (in_array($type->type, $this->canHaveVirtual, true)) { + // check if is submitted as empty, that makes it valid: - if ($this->validOBData($data)) { + + if ($this->validOBData($data) && !$this->isEmptyOBData($data)) { $this->updateOBGroup($account, $data); } - if (!$this->validOBData($data)) { + if (!$this->validOBData($data) && $this->isEmptyOBData($data)) { $this->deleteOBGroup($account); } } diff --git a/app/Support/Http/Controllers/ChartGeneration.php b/app/Support/Http/Controllers/ChartGeneration.php index d59d7c2e6d..e4718da88b 100644 --- a/app/Support/Http/Controllers/ChartGeneration.php +++ b/app/Support/Http/Controllers/ChartGeneration.php @@ -91,7 +91,7 @@ trait ChartGeneration while ($currentStart <= $end) { $format = $currentStart->format('Y-m-d'); $label = trim($currentStart->formatLocalized((string)trans('config.month_and_day', [], $locale))); - $balance = isset($range[$format]) ? round($range[$format], 12) : $previous; + $balance = $range[$format] ?? $previous; $previous = $balance; $currentStart->addDay(); $currentSet['entries'][$label] = $balance; diff --git a/composer.json b/composer.json index 00dc5673e5..12cb09e019 100644 --- a/composer.json +++ b/composer.json @@ -180,6 +180,7 @@ "@php artisan firefly-iii:delete-empty-journals", "@php artisan firefly-iii:delete-empty-groups", "@php artisan firefly-iii:fix-account-types", + "@php artisan firefly-iii:fix-account-order", "@php artisan firefly-iii:rename-meta-fields", "@php artisan firefly-iii:fix-ob-currencies", "@php artisan firefly-iii:fix-long-descriptions",