Compare commits

..

15 Commits

Author SHA1 Message Date
github-actions[bot]
8235c24c13 Merge pull request #11421 from firefly-iii/release-1767107945
🤖 Automatically merge the PR into the develop branch.
2025-12-30 16:19:14 +01:00
JC5
7ca96a766f 🤖 Auto commit for release 'develop' on 2025-12-30 2025-12-30 16:19:05 +01:00
James Cole
27586a7ec2 Allow transactions to be moved to the future and still have the running balance calculated correctly. 2025-12-30 16:13:28 +01:00
James Cole
dc3c3bb092 Add setting for anonymous amounts in preferences. 2025-12-30 07:24:14 +01:00
github-actions[bot]
7287c29778 Merge pull request #11419 from firefly-iii/release-1767020049
🤖 Automatically merge the PR into the develop branch.
2025-12-29 15:54:17 +01:00
JC5
7d374b22f9 🤖 Auto commit for release 'develop' on 2025-12-29 2025-12-29 15:54:09 +01:00
James Cole
e0542f1270 Fix end of quarter issue. 2025-12-29 11:10:28 +01:00
James Cole
73a9be0605 Fix #11410 2025-12-29 11:05:48 +01:00
github-actions[bot]
1f343bda1a Merge pull request #11416 from firefly-iii/release-1766995950
🤖 Automatically merge the PR into the develop branch.
2025-12-29 09:12:40 +01:00
JC5
1c8eaf93a6 🤖 Auto commit for release 'develop' on 2025-12-29 2025-12-29 09:12:30 +01:00
James Cole
ccfee25000 Merge pull request #11414 from oboxodo/develop
Add Uruguayan Peso to currency seeder
2025-12-29 09:11:25 +01:00
Diego Algorta
07c6dac766 Add Uruguayan Peso to currency seeder 2025-12-28 12:06:08 -03:00
github-actions[bot]
b0b2e5b752 Merge pull request #11411 from firefly-iii/release-1766901579
🤖 Automatically merge the PR into the develop branch.
2025-12-28 06:59:46 +01:00
JC5
ff3a935e9d 🤖 Auto commit for release 'develop' on 2025-12-28 2025-12-28 06:59:39 +01:00
James Cole
f0e2f09da7 Fix some end of period stuff. 2025-12-28 06:54:03 +01:00
17 changed files with 177 additions and 108 deletions

View File

@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
## 2025
- Diego Algorta
- Jihad
- jreyesr
- codearena-bot

View File

@@ -84,7 +84,7 @@ class CorrectsAmounts extends Command
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$type = TransactionType::where('type', TransactionTypeEnum::TRANSFER->value)->first();
$journals = TransactionJournal::where('transaction_type_id', $type->id)->get();
$journals = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')->whereNotNull('transactions.foreign_amount')->where('transaction_journals.transaction_type_id', $type->id)->distinct()->get(['transaction_journals.*']);
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
@@ -93,7 +93,7 @@ class CorrectsAmounts extends Command
$valid = $this->validateJournal($journal);
if (false === $valid) {
Log::debug(sprintf('Journal #%d does not need to be fixed or is invalid (see previous messages)', $journal->id));
// Log::debug(sprintf('Journal #%d does not need to be fixed or is invalid (see previous messages)', $journal->id));
continue;
}
@@ -298,7 +298,7 @@ class CorrectsAmounts extends Command
return false;
}
if (null === $source->foreign_currency_id || null === $destination->foreign_currency_id) {
Log::debug('No foreign currency information is present, can safely continue with other transactions.');
// Log::debug('No foreign currency information is present, can safely continue with other transactions.');
return false;
}

View File

@@ -51,7 +51,7 @@ class TransactionJournalMetaFactory
Log::debug('Is a carbon object.');
$value = $data['data']->toW3cString();
}
if ('' === (string) $value) {
if ('' === (string)$value) {
// Log::debug('Is an empty string.');
// don't store blank strings.
if (null !== $entry) {

View File

@@ -24,11 +24,12 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Models\AccountBalanceCalculator;
use Illuminate\Support\Facades\Log;
use FireflyIII\Support\Facades\FireflyConfig;
/**
* Class TransactionObserver
@@ -42,7 +43,10 @@ class TransactionObserver
Log::debug('Observe "created" of a transaction.');
if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data && (1 === bccomp($transaction->amount, '0') && self::$recalculate)) {
Log::debug('Trigger recalculateForJournal');
AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal);
$journal = $transaction->transactionJournal;
if ($journal instanceof TransactionJournal) {
AccountBalanceCalculator::recalculateForJournal($journal);
}
}
$this->updatePrimaryCurrencyAmount($transaction);
}
@@ -56,7 +60,10 @@ 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))) {
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);

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Support\Facades\Navigation;
use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
@@ -33,7 +32,9 @@ use FireflyIII\Http\Requests\PreferencesRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Navigation;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Singleton\PreferencesSingleton;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
@@ -44,7 +45,6 @@ use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use JsonException;
use Safe\Exceptions\FilesystemException;
use FireflyIII\Support\Facades\Steam;
use function Safe\file_get_contents;
use function Safe\json_decode;
@@ -63,7 +63,7 @@ class PreferencesController extends Controller
$this->middleware(
static function ($request, $next) {
app('view')->share('title', (string) trans('firefly.preferences'));
app('view')->share('title', (string)trans('firefly.preferences'));
app('view')->share('mainTitleIcon', 'fa-gear');
return $next($request);
@@ -87,8 +87,8 @@ class PreferencesController extends Controller
/** @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);
@@ -97,7 +97,7 @@ class PreferencesController extends Controller
if ('opt_group_' === $role) {
$role = 'opt_group_defaultAsset';
}
$groupedAccounts[(string) trans(sprintf('firefly.%s', $role))][$account->id] = $account->name;
$groupedAccounts[(string)trans(sprintf('firefly.%s', $role))][$account->id] = $account->name;
}
ksort($groupedAccounts);
@@ -120,21 +120,22 @@ class PreferencesController extends Controller
if (is_array($fiscalYearStartStr)) {
$fiscalYearStartStr = '01-01';
}
$fiscalYearStart = sprintf('%s-%s', Carbon::now()->format('Y'), (string) $fiscalYearStartStr);
$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;
$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;
$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;
$ntfyPass = (string)Preferences::getEncrypted('ntfy_pass', '')->data;
$channels = config('notifications.channels');
$forcedAvailability = [];
$anonymous = Steam::anonymous();
// notification preferences
$notifications = [];
@@ -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)) {
@@ -184,7 +185,7 @@ class PreferencesController extends Controller
$ntfyPass = '';
}
return view('preferences.index', ['language' => $language, 'pushoverAppToken' => $pushoverAppToken, 'pushoverUserToken' => $pushoverUserToken, 'ntfyServer' => $ntfyServer, 'ntfyTopic' => $ntfyTopic, 'ntfyAuth' => $ntfyAuth, 'channels' => $channels, 'ntfyUser' => $ntfyUser, 'forcedAvailability' => $forcedAvailability, 'ntfyPass' => $ntfyPass, 'groupedAccounts' => $groupedAccounts, 'isDocker' => $isDocker, 'frontpageAccounts' => $frontpageAccounts, 'languages' => $languages, 'darkMode' => $darkMode, 'availableDarkModes' => $availableDarkModes, 'notifications' => $notifications, 'convertToPrimary' => $convertToPrimary, 'slackUrl' => $slackUrl, 'locales' => $locales, 'locale' => $locale, 'tjOptionalFields' => $tjOptionalFields, 'viewRange' => $viewRange, 'customFiscalYear' => $customFiscalYear, 'listPageSize' => $listPageSize, 'fiscalYearStart' => $fiscalYearStart]);
return view('preferences.index', ['anonymous' => $anonymous, 'language' => $language, 'pushoverAppToken' => $pushoverAppToken, 'pushoverUserToken' => $pushoverUserToken, 'ntfyServer' => $ntfyServer, 'ntfyTopic' => $ntfyTopic, 'ntfyAuth' => $ntfyAuth, 'channels' => $channels, 'ntfyUser' => $ntfyUser, 'forcedAvailability' => $forcedAvailability, 'ntfyPass' => $ntfyPass, 'groupedAccounts' => $groupedAccounts, 'isDocker' => $isDocker, 'frontpageAccounts' => $frontpageAccounts, 'languages' => $languages, 'darkMode' => $darkMode, 'availableDarkModes' => $availableDarkModes, 'notifications' => $notifications, 'convertToPrimary' => $convertToPrimary, 'slackUrl' => $slackUrl, 'locales' => $locales, 'locale' => $locale, 'tjOptionalFields' => $tjOptionalFields, 'viewRange' => $viewRange, 'customFiscalYear' => $customFiscalYear, 'listPageSize' => $listPageSize, 'fiscalYearStart' => $fiscalYearStart]);
}
/**
@@ -201,7 +202,7 @@ class PreferencesController extends Controller
$frontpageAccounts = [];
if (is_array($request->get('frontpageAccounts')) && count($request->get('frontpageAccounts')) > 0) {
foreach ($request->get('frontpageAccounts') as $id) {
$frontpageAccounts[] = (int) $id;
$frontpageAccounts[] = (int)$id;
}
Preferences::set('frontpageAccounts', $frontpageAccounts);
}
@@ -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,7 +264,7 @@ 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);
}
@@ -282,7 +283,7 @@ class PreferencesController extends Controller
// same for locale:
if (!auth()->user()->hasRole('demo')) {
$locale = (string) $request->get('locale');
$locale = (string)$request->get('locale');
$locale = '' === $locale ? null : $locale;
Preferences::set('locale', $locale);
}
@@ -311,7 +312,12 @@ class PreferencesController extends Controller
Preferences::set('darkMode', $darkMode);
}
session()->flash('success', (string) trans('firefly.saved_preferences'));
// anonymous amounts?
$anonymous = '1' === $request->get('anonymous');
Preferences::set('anonymous', $anonymous);
// save and continue
session()->flash('success', (string)trans('firefly.saved_preferences'));
Preferences::mark();
return redirect(route('preferences.index'));
@@ -325,7 +331,7 @@ class PreferencesController extends Controller
switch ($channel) {
default:
session()->flash('error', (string) trans('firefly.notification_test_failed', ['channel' => $channel]));
session()->flash('error', (string)trans('firefly.notification_test_failed', ['channel' => $channel]));
break;
@@ -337,7 +343,7 @@ class PreferencesController extends Controller
$user = auth()->user();
Log::debug(sprintf('Now in testNotification("%s") controller.', $channel));
event(new UserTestNotificationChannel($channel, $user));
session()->flash('success', (string) trans('firefly.notification_test_executed', ['channel' => $channel]));
session()->flash('success', (string)trans('firefly.notification_test_executed', ['channel' => $channel]));
}
return '';

View File

@@ -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;
/**
@@ -38,7 +36,6 @@ use Illuminate\Support\Collection;
* @method getUserGroup()
* @method getUser()
* @method checkUserGroupAccess(UserRoleEnum $role)
* @method setUser(null|Authenticatable|User $user)
* @method setUserGroupById(int $userGroupId)
*/
interface AttachmentRepositoryInterface

View File

@@ -68,8 +68,7 @@ class JournalUpdateService
private ?Account $destinationAccount = null;
private ?Transaction $destinationTransaction = null;
private array $metaDate
= ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date',
'invoice_date', ];
= ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date', '_internal_previous_date'];
private array $metaString
= [
'sepa_cc',
@@ -205,11 +204,9 @@ class JournalUpdateService
$validator->setUser($this->transactionJournal->user);
$result = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
Log::debug(
sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true))
);
Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true)));
// TODO typeoverrule the account validator may have a different opinion on the transaction type.
// TODO type overrule the account validator may have a different opinion on the transaction type.
// validate submitted info:
return $result;
@@ -283,14 +280,7 @@ class JournalUpdateService
$validator->setUser($this->transactionJournal->user);
$validator->source = $this->getValidSourceAccount();
$result = $validator->validateDestination(['id' => $destId, 'name' => $destName]);
Log::debug(
sprintf(
'hasValidDestinationAccount(%d, "%s") will return %s',
$destId,
$destName,
var_export($result, true)
)
);
Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true)));
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
@@ -494,6 +484,24 @@ 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);
Log::debug(sprintf('Old date: %s, new date: %s', $this->transactionJournal->date->toW3cString(), $value->toW3cString()));
/** @var TransactionJournalMetaFactory $factory */
$factory = app(TransactionJournalMetaFactory::class);
$set = [
'journal' => $this->transactionJournal,
'name' => '_internal_previous_date',
'data' => null,
];
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) {
Log::debug('Transaction is NOT set to be AFTER its current date. Remove the "_internal_previous_date"-field.');
}
$factory->updateOrCreate($set);
}
event(new TriggeredAuditLog($this->transactionJournal->user, $this->transactionJournal, sprintf('update_%s', $fieldName), $this->transactionJournal->{$fieldName}, $value));

View File

@@ -72,12 +72,25 @@ class AccountBalanceCalculator
$set[$transaction->account_id] = $transaction->account;
}
$accounts = new Collection()->push(...$set);
$object->optimizedCalculation($accounts, $transactionJournal->date);
// find meta value:
$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);
Log::debug(sprintf('Date is overruled with "%s"', $date->toW3cString()));
}
$object->optimizedCalculation($accounts, $date);
}
private function getLatestBalance(int $accountId, int $currencyId, ?Carbon $notBefore): string
{
if (!$notBefore instanceof Carbon) {
Log::debug(sprintf('Start balance for account #%d and currency #%d is 0.', $accountId, $currencyId));
return '0';
}
Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d')));

View File

@@ -23,16 +23,16 @@ declare(strict_types=1);
namespace FireflyIII\Support;
use FireflyIII\Support\Facades\Preferences;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Exceptions\IntervalException;
use FireflyIII\Helpers\Fiscal\FiscalHelperInterface;
use FireflyIII\Support\Calendar\Calculator;
use FireflyIII\Support\Calendar\Periodicity;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Facades\Log;
use Throwable;
use FireflyIII\Support\Facades\Steam;
/**
* Class Navigation.
@@ -214,6 +214,11 @@ class Navigation
Log::debug('endOfPeriod() requests "YTD" + future, set it to "1Y" instead.');
$repeatFreq = '1Y';
}
if ('QTD' === $repeatFreq && $end->isFuture()) {
// fall back to a yearly schedule if the requested period is YTD.
Log::debug('endOfPeriod() requests "YTD" + future, set it to "3M" instead.');
$repeatFreq = '3M';
}
$functionMap = [
'1D' => 'endOfDay',
@@ -295,7 +300,25 @@ class Navigation
if (null !== $result) {
// add sanity check.
if ($currentEnd->lt($end)) {
throw new FireflyException(sprintf('[d] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString()));
switch ($repeatFreq) {
case 'QTD':
$currentEnd = $end->clone()->endOfQuarter()->setMilli(0);
break;
case 'MTD':
$currentEnd = $end->clone()->endOfMonth()->setMilli(0);
break;
case 'YTD':
$currentEnd = $end->clone()->endOfYear()->setMilli(0);
break;
}
if ($currentEnd->lt($end)) {
throw new FireflyException(sprintf('[d] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString()));
}
}
return $result;

View File

@@ -75,7 +75,7 @@ class TransactionGroupTransformer extends AbstractTransformer
'recurrence_count',
'recurrence_total',
];
$this->metaDateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
$this->metaDateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date', '_internal_previous_date'];
}
public function transform(array $group): array

82
composer.lock generated
View File

@@ -1182,24 +1182,24 @@
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.3",
"version": "v1.1.4",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.3"
"phpoption/phpoption": "^1.9.5"
},
"require-dev": {
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
"phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
},
"type": "library",
"autoload": {
@@ -1228,7 +1228,7 @@
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
},
"funding": [
{
@@ -1240,7 +1240,7 @@
"type": "tidelift"
}
],
"time": "2024-07-20T21:45:45+00:00"
"time": "2025-12-27T19:43:20+00:00"
},
{
"name": "guzzlehttp/guzzle",
@@ -2813,16 +2813,16 @@
},
{
"name": "league/csv",
"version": "9.27.1",
"version": "9.28.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "26de738b8fccf785397d05ee2fc07b6cd8749797"
"reference": "6582ace29ae09ba5b07049d40ea13eb19c8b5073"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/26de738b8fccf785397d05ee2fc07b6cd8749797",
"reference": "26de738b8fccf785397d05ee2fc07b6cd8749797",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/6582ace29ae09ba5b07049d40ea13eb19c8b5073",
"reference": "6582ace29ae09ba5b07049d40ea13eb19c8b5073",
"shasum": ""
},
"require": {
@@ -2832,14 +2832,14 @@
"require-dev": {
"ext-dom": "*",
"ext-xdebug": "*",
"friendsofphp/php-cs-fixer": "^3.75.0",
"phpbench/phpbench": "^1.4.1",
"phpstan/phpstan": "^1.12.27",
"friendsofphp/php-cs-fixer": "^3.92.3",
"phpbench/phpbench": "^1.4.3",
"phpstan/phpstan": "^1.12.32",
"phpstan/phpstan-deprecation-rules": "^1.2.1",
"phpstan/phpstan-phpunit": "^1.4.2",
"phpstan/phpstan-strict-rules": "^1.6.2",
"phpunit/phpunit": "^10.5.16 || ^11.5.22 || ^12.3.6",
"symfony/var-dumper": "^6.4.8 || ^7.3.0"
"phpunit/phpunit": "^10.5.16 || ^11.5.22 || ^12.5.4",
"symfony/var-dumper": "^6.4.8 || ^7.4.0 || ^8.0"
},
"suggest": {
"ext-dom": "Required to use the XMLConverter and the HTMLConverter classes",
@@ -2900,7 +2900,7 @@
"type": "github"
}
],
"time": "2025-10-25T08:35:20+00:00"
"time": "2025-12-27T15:18:42+00:00"
},
{
"name": "league/event",
@@ -4749,16 +4749,16 @@
},
{
"name": "phpoption/phpoption",
"version": "1.9.4",
"version": "1.9.5",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d"
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
"reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
"shasum": ""
},
"require": {
@@ -4808,7 +4808,7 @@
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.4"
"source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
},
"funding": [
{
@@ -4820,7 +4820,7 @@
"type": "tidelift"
}
],
"time": "2025-08-21T11:53:16+00:00"
"time": "2025-12-27T19:41:33+00:00"
},
{
"name": "phpseclib/phpseclib",
@@ -9918,26 +9918,26 @@
},
{
"name": "vlucas/phpdotenv",
"version": "v5.6.2",
"version": "v5.6.3",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
"reference": "955e7815d677a3eaa7075231212f2110983adecc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc",
"reference": "955e7815d677a3eaa7075231212f2110983adecc",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"graham-campbell/result-type": "^1.1.3",
"graham-campbell/result-type": "^1.1.4",
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.3",
"symfony/polyfill-ctype": "^1.24",
"symfony/polyfill-mbstring": "^1.24",
"symfony/polyfill-php80": "^1.24"
"phpoption/phpoption": "^1.9.5",
"symfony/polyfill-ctype": "^1.26",
"symfony/polyfill-mbstring": "^1.26",
"symfony/polyfill-php80": "^1.26"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
@@ -9986,7 +9986,7 @@
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3"
},
"funding": [
{
@@ -9998,7 +9998,7 @@
"type": "tidelift"
}
],
"time": "2025-04-30T23:37:27+00:00"
"time": "2025-12-27T19:49:13+00:00"
},
{
"name": "voku/portable-ascii",
@@ -10367,16 +10367,16 @@
},
{
"name": "composer/class-map-generator",
"version": "1.7.0",
"version": "1.7.1",
"source": {
"type": "git",
"url": "https://github.com/composer/class-map-generator.git",
"reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6"
"reference": "8f5fa3cc214230e71f54924bd0197a3bcc705eb1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/2373419b7709815ed323ebf18c3c72d03ff4a8a6",
"reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/8f5fa3cc214230e71f54924bd0197a3bcc705eb1",
"reference": "8f5fa3cc214230e71f54924bd0197a3bcc705eb1",
"shasum": ""
},
"require": {
@@ -10420,7 +10420,7 @@
],
"support": {
"issues": "https://github.com/composer/class-map-generator/issues",
"source": "https://github.com/composer/class-map-generator/tree/1.7.0"
"source": "https://github.com/composer/class-map-generator/tree/1.7.1"
},
"funding": [
{
@@ -10432,7 +10432,7 @@
"type": "github"
}
],
"time": "2025-11-19T10:41:15+00:00"
"time": "2025-12-29T13:15:25+00:00"
},
{
"name": "composer/pcre",

View File

@@ -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-27',
'build_time' => 1766836093,
'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.

View File

@@ -57,6 +57,7 @@ class TransactionCurrencySeeder extends Seeder
$currencies[] = ['code' => 'ARS', 'name' => 'Argentinian Peso', 'symbol' => '$', 'decimal_places' => 2];
$currencies[] = ['code' => 'COP', 'name' => 'Colombian Peso', 'symbol' => '$', 'decimal_places' => 2];
$currencies[] = ['code' => 'CLP', 'name' => 'Chilean Peso', 'symbol' => '$', 'decimal_places' => 2];
$currencies[] = ['code' => 'UYU', 'name' => 'Uruguayan Peso', 'symbol' => '$', 'decimal_places' => 2];
// oceanian currencies
$currencies[] = ['code' => 'IDR', 'name' => 'Indonesian rupiah', 'symbol' => 'Rp', 'decimal_places' => 2];

18
package-lock.json generated
View File

@@ -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": [
{
@@ -7082,9 +7082,9 @@
}
},
"node_modules/i18next-chained-backend": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/i18next-chained-backend/-/i18next-chained-backend-4.6.2.tgz",
"integrity": "sha512-2P092fR+nAPQlGzPUoIIxbwo7PTBqQYgLxwv1XhSTQUAUoelLo5LkX+FqRxxSDg9WEAsrc8+2WL6mJtMGIa6WQ==",
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/i18next-chained-backend/-/i18next-chained-backend-4.6.3.tgz",
"integrity": "sha512-Yg4hAKg/98zRAMQs87vJSNevTzaPPrYF3Eb7Kpx+UEaaXLd3p69g7dulAL+hpmZQHeMQ/5gFqHVtdwva53mB0Q==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.23.2"
@@ -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": {

View File

@@ -34,12 +34,14 @@ $(document).ready(function () {
$('input[name="ignore_budget"]').attr('checked', false);
});
$('input[name="tags"]').on('itemAdded', function (event) {
var isChecked = $('#tags_action_do_nothing').is(':checked');
if (true === isChecked) {
$('#tags_action_do_nothing').attr('checked', false);
$('#tags_action_do_replace').attr('checked', true);
}
$('input[name="tags"]').on('itemAdded', function(event) {
$('#tags_action_do_nothing').attr('checked', false);
$('#tags_action_do_replace').attr('checked', true);
});
});
});

View File

@@ -149,6 +149,9 @@ return [
'left_in_budget_limit' => 'Left to spend according to budgeting',
'current_period' => 'Current period',
'show_the_current_period_and_overview' => 'Show the current period and overview',
'pref_anonymous' => 'Hidden amounts',
'pref_anonymous_help' => 'For privacy and anonymity, you can make Firefly III hide all amounts. This will not hide amounts in edit boxes, but the rest are hidden. You can also toggle this with the little "eye"-icon in the top bar.',
'pref_anonymous_label' => 'Hide amounts',
'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.',
'budget_in_period' => 'All transactions for budget ":name" between :start and :end in :currency',
'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end in :currency',

View File

@@ -110,6 +110,14 @@
{{ ExpandedForm.checkbox('convertToPrimary','1',convertToPrimary,{ 'label' : 'pref_convert_primary_help'|_ }) }}
</div>
{% endif %}
{# conversion back to primary currency #}
<div class="preferences-box">
<h3>{{ 'pref_anonymous'|_ }}</h3>
<p class="text-info">
{{ 'pref_anonymous_help'|_ }}
</p>
{{ ExpandedForm.checkbox('anonymous','1',anonymous,{ 'label' : 'pref_anonymous_label'|_ }) }}
</div>
</div>
{# general settings column B #}