From 7ca96a766f9067dba8e53bce56f1c4f4a0628e66 Mon Sep 17 00:00:00 2001 From: JC5 Date: Tue, 30 Dec 2025 16:19:05 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20release=20?= =?UTF-8?q?'develop'=20on=202025-12-30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Factory/TransactionJournalMetaFactory.php | 6 +- app/Handlers/Observer/TransactionObserver.php | 12 +-- app/Http/Controllers/HomeController.php | 1 - .../Controllers/PreferencesController.php | 99 ++++++++++--------- .../AttachmentRepositoryInterface.php | 2 - .../Internal/Update/JournalUpdateService.php | 11 ++- .../Models/AccountBalanceCalculator.php | 50 +++++----- config/firefly.php | 4 +- package-lock.json | 12 +-- 9 files changed, 99 insertions(+), 98 deletions(-) diff --git a/app/Factory/TransactionJournalMetaFactory.php b/app/Factory/TransactionJournalMetaFactory.php index 9d2c1f1a80..bef9804fc3 100644 --- a/app/Factory/TransactionJournalMetaFactory.php +++ b/app/Factory/TransactionJournalMetaFactory.php @@ -36,10 +36,10 @@ class TransactionJournalMetaFactory public function updateOrCreate(array $data): ?TransactionJournalMeta { // Log::debug('In updateOrCreate()'); - $value = $data['data']; + $value = $data['data']; /** @var null|TransactionJournalMeta $entry */ - $entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first(); + $entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first(); if (null === $value && null !== $entry) { // Log::debug('Value is empty, delete meta value.'); $entry->delete(); @@ -65,7 +65,7 @@ class TransactionJournalMetaFactory if (null === $entry) { // Log::debug('Will create new object.'); Log::debug(sprintf('Going to create new meta-data entry to store "%s".', $data['name'])); - $entry = new TransactionJournalMeta(); + $entry = new TransactionJournalMeta(); $entry->transactionJournal()->associate($data['journal']); $entry->name = $data['name']; } diff --git a/app/Handlers/Observer/TransactionObserver.php b/app/Handlers/Observer/TransactionObserver.php index b44c56c124..2f542a67c8 100644 --- a/app/Handlers/Observer/TransactionObserver.php +++ b/app/Handlers/Observer/TransactionObserver.php @@ -60,18 +60,18 @@ class TransactionObserver $transaction->native_amount = null; $transaction->native_foreign_amount = null; // first normal amount - if ($transaction->transactionCurrency->id !== $userCurrency->id && - (null === $transaction->foreign_currency_id || - (null !== $transaction->foreign_currency_id && - $transaction->foreign_currency_id !== $userCurrency->id))) { - $converter = new ExchangeRateConverter(); + if ($transaction->transactionCurrency->id !== $userCurrency->id + && (null === $transaction->foreign_currency_id + || (null !== $transaction->foreign_currency_id + && $transaction->foreign_currency_id !== $userCurrency->id))) { + $converter = new ExchangeRateConverter(); $converter->setUserGroup($transaction->transactionJournal->user->userGroup); $converter->setIgnoreSettings(true); $transaction->native_amount = $converter->convert($transaction->transactionCurrency, $userCurrency, $transaction->transactionJournal->date, $transaction->amount); } // then foreign amount if ($transaction->foreignCurrency?->id !== $userCurrency->id && null !== $transaction->foreign_amount && null !== $transaction->foreignCurrency) { - $converter = new ExchangeRateConverter(); + $converter = new ExchangeRateConverter(); $converter->setUserGroup($transaction->transactionJournal->user->userGroup); $converter->setIgnoreSettings(true); $transaction->native_foreign_amount = $converter->convert($transaction->foreignCurrency, $userCurrency, $transaction->transactionJournal->date, $transaction->foreign_amount); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index f96f0e501d..67118821c6 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; -use FireflyIII\Support\Facades\Navigation; use FireflyIII\Support\Facades\Preferences; use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index aca67a7c5e..5f2a3b03a6 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -45,6 +45,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\View\View; use JsonException; use Safe\Exceptions\FilesystemException; + use function Safe\file_get_contents; use function Safe\json_decode; @@ -78,16 +79,16 @@ class PreferencesController extends Controller * @throws FireflyException * @throws FilesystemException */ - public function index(AccountRepositoryInterface $repository): Factory | \Illuminate\Contracts\View\View + public function index(AccountRepositoryInterface $repository): Factory|\Illuminate\Contracts\View\View { - $accounts = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]); - $isDocker = env('IS_DOCKER', false); // @phpstan-ignore-line - $groupedAccounts = []; + $accounts = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]); + $isDocker = env('IS_DOCKER', false); // @phpstan-ignore-line + $groupedAccounts = []; /** @var Account $account */ foreach ($accounts as $account) { - $type = $account->accountType->type; - $role = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role')); + $type = $account->accountType->type; + $role = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role')); if (in_array($type, [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value], true)) { $role = sprintf('opt_group_l_%s', $type); @@ -101,50 +102,50 @@ class PreferencesController extends Controller ksort($groupedAccounts); /** @var array $accountIds */ - $accountIds = $accounts->pluck('id')->toArray(); - $viewRange = Navigation::getViewRange(false); - $frontpageAccountsPref = Preferences::get('frontpageAccounts', $accountIds); - $frontpageAccounts = $frontpageAccountsPref->data; + $accountIds = $accounts->pluck('id')->toArray(); + $viewRange = Navigation::getViewRange(false); + $frontpageAccountsPref = Preferences::get('frontpageAccounts', $accountIds); + $frontpageAccounts = $frontpageAccountsPref->data; if (!is_array($frontpageAccounts)) { $frontpageAccounts = $accountIds; } - $language = Steam::getLanguage(); - $languages = config('firefly.languages'); - $locale = Preferences::get('locale', config('firefly.default_locale', 'equal'))->data; - $listPageSize = Preferences::get('listPageSize', 50)->data; - $darkMode = Preferences::get('darkMode', 'browser')->data; - $customFiscalYear = Preferences::get('customFiscalYear', 0)->data; - $fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data; - $convertToPrimary = $this->convertToPrimary; + $language = Steam::getLanguage(); + $languages = config('firefly.languages'); + $locale = Preferences::get('locale', config('firefly.default_locale', 'equal'))->data; + $listPageSize = Preferences::get('listPageSize', 50)->data; + $darkMode = Preferences::get('darkMode', 'browser')->data; + $customFiscalYear = Preferences::get('customFiscalYear', 0)->data; + $fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data; + $convertToPrimary = $this->convertToPrimary; if (is_array($fiscalYearStartStr)) { $fiscalYearStartStr = '01-01'; } - $fiscalYearStart = sprintf('%s-%s', Carbon::now()->format('Y'), (string)$fiscalYearStartStr); - $tjOptionalFields = Preferences::get('transaction_journal_optional_fields', [])->data; - $availableDarkModes = config('firefly.available_dark_modes'); + $fiscalYearStart = sprintf('%s-%s', Carbon::now()->format('Y'), (string)$fiscalYearStartStr); + $tjOptionalFields = Preferences::get('transaction_journal_optional_fields', [])->data; + $availableDarkModes = config('firefly.available_dark_modes'); // notifications settings - $slackUrl = Preferences::getEncrypted('slack_webhook_url', '')->data; - $pushoverAppToken = (string)Preferences::getEncrypted('pushover_app_token', '')->data; - $pushoverUserToken = (string)Preferences::getEncrypted('pushover_user_token', '')->data; - $ntfyServer = Preferences::getEncrypted('ntfy_server', 'https://ntfy.sh')->data; - $ntfyTopic = (string)Preferences::getEncrypted('ntfy_topic', '')->data; - $ntfyAuth = '1' === Preferences::get('ntfy_auth', false)->data; - $ntfyUser = Preferences::getEncrypted('ntfy_user', '')->data; - $ntfyPass = (string)Preferences::getEncrypted('ntfy_pass', '')->data; - $channels = config('notifications.channels'); - $forcedAvailability = []; - $anonymous = Steam::anonymous(); + $slackUrl = Preferences::getEncrypted('slack_webhook_url', '')->data; + $pushoverAppToken = (string)Preferences::getEncrypted('pushover_app_token', '')->data; + $pushoverUserToken = (string)Preferences::getEncrypted('pushover_user_token', '')->data; + $ntfyServer = Preferences::getEncrypted('ntfy_server', 'https://ntfy.sh')->data; + $ntfyTopic = (string)Preferences::getEncrypted('ntfy_topic', '')->data; + $ntfyAuth = '1' === Preferences::get('ntfy_auth', false)->data; + $ntfyUser = Preferences::getEncrypted('ntfy_user', '')->data; + $ntfyPass = (string)Preferences::getEncrypted('ntfy_pass', '')->data; + $channels = config('notifications.channels'); + $forcedAvailability = []; + $anonymous = Steam::anonymous(); // notification preferences - $notifications = []; + $notifications = []; foreach (config('notifications.notifications.user') as $key => $info) { if (true === $info['enabled']) { $notifications[$key] = [ - 'enabled' => true === Preferences::get(sprintf('notification_%s', $key), true)->data, - 'configurable' => $info['configurable'], - ]; + 'enabled' => true === Preferences::get(sprintf('notification_%s', $key), true)->data, + 'configurable' => $info['configurable'], + ]; } } // loop all channels to see if they are available. @@ -164,7 +165,7 @@ class PreferencesController extends Controller Log::error($e->getMessage()); $locales = []; } - $locales = ['equal' => (string)trans('firefly.equal_to_language')] + $locales; + $locales = ['equal' => (string)trans('firefly.equal_to_language')] + $locales; // an important fallback is that the frontPageAccount array gets refilled automatically // when it turns up empty. if (0 === count($frontpageAccounts)) { @@ -195,7 +196,7 @@ class PreferencesController extends Controller * @SuppressWarnings("PHPMD.ExcessiveMethodLength") * @SuppressWarnings("PHPMD.NPathComplexity") */ - public function postIndex(PreferencesRequest $request): Redirector | RedirectResponse + public function postIndex(PreferencesRequest $request): Redirector|RedirectResponse { // front page accounts $frontpageAccounts = []; @@ -207,7 +208,7 @@ class PreferencesController extends Controller } // extract notifications: - $all = $request->all(); + $all = $request->all(); foreach (config('notifications.notifications.user') as $key => $info) { $key = sprintf('notification_%s', $key); if (array_key_exists($key, $all)) { @@ -241,7 +242,7 @@ class PreferencesController extends Controller } // convert primary - $convertToPrimary = 1 === (int)$request->get('convertToPrimary'); + $convertToPrimary = 1 === (int)$request->get('convertToPrimary'); if ($convertToPrimary && !$this->convertToPrimary) { // set to true! Log::debug('User sets convertToPrimary to true.'); @@ -253,9 +254,9 @@ class PreferencesController extends Controller Preferences::set('convert_to_primary', $convertToPrimary); // custom fiscal year - $customFiscalYear = 1 === (int)$request->get('customFiscalYear'); + $customFiscalYear = 1 === (int)$request->get('customFiscalYear'); Preferences::set('customFiscalYear', $customFiscalYear); - $fiscalYearString = (string)$request->get('fiscalYearStart'); + $fiscalYearString = (string)$request->get('fiscalYearStart'); if ('' !== $fiscalYearString) { $fiscalYearStart = Carbon::parse($fiscalYearString, config('app.timezone'))->format('m-d'); Preferences::set('fiscalYearStart', $fiscalYearStart); @@ -263,15 +264,15 @@ class PreferencesController extends Controller // save page size: Preferences::set('listPageSize', 50); - $listPageSize = (int)$request->get('listPageSize'); + $listPageSize = (int)$request->get('listPageSize'); if ($listPageSize > 0 && $listPageSize < 1337) { Preferences::set('listPageSize', $listPageSize); } // language: /** @var Preference $currentLang */ - $currentLang = Preferences::get('language', 'en_US'); - $lang = $request->get('language'); + $currentLang = Preferences::get('language', 'en_US'); + $lang = $request->get('language'); if (array_key_exists($lang, config('firefly.languages'))) { Preferences::set('language', $lang); } @@ -288,8 +289,8 @@ class PreferencesController extends Controller } // optional fields for transactions: - $setOptions = $request->get('tj') ?? []; - $optionalTj = [ + $setOptions = $request->get('tj') ?? []; + $optionalTj = [ 'interest_date' => array_key_exists('interest_date', $setOptions), 'book_date' => array_key_exists('book_date', $setOptions), 'process_date' => array_key_exists('process_date', $setOptions), @@ -306,13 +307,13 @@ class PreferencesController extends Controller Preferences::set('transaction_journal_optional_fields', $optionalTj); // dark mode - $darkMode = $request->get('darkMode') ?? 'browser'; + $darkMode = $request->get('darkMode') ?? 'browser'; if (in_array($darkMode, config('firefly.available_dark_modes'), true)) { Preferences::set('darkMode', $darkMode); } // anonymous amounts? - $anonymous = '1' === $request->get('anonymous'); + $anonymous = '1' === $request->get('anonymous'); Preferences::set('anonymous', $anonymous); // save and continue diff --git a/app/Repositories/Attachment/AttachmentRepositoryInterface.php b/app/Repositories/Attachment/AttachmentRepositoryInterface.php index 756219f108..56078984e2 100644 --- a/app/Repositories/Attachment/AttachmentRepositoryInterface.php +++ b/app/Repositories/Attachment/AttachmentRepositoryInterface.php @@ -27,8 +27,6 @@ use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Attachment; use FireflyIII\Models\UserGroup; -use FireflyIII\User; -use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; /** diff --git a/app/Services/Internal/Update/JournalUpdateService.php b/app/Services/Internal/Update/JournalUpdateService.php index 0e7b19ba34..17d0a0da4b 100644 --- a/app/Services/Internal/Update/JournalUpdateService.php +++ b/app/Services/Internal/Update/JournalUpdateService.php @@ -484,20 +484,21 @@ class JournalUpdateService // do some parsing. Log::debug(sprintf('Create date value from string "%s".', $value)); $this->transactionJournal->date_tz = $value->format('e'); - $res = $value->gt($this->transactionJournal->date); + $res = $value->gt($this->transactionJournal->date); Log::debug(sprintf('Old date: %s, new date: %s', $this->transactionJournal->date->toW3cString(), $value->toW3cString())); + /** @var TransactionJournalMetaFactory $factory */ - $factory = app(TransactionJournalMetaFactory::class); - $set = [ + $factory = app(TransactionJournalMetaFactory::class); + $set = [ 'journal' => $this->transactionJournal, 'name' => '_internal_previous_date', 'data' => null, ]; - if($res) { + if ($res) { Log::debug('Transaction is set to be AFTER its current date. Save also the "_internal_previous_date"-field.'); $set['data'] = clone $this->transactionJournal->date; } - if(!$res) { + if (!$res) { Log::debug('Transaction is NOT set to be AFTER its current date. Remove the "_internal_previous_date"-field.'); } $factory->updateOrCreate($set); diff --git a/app/Support/Models/AccountBalanceCalculator.php b/app/Support/Models/AccountBalanceCalculator.php index b77496a7eb..3298c7ed39 100644 --- a/app/Support/Models/AccountBalanceCalculator.php +++ b/app/Support/Models/AccountBalanceCalculator.php @@ -65,17 +65,17 @@ class AccountBalanceCalculator public static function recalculateForJournal(TransactionJournal $transactionJournal): void { Log::debug(__METHOD__); - $object = new self(); + $object = new self(); - $set = []; + $set = []; foreach ($transactionJournal->transactions as $transaction) { $set[$transaction->account_id] = $transaction->account; } $accounts = new Collection()->push(...$set); // find meta value: - $date = $transactionJournal->date; - $meta = $transactionJournal->transactionJournalMeta()->where('name', '_internal_previous_date')->where('data', '!=', '')->first(); + $date = $transactionJournal->date; + $meta = $transactionJournal->transactionJournalMeta()->where('name', '_internal_previous_date')->where('data', '!=', '')->first(); Log::debug(sprintf('Date used is "%s"', $date->toW3cString())); if (null !== $meta) { $date = Carbon::parse($meta->data); @@ -94,17 +94,18 @@ class AccountBalanceCalculator return '0'; } Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d'))); - $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->where('transaction_journals.transaction_currency_id', $currencyId) - ->whereNull('transaction_journals.deleted_at') + $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->whereNull('transactions.deleted_at') + ->where('transaction_journals.transaction_currency_id', $currencyId) + ->whereNull('transaction_journals.deleted_at') // this order is the same as GroupCollector - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC') - ->orderBy('transaction_journals.description', 'DESC') - ->orderBy('transactions.amount', 'DESC') - ->where('transactions.account_id', $accountId); + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->orderBy('transaction_journals.description', 'DESC') + ->orderBy('transactions.amount', 'DESC') + ->where('transactions.account_id', $accountId) + ; $notBefore->startOfDay(); $query->where('transaction_journals.date', '<', $notBefore); @@ -125,14 +126,15 @@ class AccountBalanceCalculator $balances = []; $count = 0; $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') // this order is the same as GroupCollector, but in the exact reverse. - ->orderBy('transaction_journals.date', 'asc') - ->orderBy('transaction_journals.order', 'desc') - ->orderBy('transaction_journals.id', 'asc') - ->orderBy('transaction_journals.description', 'asc') - ->orderBy('transactions.amount', 'asc'); + ->orderBy('transaction_journals.date', 'asc') + ->orderBy('transaction_journals.order', 'desc') + ->orderBy('transaction_journals.id', 'asc') + ->orderBy('transaction_journals.description', 'asc') + ->orderBy('transactions.amount', 'asc') + ; if ($accounts->count() > 0) { $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); } @@ -141,7 +143,7 @@ class AccountBalanceCalculator $query->where('transaction_journals.date', '>=', $notBefore); } - $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); + $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); Log::debug(sprintf('Counted %d transaction(s)', $set->count())); // the balance value is an array. @@ -154,8 +156,8 @@ class AccountBalanceCalculator $balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null]; // before and after are easy: - $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; - $after = bcadd($before, (string)$entry->amount); + $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; + $after = bcadd($before, (string)$entry->amount); if (true === $entry->balance_dirty || $accounts->count() > 0) { // update the transaction: $entry->balance_before = $before; diff --git a/config/firefly.php b/config/firefly.php index a2dfd0783c..7bf86de769 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used. // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-12-29', - 'build_time' => 1767019946, + 'version' => 'develop/2025-12-30', + 'build_time' => 1767107843, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. diff --git a/package-lock.json b/package-lock.json index b3582a9dd1..30f337f330 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4508,9 +4508,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001761", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", - "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", + "version": "1.0.30001762", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", + "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", "dev": true, "funding": [ { @@ -9650,9 +9650,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": {