Compare commits

..

45 Commits

Author SHA1 Message Date
github-actions[bot]
2c2ebae8fa Merge pull request #11691 from firefly-iii/release-1770727195
🤖 Automatically merge the PR into the develop branch.
2026-02-10 13:40:04 +01:00
JC5
aff9afb767 🤖 Auto commit for release 'develop' on 2026-02-10 2026-02-10 13:39:55 +01:00
James Cole
c8e95fe131 Fix audit log entry position. 2026-02-09 20:05:38 +01:00
James Cole
cef514c22b Merge pull request #11680 from firefly-iii/dependabot/composer/develop/mailersend/laravel-driver-3.0.0 2026-02-09 05:57:03 +01:00
github-actions[bot]
46d2ba3d3b Merge pull request #11681 from firefly-iii/release-1770609440
🤖 Automatically merge the PR into the develop branch.
2026-02-09 04:57:28 +01:00
JC5
45a0504eba 🤖 Auto commit for release 'develop' on 2026-02-09 2026-02-09 04:57:20 +01:00
dependabot[bot]
9711bc1d04 Bump mailersend/laravel-driver from 2.12.0 to 3.0.0
Bumps [mailersend/laravel-driver](https://github.com/mailersend/mailersend-laravel-driver) from 2.12.0 to 3.0.0.
- [Release notes](https://github.com/mailersend/mailersend-laravel-driver/releases)
- [Commits](https://github.com/mailersend/mailersend-laravel-driver/compare/v2.12.0...v3.0.0)

---
updated-dependencies:
- dependency-name: mailersend/laravel-driver
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-09 03:52:32 +00:00
James Cole
cf2343c0dd Catch errors in DB thing. 2026-02-08 21:07:50 +01:00
James Cole
34097cecf0 Fix viewrange. 2026-02-08 08:29:59 +01:00
github-actions[bot]
8735be2f6b Merge pull request #11676 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-08 08:27:47 +01:00
github-actions[bot]
0e8cc91308 Merge pull request #11675 from firefly-iii/release-1770535655
🤖 Automatically merge the PR into the develop branch.
2026-02-08 08:27:42 +01:00
JC5
5cbb311e4d 🤖 Auto commit for release 'v6.4.18' on 2026-02-08 2026-02-08 08:27:35 +01:00
James Cole
c334641b90 Add class reference. 2026-02-08 08:21:24 +01:00
James Cole
4c2356881d Remove unused debug logging. 2026-02-08 08:20:50 +01:00
James Cole
d9a0d06712 Clean up some debug logging. 2026-02-08 08:16:33 +01:00
github-actions[bot]
65b9dedc03 Merge pull request #11674 from firefly-iii/release-1770534470
🤖 Automatically merge the PR into the develop branch.
2026-02-08 08:07:56 +01:00
JC5
e46ef138b1 🤖 Auto commit for release 'develop' on 2026-02-08 2026-02-08 08:07:50 +01:00
James Cole
f0fdb57754 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-08 08:04:05 +01:00
James Cole
09799582aa Remove debug comments. 2026-02-08 08:03:59 +01:00
github-actions[bot]
850c824da1 Merge pull request #11673 from firefly-iii/release-1770531358
🤖 Automatically merge the PR into the develop branch.
2026-02-08 07:16:05 +01:00
JC5
34160da67a 🤖 Auto commit for release 'develop' on 2026-02-08 2026-02-08 07:15:58 +01:00
James Cole
2848a64c13 Fix sponsorship link. 2026-02-08 07:11:24 +01:00
James Cole
998d6bf874 Fix #11668 2026-02-08 06:49:27 +01:00
James Cole
b85200e1f6 Fix #11667 2026-02-08 06:46:14 +01:00
James Cole
6944001887 Fix #11671 2026-02-08 06:22:40 +01:00
github-actions[bot]
154abf1fc0 Merge pull request #11665 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-07 07:55:46 +01:00
github-actions[bot]
0113cc1651 Merge pull request #11664 from firefly-iii/release-1770447333
🤖 Automatically merge the PR into the develop branch.
2026-02-07 07:55:40 +01:00
JC5
38b5b656a1 🤖 Auto commit for release 'v6.4.17' on 2026-02-07 2026-02-07 07:55:33 +01:00
github-actions[bot]
e74163a7ec Merge pull request #11663 from firefly-iii/release-1770445570
🤖 Automatically merge the PR into the develop branch.
2026-02-07 07:26:17 +01:00
JC5
c60094d231 🤖 Auto commit for release 'develop' on 2026-02-07 2026-02-07 07:26:10 +01:00
James Cole
39d46d469c Fix query parser logging. 2026-02-07 06:53:12 +01:00
github-actions[bot]
6caea5ffa3 Merge pull request #11662 from firefly-iii/release-1770442761
🤖 Automatically merge the PR into the develop branch.
2026-02-07 06:39:28 +01:00
JC5
4024f76a51 🤖 Auto commit for release 'develop' on 2026-02-07 2026-02-07 06:39:21 +01:00
James Cole
de84946371 Expand changelog. 2026-02-07 06:33:30 +01:00
James Cole
6d4aca54de Fix #11246 2026-02-07 06:32:11 +01:00
James Cole
256262b2ba Fix #11657 2026-02-07 06:16:23 +01:00
James Cole
fb035ba594 Fix #11660 2026-02-07 06:09:41 +01:00
James Cole
20776949a6 Clean up changelog. 2026-02-06 18:32:55 +01:00
github-actions[bot]
ad5a8a2934 Merge pull request #11656 from firefly-iii/release-1770398774
🤖 Automatically merge the PR into the develop branch.
2026-02-06 18:26:25 +01:00
JC5
e37ef69491 🤖 Auto commit for release 'develop' on 2026-02-06 2026-02-06 18:26:14 +01:00
James Cole
df8a406c58 Fix issue with email change. 2026-02-06 18:12:52 +01:00
James Cole
88d3e01065 Add events for opening balance. 2026-02-06 18:10:41 +01:00
James Cole
7a1c32f1aa Expand changelog. 2026-02-06 15:58:45 +01:00
James Cole
54df0d44f7 Clean up events 2026-02-06 15:47:34 +01:00
James Cole
1f7775032b Fix budgeted amounts. 2026-02-06 15:38:32 +01:00
104 changed files with 903 additions and 474 deletions

BIN
.github/assets/img/testmu.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -167,7 +167,7 @@ class BasicController extends Controller
],
];
$newIncomes = [
$primary->id => [
$primary->id => [
'currency_id' => $primary->id,
'currency_code' => $primary->code,
'currency_symbol' => $primary->symbol,
@@ -176,7 +176,7 @@ class BasicController extends Controller
],
];
$sums = [
$primary->id => [
$primary->id => [
'currency_id' => $primary->id,
'currency_code' => $primary->code,
'currency_symbol' => $primary->symbol,
@@ -244,7 +244,7 @@ class BasicController extends Controller
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $sums[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -258,7 +258,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $expenses[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -270,7 +270,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $incomes[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -286,7 +286,7 @@ class BasicController extends Controller
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -298,7 +298,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -310,7 +310,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -345,7 +345,7 @@ class BasicController extends Controller
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
$return[$currencyId] = [
'key' => sprintf('left-to-spend-in-%s', $currencies[$currencyId]->code),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currencies[$currencyId]->symbol]),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currencies[$currencyId]->symbol]),
'no_available_budgets' => false,
'monetary_value' => $availableBudget,
'currency_id' => (string) $currencies[$currencyId]->id,
@@ -386,7 +386,7 @@ class BasicController extends Controller
$return[$currencyId] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
'no_available_budgets' => false,
'monetary_value' => $leftToSpend,
'currency_id' => (string) $row['currency_id'],
@@ -493,7 +493,7 @@ class BasicController extends Controller
// return stuff
$return[] = [
'key' => sprintf('net-worth-in-%s', $data['currency_code']),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]),
'monetary_value' => $amount,
'currency_id' => (string) $data['currency_id'],
'currency_code' => $data['currency_code'],
@@ -507,7 +507,7 @@ class BasicController extends Controller
if (0 === count($return)) {
$return[] = [
'key' => sprintf('net-worth-in-%s', $this->primaryCurrency->code),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $this->primaryCurrency->symbol]),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $this->primaryCurrency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $this->primaryCurrency->id,
'currency_code' => $this->primaryCurrency->code,
@@ -533,7 +533,7 @@ class BasicController extends Controller
*/
$paidAmount = $this->billRepository->sumPaidInRange($start, $end);
$unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end);
$currencies = [$this->primaryCurrency->id => $this->primaryCurrency];
$currencies = [$this->primaryCurrency->id => $this->primaryCurrency];
if ($this->convertToPrimary) {
$converter = new ExchangeRateConverter();
@@ -598,7 +598,7 @@ class BasicController extends Controller
$amount = bcmul((string) $info['sum'], '-1');
$return[] = [
'key' => sprintf('bills-paid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]),
'monetary_value' => $amount,
'currency_id' => (string) $info['id'],
'currency_code' => $info['code'],
@@ -617,7 +617,7 @@ class BasicController extends Controller
$amount = bcmul((string) $info['sum'], '-1');
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]),
'monetary_value' => $amount,
'currency_id' => (string) $info['id'],
'currency_code' => $info['code'],
@@ -636,7 +636,7 @@ class BasicController extends Controller
$return[] = [
'key' => sprintf('bills-paid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -648,7 +648,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,

View File

@@ -113,7 +113,7 @@ class UpdateRequest extends FormRequest
];
$this->booleanFields = ['reconciled'];
$this->arrayFields = ['tags'];
$data = ['batch_submission' => false];
$data = ['batch_submission' => false];
if ($this->has('transactions')) {
$data['transactions'] = $this->getTransactionData();
}

View File

@@ -68,7 +68,7 @@ class CorrectsMetaDataFields extends Command
private function rename(string $original, string $update): void
{
$total = DB::table('journal_meta')->where('name', '=', $original)->update(['name' => $update]);
$total = DB::table('journal_meta')->where('name', '=', $original)->update(['name' => $update]);
$this->count += $total;
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
/*
* RollbackSingleMigration.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class RollbacksSingleMigration extends Command
{
use ShowsFriendlyMessages;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'correction:rollback-single-migration {--force}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes the last entry from the migration table. ';
/**
* Execute the console command.
*/
public function handle(): int
{
$entry = DB::table('migrations')->orderBy('id', 'DESC')->first();
if (null === $entry) {
$this->friendlyError('There are no more database migrations to rollback.');
return Command::FAILURE;
}
$this->friendlyLine(sprintf('This command will remove the database migration entry called "%s" from your database.', $entry->migration));
$this->friendlyLine('This does not change the database in anyway. It makes Firefly III forget it made the changes in this particular migration.');
$this->friendlyLine('');
$this->friendlyLine('If you run "php artisan migrate" after doing this, Firefly III will try to run the database migration again.');
$this->friendlyLine('Missing tables or indices will be created.');
$this->friendlyLine('This may not work, or give you warnings, but if you have a botched database it may restore it again.');
$this->friendlyLine('');
$this->friendlyLine('If this doesn\'t work, run the command a few times to remove more rows and try "php artisan migrate" again.');
$this->friendlyLine('');
$res = true;
if (!$this->option('force')) {
$this->friendlyWarning('Use this command at your own risk.');
$res = $this->confirm('Are you sure you want to continue?');
}
if ($res) {
DB::table('migrations')->where('id', (int) $entry->id)->delete();
$this->friendlyInfo(sprintf('Database migration #%d ("%s") is deleted.', $entry->id, $entry->migration));
$this->friendlyLine('');
$this->friendlyLine('Try running "php artisan migrate" now.');
$this->friendlyLine('');
}
if (!$res) {
$this->friendlyError('User cancelled, will not delete anything.');
}
return Command::SUCCESS;
}
}

View File

@@ -110,7 +110,7 @@ class UpgradesBillsToRules extends Command
'active' => true,
'strict' => false,
'stop_processing' => false, // field is no longer used.
'title' => (string) trans('firefly.rule_for_bill_title', ['name' => $bill->name], $languageString),
'title' => (string) trans('firefly.rule_for_bill_title', ['name' => $bill->name], $languageString),
'description' => (string) trans('firefly.rule_for_bill_description', ['name' => $bill->name], $languageString),
'trigger' => 'store-journal',
'triggers' => [['type' => 'description_contains', 'value' => $match]],

View File

@@ -36,6 +36,7 @@ class UpdatedExistingAccount extends Event
* Create a new event instance.
*/
public function __construct(
public Account $account
public Account $account,
public array $oldData
) {}
}

View File

@@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
/*
* TriggeredStoredTransactionGroup.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Events\Model\TransactionGroup;
use FireflyIII\Events\Event;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionGroup;
use Illuminate\Queue\SerializesModels;
/**
* @deprecated
*/
class TriggeredStoredTransactionGroup extends Event
{
use SerializesModels;
public ?RuleGroup $ruleGroup = null;
/**
* Create a new event instance.
*/
public function __construct(
public TransactionGroup $transactionGroup,
?RuleGroup $ruleGroup = null
) {
$this->ruleGroup = $ruleGroup;
}
}

View File

@@ -157,7 +157,7 @@ class Handler extends ExceptionHandler
return response()->json([
'message' => sprintf('Validation exception: %s', $e->getMessage()),
'errors' => ['field' => 'Field is invalid'],
'errors' => ['field' => 'Field is invalid'],
], $errorCode);
}

View File

@@ -433,7 +433,7 @@ class GroupCollector implements GroupCollectorInterface
*/
public function getGroups(): Collection
{
Log::debug('Now in getGroups()');
// Log::debug('Now in getGroups()');
if ($this->expandGroupSearch) {
// get group ID's for the query:
$groupIds = $this->getCollectedGroupIds();
@@ -556,7 +556,7 @@ class GroupCollector implements GroupCollectorInterface
if (0 !== count($journalIds)) {
// make all integers.
$integerIDs = array_map(intval(...), $journalIds);
Log::debug(sprintf('GroupCollector: setJournalIds: %s', implode(', ', $integerIDs)));
// Log::debug(sprintf('GroupCollector: setJournalIds: %s', implode(', ', $integerIDs)));
$this->query->whereIn('transaction_journals.id', $integerIDs);
}

View File

@@ -72,7 +72,7 @@ class DeleteController extends Controller
}
$typeName = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
$subTitle = (string) trans(sprintf('firefly.delete_%s_account', $typeName), ['name' => $account->name]);
$subTitle = (string) trans(sprintf('firefly.delete_%s_account', $typeName), ['name' => $account->name]);
$accountList = app('expandedform')->makeSelectListWithEmpty($this->repository->getAccountsByType([$account->accountType->type]));
$objectType = $typeName;
unset($accountList[$account->id]);

View File

@@ -86,7 +86,7 @@ class EditController extends Controller
}
$objectType = config('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
$subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
$roles = $this->getRoles();
$liabilityTypes = $this->getLiabilityTypes();

View File

@@ -137,7 +137,7 @@ class ReconcileController extends Controller
);
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);
// various links
$transactionsUrl = route('accounts.reconcile.transactions', [$account->id, '%start%', '%end%']);

View File

@@ -219,7 +219,7 @@ class ShowController extends Controller
$page = (int) $request->get('page');
$pageSize = (int) Preferences::get('listPageSize', 50)->data;
$currency = $this->repository->getAccountCurrency($account) ?? $this->primaryCurrency;
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
$periods = new Collection();
$end->endOfDay();

View File

@@ -94,7 +94,7 @@ class LinkController extends Controller
}
Log::channel('audit')->info(sprintf('User wants to delete link type #%d', $linkType->id));
$subTitle = (string) trans('firefly.delete_link_type', ['name' => $linkType->name]);
$subTitle = (string) trans('firefly.delete_link_type', ['name' => $linkType->name]);
$otherTypes = $this->repository->get();
$count = $this->repository->countJournals($linkType);
$moveTo = [];
@@ -141,7 +141,7 @@ class LinkController extends Controller
return redirect(route('settings.links.index'));
}
$subTitle = (string) trans('firefly.edit_link_type', ['name' => $linkType->name]);
$subTitle = (string) trans('firefly.edit_link_type', ['name' => $linkType->name]);
$subTitleIcon = 'fa-link';
Log::channel('audit')->info(sprintf('User wants to edit link type #%d', $linkType->id));
@@ -181,7 +181,7 @@ class LinkController extends Controller
*/
public function show(LinkType $linkType): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.overview_for_link', ['name' => $linkType->name]);
$subTitle = (string) trans('firefly.overview_for_link', ['name' => $linkType->name]);
$subTitleIcon = 'fa-link';
$links = $this->repository->getJournalLinks($linkType);

View File

@@ -135,7 +135,7 @@ class UserController extends Controller
}
session()->forget('users.edit.fromUpdate');
$subTitle = (string) trans('firefly.edit_user', ['email' => $user->email]);
$subTitle = (string) trans('firefly.edit_user', ['email' => $user->email]);
$subTitleIcon = 'fa-user-o';
$currentUser = auth()->user();
$isAdmin = $this->repository->hasRole($user, 'owner');
@@ -216,7 +216,7 @@ class UserController extends Controller
{
$title = (string) trans('firefly.system_settings');
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = (string) trans('firefly.single_user_administration', ['email' => $user->email]);
$subTitle = (string) trans('firefly.single_user_administration', ['email' => $user->email]);
$subTitleIcon = 'fa-user';
$information = $this->repository->getUserData($user);

View File

@@ -131,7 +131,7 @@ class AttachmentController extends Controller
public function edit(Request $request, Attachment $attachment): Factory|\Illuminate\Contracts\View\View
{
$subTitleIcon = 'fa-pencil';
$subTitle = (string) trans('firefly.edit_attachment', ['name' => $attachment->filename]);
$subTitle = (string) trans('firefly.edit_attachment', ['name' => $attachment->filename]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('attachments.edit.fromUpdate')) {

View File

@@ -251,7 +251,7 @@ class LoginController extends Controller
*/
protected function sendFailedLoginResponse(Request $request): void
{
$exception = ValidationException::withMessages([$this->username() => [trans('auth.failed')]]);
$exception = ValidationException::withMessages([$this->username() => [trans('auth.failed')]]);
$exception->redirectTo = route('login');
throw $exception;

View File

@@ -74,15 +74,15 @@ class IndexController extends Controller
$this->cleanupObjectGroups();
$this->repository->correctOrder();
$this->repository->correctTransfers();
$start = session('start');
$end = session('end');
$start = session('start')->clone();
$end = session('end')->clone();
$viewRange = Preferences::get('viewRange', '1M')->data;
// give the end some extra space when the user has last7, last30 or last90.
if ('last7' === $viewRange || 'last30' === $viewRange) {
$end->addDays(30);
}
if ('last90') {
if ('last90' === $viewRange) {
$end->addDays(90);
}

View File

@@ -71,7 +71,7 @@ class EditController extends Controller
*/
public function edit(Request $request, Budget $budget): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.edit_budget', ['name' => $budget->name]);
$subTitle = (string) trans('firefly.edit_budget', ['name' => $budget->name]);
$autoBudget = $this->repository->getAutoBudget($budget);
// auto budget types

View File

@@ -73,7 +73,7 @@ class BillController extends Controller
*/
foreach ($paid as $info) {
$amount = $info['sum'];
$label = (string) trans('firefly.paid_in_currency', ['currency' => $info['name']]);
$label = (string) trans('firefly.paid_in_currency', ['currency' => $info['name']]);
$chartData[$label] = ['amount' => $amount, 'currency_symbol' => $info['symbol'], 'currency_code' => $info['code']];
}
@@ -82,7 +82,7 @@ class BillController extends Controller
*/
foreach ($unpaid as $info) {
$amount = $info['sum'];
$label = (string) trans('firefly.unpaid_in_currency', ['currency' => $info['name']]);
$label = (string) trans('firefly.unpaid_in_currency', ['currency' => $info['name']]);
$chartData[$label] = ['amount' => $amount, 'currency_symbol' => $info['symbol'], 'currency_code' => $info['code']];
}

View File

@@ -505,14 +505,14 @@ class BudgetController extends Controller
$preferredRange = Navigation::preferredRangeFormat($start, $end);
$chartData = [
[
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency->name]),
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency->name]),
'type' => 'bar',
'entries' => [],
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
],
[
'label' => (string) trans('firefly.box_budgeted_in_currency', ['currency' => $currency->name]),
'label' => (string) trans('firefly.box_budgeted_in_currency', ['currency' => $currency->name]),
'type' => 'bar',
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,

View File

@@ -171,7 +171,7 @@ class BudgetReportController extends Controller
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_budget', ['budget' => $budget->name]),
(string) trans('firefly.spent_in_specific_budget', ['budget' => $budget->name]),
$currency['currency_name']
),
'type' => 'bar',

View File

@@ -216,7 +216,7 @@ class CategoryReportController extends Controller
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_category', ['category' => $category->name]),
(string) trans('firefly.spent_in_specific_category', ['category' => $category->name]),
$currency['currency_name']
),
'type' => 'bar',
@@ -243,7 +243,7 @@ class CategoryReportController extends Controller
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.earned_in_specific_category', ['category' => $category->name]),
(string) trans('firefly.earned_in_specific_category', ['category' => $category->name]),
$currency['currency_name']
),
'type' => 'bar',

View File

@@ -162,11 +162,7 @@ class DoubleReportController extends Controller
$name = $this->getCounterpartName($accounts, $account->id, $account->name, $account->iban);
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_double', ['account' => $name]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.spent_in_specific_double', ['account' => $name]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
@@ -188,11 +184,7 @@ class DoubleReportController extends Controller
$name = $this->getCounterpartName($accounts, $account->id, $account->name, $account->iban);
$chartData[$earnedKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.earned_in_specific_double', ['account' => $name]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.earned_in_specific_double', ['account' => $name]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],

View File

@@ -212,7 +212,7 @@ class ReportController extends Controller
foreach ($data as $currency) {
Log::debug(sprintf('Now processing currency "%s"', $currency['currency_name']));
$income = [
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
'type' => 'bar',
'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green
'currency_id' => $currency['currency_id'],
@@ -221,7 +221,7 @@ class ReportController extends Controller
'entries' => [],
];
$expense = [
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
'type' => 'bar',
'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
'currency_id' => $currency['currency_id'],

View File

@@ -220,11 +220,7 @@ class TagReportController extends Controller
// add things to chart Data for each currency:
$spentKey = sprintf('%d-spent', $currency['currency_id']);
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
@@ -247,11 +243,7 @@ class TagReportController extends Controller
// add things to chart Data for each currency:
$spentKey = sprintf('%d-earned', $currency['currency_id']);
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],

View File

@@ -183,11 +183,11 @@ class RecurrenceController extends Controller
$result = [
'daily' => ['label' => (string) trans('firefly.recurring_daily'), 'selected' => str_starts_with($preSelected, 'daily')],
$weekly => [
'label' => (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),
'label' => (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),
'selected' => str_starts_with($preSelected, 'weekly'),
],
$monthly => [
'label' => (string) trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]),
'label' => (string) trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]),
'selected' => str_starts_with($preSelected, 'monthly'),
],
$ndom => [
@@ -195,7 +195,7 @@ class RecurrenceController extends Controller
'selected' => str_starts_with($preSelected, 'ndom'),
],
$yearly => [
'label' => (string) trans('firefly.recurring_yearly', ['date' => $yearlyDate]),
'label' => (string) trans('firefly.recurring_yearly', ['date' => $yearlyDate]),
'selected' => str_starts_with($preSelected, 'yearly'),
],
];

View File

@@ -61,7 +61,7 @@ class DeleteController extends Controller
*/
public function delete(ObjectGroup $objectGroup): Factory|View
{
$subTitle = (string) trans('firefly.delete_object_group', ['title' => $objectGroup->title]);
$subTitle = (string) trans('firefly.delete_object_group', ['title' => $objectGroup->title]);
$piggyBanks = $objectGroup->piggyBanks()->count();
// put previous url in session

View File

@@ -64,7 +64,7 @@ class EditController extends Controller
*/
public function edit(ObjectGroup $objectGroup): Factory|View
{
$subTitle = (string) trans('firefly.edit_object_group', ['title' => $objectGroup->title]);
$subTitle = (string) trans('firefly.edit_object_group', ['title' => $objectGroup->title]);
$subTitleIcon = 'fa-pencil';
if (true !== session('object-groups.edit.fromUpdate')) {

View File

@@ -70,7 +70,7 @@ class EditController extends Controller
*/
public function edit(PiggyBank $piggyBank): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.update_piggy_title', ['name' => $piggyBank->name]);
$subTitle = (string) trans('firefly.update_piggy_title', ['name' => $piggyBank->name]);
$subTitleIcon = 'fa-pencil';
$note = $piggyBank->notes()->first();
// Flash some data to fill the form.

View File

@@ -152,7 +152,7 @@ class ProfileController extends Controller
}
$repository->unblockUser($user);
// also remove the "remote_guard_alt_email" preference.
Preferences::delete('remote_guard_alt_email');
Preferences::deleteForUser($user, 'remote_guard_alt_email');
// return to log in.
session()->flash('success', (string) trans('firefly.login_with_new_email'));

View File

@@ -80,7 +80,7 @@ class CreateController extends Controller
public function create(Request $request, ?RuleGroup $ruleGroup = null): Factory|\Illuminate\Contracts\View\View
{
$this->createDefaultRuleGroup();
$preFilled = ['strict' => true];
$preFilled = ['strict' => true];
$oldTriggers = [];
$oldActions = [];
@@ -159,7 +159,7 @@ class CreateController extends Controller
$this->createDefaultRuleGroup();
$preFilled = [
'strict' => true,
'title' => (string) trans('firefly.new_rule_for_bill_title', ['name' => $bill->name]),
'title' => (string) trans('firefly.new_rule_for_bill_title', ['name' => $bill->name]),
'description' => (string) trans('firefly.new_rule_for_bill_description', ['name' => $bill->name]),
];
@@ -221,7 +221,7 @@ class CreateController extends Controller
// collect pre-filled information:
$preFilled = [
'strict' => true,
'title' => (string) trans('firefly.new_rule_for_journal_title', ['description' => $journal->description]),
'title' => (string) trans('firefly.new_rule_for_journal_title', ['description' => $journal->description]),
'description' => (string) trans('firefly.new_rule_for_journal_description', ['description' => $journal->description]),
];

View File

@@ -70,7 +70,7 @@ class EditController extends Controller
$subTitle = (string) trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]);
$hasOldInput = null !== $request->old('_token');
$preFilled = ['active' => $hasOldInput ? (bool) $request->old('active') : $ruleGroup->active];
$preFilled = ['active' => $hasOldInput ? (bool) $request->old('active') : $ruleGroup->active];
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('rule-groups.edit.fromUpdate')) {
$this->rememberPreviousUrl('rule-groups.edit.url');

View File

@@ -26,16 +26,15 @@ namespace FireflyIII\Http\Controllers\RuleGroup;
use Carbon\Carbon;
use Exception;
use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
@@ -45,6 +44,7 @@ use Illuminate\View\View;
class ExecutionController extends Controller
{
private readonly AccountRepositoryInterface $repository;
private readonly RuleGroupRepositoryInterface $ruleGroupRepository;
/**
* ExecutionController constructor.
@@ -52,11 +52,13 @@ class ExecutionController extends Controller
public function __construct()
{
parent::__construct();
$this->repository = app(AccountRepositoryInterface::class);
$this->repository = app(AccountRepositoryInterface::class);
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
$this->middleware(function ($request, $next) {
app('view')->share('title', (string) trans('firefly.rules'));
app('view')->share('mainTitleIcon', 'fa-random');
$this->repository->setUser(auth()->user());
$this->ruleGroupRepository->setUser(auth()->user());
return $next($request);
});
@@ -70,44 +72,37 @@ class ExecutionController extends Controller
public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse
{
Log::debug(sprintf('You have selected rule group #%d', $ruleGroup->id));
// Get parameters specified by the user
$accounts = $request->get('accounts');
$set = new Collection();
if (is_array($accounts)) {
$set = $this->repository->getAccountsById($accounts);
}
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
if (count($set) > 0) {
$collector->setAccounts($set);
}
// start code
/** @var User $user */
$user = auth()->user();
$accounts = implode(',', $request->get('accounts'));
// create new rule engine:
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($user);
// add date operators.
if (null !== $request->get('start')) {
$startDate = new Carbon($request->get('start'));
$collector->setStart($startDate);
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
}
if (null !== $request->get('end')) {
$endDate = new Carbon($request->get('end'));
$collector->setEnd($endDate);
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
}
$final = $collector->getGroups();
$ids = $final->pluck('id')->toArray();
Log::debug(sprintf('Found %d groups collected from %d account(s)', $final->count(), $set->count()));
foreach (array_chunk($ids, 1337) as $setOfIds) {
Log::debug(sprintf('Now processing %d groups', count($setOfIds)));
$groups = TransactionGroup::whereIn('id', $setOfIds)->get();
/** @var TransactionGroup $group */
foreach ($groups as $group) {
Log::debug(sprintf('Processing group #%d.', $group->id));
event(new TriggeredStoredTransactionGroup($group, $ruleGroup));
}
}
// add extra operators:
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
// set rules:
$rules = $this->ruleGroupRepository->getActiveRules($ruleGroup);
$newRuleEngine->setRules($rules);
$newRuleEngine->fire();
$resultCount = $newRuleEngine->getResults();
// Tell the user that the job is queued
session()->flash('success', (string) trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title]));
session()->flash('success', trans_choice('firefly.applied_rule_group_selection', $resultCount, ['title' => $ruleGroup->title]));
return redirect()->route('rules.index');
}

View File

@@ -87,7 +87,7 @@ class SearchController extends Controller
$excludedWords = $searcher->getExcludedWords();
$operators = $searcher->getOperators();
$invalidOperators = $searcher->getInvalidOperators();
$subTitle = (string) trans('breadcrumbs.search_result', ['query' => $fullQuery]);
$subTitle = (string) trans('breadcrumbs.search_result', ['query' => $fullQuery]);
return view('search.index', [
'words' => $words,

View File

@@ -61,7 +61,7 @@ class InstallController extends Controller
'migrate' => ['--seed' => true, '--force' => true],
'generate-keys' => [], // an exception :(
'firefly-iii:upgrade-database' => [],
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
'firefly-iii:verify-security-alerts' => [],
];

View File

@@ -134,7 +134,7 @@ class TagController extends Controller
*/
public function edit(Tag $tag): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.edit_tag', ['tag' => $tag->tag]);
$subTitle = (string) trans('firefly.edit_tag', ['tag' => $tag->tag]);
$subTitleIcon = 'fa-tag';
$location = $this->repository->getLocation($tag);
@@ -289,7 +289,7 @@ class TagController extends Controller
$page = (int) $request->get('page');
$pageSize = (int) Preferences::get('listPageSize', 50)->data;
$periods = [];
$subTitle = (string) trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
$subTitle = (string) trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
$start = $this->repository->firstUseDate($tag) ?? today(config('app.timezone'));
$end = $this->repository->lastUseDate($tag) ?? today(config('app.timezone'));
$attachments = $this->repository->getAttachments($tag);

View File

@@ -103,7 +103,7 @@ class ConvertController extends Controller
$groupTitle = $group->title ?? $first->description;
$groupArray = $transformer->transformObject($group);
$subTitle = (string) trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]);
$subTitle = (string) trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]);
$subTitleIcon = 'fa-exchange';
// get a list of asset accounts and liabilities and stuff, in various combinations:

View File

@@ -77,7 +77,7 @@ class DeleteController extends Controller
throw new NotFoundHttpException();
}
$objectType = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = (string) trans('firefly.delete_'.$objectType, ['description' => $group->title ?? $journal->description]);
$subTitle = (string) trans('firefly.delete_'.$objectType, ['description' => $group->title ?? $journal->description]);
$previous = Steam::getSafePreviousUrl();
// put previous url in session
Log::debug('Will try to remember previous URL');

View File

@@ -91,7 +91,7 @@ class EditController extends Controller
$title = $transactionGroup->transactionJournals()->count() > 1
? $transactionGroup->title
: $transactionGroup->transactionJournals()->first()->description;
$subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]);
$subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]);
$subTitleIcon = 'fa-plus';
$cash = $repository->getCashAccount();
$previousUrl = $this->rememberPreviousUrl('transactions.edit.url');

View File

@@ -194,7 +194,12 @@ class ShowController extends Controller
foreach ($group['transactions'] as $transaction) {
// add normal amount:
$symbol = $transaction['currency_symbol'];
$amounts[$symbol] ??= ['amount' => '0', 'symbol' => $symbol, 'decimal_places' => $transaction['currency_decimal_places']];
$amounts[$symbol] ??= [
'amount' => '0',
'symbol' => $symbol,
'decimal_places' => $transaction['currency_decimal_places'],
'approximate' => false,
];
$amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], (string) $transaction['amount']);
// add foreign amount:
@@ -207,17 +212,23 @@ class ShowController extends Controller
$foreignSymbol = $transaction['foreign_currency_symbol'];
$amounts[$foreignSymbol] ??= [
'amount' => '0',
'approximate' => false,
'symbol' => $foreignSymbol,
'decimal_places' => $transaction['foreign_currency_decimal_places'],
];
$amounts[$foreignSymbol]['amount'] = bcadd($amounts[$foreignSymbol]['amount'], (string) $transaction['foreign_amount']);
}
// add primary currency amount
if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) {
// add primary currency amount, but only if it is not the foreign amount or the current one.
if (
null !== $transaction['pc_amount']
&& $transaction['currency_id'] !== $this->primaryCurrency->id
&& $transaction['foreign_currency_code'] !== $this->primaryCurrency->code
) {
// same for foreign currency:
$primarySymbol = $this->primaryCurrency->symbol;
$amounts[$primarySymbol] ??= [
'amount' => '0',
'approximate' => true,
'symbol' => $this->primaryCurrency->symbol,
'decimal_places' => $this->primaryCurrency->decimal_places,
];

View File

@@ -78,7 +78,7 @@ class EditController extends Controller
}
$subTitleIcon = 'fa-pencil';
$subTitle = (string) trans('breadcrumbs.edit_currency', ['name' => $currency->name]);
$subTitle = (string) trans('breadcrumbs.edit_currency', ['name' => $currency->name]);
$currency->symbol = htmlentities($currency->symbol);
// is currently enabled (for this user?)
@@ -91,7 +91,7 @@ class EditController extends Controller
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
$preFilled = ['enabled' => $hasOldInput ? (bool) $request->old('enabled') : $enabled];
$preFilled = ['enabled' => $hasOldInput ? (bool) $request->old('enabled') : $enabled];
$request->session()->flash('preFilled', $preFilled);
Log::channel('audit')->info('Edit currency.', $currency->toArray());

View File

@@ -39,7 +39,7 @@ class EditController extends Controller
public function edit(UserGroup $userGroup)
{
$title = (string) trans('firefly.administrations_page_title');
$subTitle = (string) trans('firefly.administrations_page_edit_sub_title', ['title' => $userGroup->title]);
$subTitle = (string) trans('firefly.administrations_page_edit_sub_title', ['title' => $userGroup->title]);
$mainTitleIcon = 'fa-book';
Log::debug(sprintf('Now at %s', __METHOD__));

View File

@@ -167,7 +167,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
/** @var BudgetLimitRepositoryInterface $repository */
$repository = app(BudgetLimitRepositoryInterface::class);
$repository->setUserGroup($autoBudget->budget->user->userGroup);
$repository->setUser($autoBudget->budget->user);
$budgetLimit = $repository->store([
'currency_id' => $autoBudget->transaction_currency_id,

View File

@@ -29,7 +29,11 @@ use FireflyIII\Events\Model\Account\UpdatedExistingAccount;
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
use FireflyIII\Models\Account;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
@@ -40,6 +44,89 @@ class UpdatesAccountInformation implements ShouldQueue
{
$this->recalculateCredit($event->account);
$this->updateVirtualBalance($event->account);
if ($event instanceof UpdatedExistingAccount) {
$this->renameRules($event->account, $event->oldData);
}
}
private function correctRuleActions(Account $account, array $oldData, Rule $rule): void
{
$fields = ['set_source_account', 'set_destination_account'];
Log::debug(sprintf('Check if rule #%d actions reference account #%d "%s"', $rule->id, $account->id, $account->name));
$fixed = 0;
/** @var RuleAction $action */
foreach ($rule->ruleActions as $action) {
// fix name:
if ($oldData['name'] === $action->action_value && in_array($action->action_type, $fields, true)) {
Log::debug(sprintf('Rule action #%d "%s" has old account name, replace with new.', $action->id, $action->action_type));
$action->action_value = $account->name;
$action->save();
++$fixed;
}
}
Log::debug(sprintf('Corrected %d action(s) for rule #%d', $fixed, $rule->id));
}
private function correctRuleTriggers(Account $account, array $oldData, Rule $rule): void
{
$nameFields = [
'source_account_is',
'source_account_contains',
'source_account_ends',
'source_account_starts',
'destination_account_is',
'destination_account_contains',
'destination_account_ends',
'destination_account_starts',
'account_is',
'account_contains',
'account_ends',
'account_starts',
];
$numberFields = [
'source_account_nr_is',
'source_account_nr_contains',
'source_account_nr_ends',
'source_account_nr_starts',
'destination_account_nr_is',
'destination_account_nr_contains',
'destination_account_nr_starts',
'account_nr_is',
'account_nr_contains',
'account_nr_ends',
'account_nr_starts',
];
Log::debug(sprintf('Check if rule #%d triggers reference account #%d "%s"', $rule->id, $account->id, $account->name));
$fixed = 0;
/** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) {
// fix name:
if ($oldData['name'] === $trigger->trigger_value && in_array($trigger->trigger_type, $nameFields, true)) {
Log::debug(sprintf('Rule trigger #%d "%s" has old account name, replace with new.', $trigger->id, $trigger->trigger_type));
$trigger->trigger_value = $account->name;
$trigger->save();
++$fixed;
}
// fix IBAN:
if ($oldData['iban'] === $trigger->trigger_value && in_array($trigger->trigger_type, $numberFields, true)) {
Log::debug(sprintf('Rule trigger #%d "%s" has old account IBAN, replace with new.', $trigger->id, $trigger->trigger_type));
$trigger->trigger_value = $account->iban;
$trigger->save();
++$fixed;
}
// fix account number: // account_number
if ($oldData['account_number'] === $trigger->trigger_value && in_array($trigger->trigger_type, $numberFields, true)) {
Log::debug(sprintf('Rule trigger #%d "%s" has old account account_number, replace with new.', $trigger->id, $trigger->trigger_type));
$trigger->trigger_value = $account->iban;
$trigger->save();
++$fixed;
}
}
Log::debug(sprintf('Corrected %d trigger(s) for rule #%d', $fixed, $rule->id));
}
private function recalculateCredit(Account $account): void
@@ -52,6 +139,20 @@ class UpdatesAccountInformation implements ShouldQueue
$object->recalculate();
}
private function renameRules(Account $account, array $oldData): void
{
Log::debug('Updated account, will now correct rules.');
$repository = app(RuleRepositoryInterface::class);
$repository->setUser($account->user);
$rules = $repository->getAll();
/** @var Rule $rule */
foreach ($rules as $rule) {
$this->correctRuleTriggers($account, $oldData, $rule);
$this->correctRuleActions($account, $oldData, $rule);
}
}
private function updateVirtualBalance(Account $account): void
{
Log::debug('Will updateVirtualBalance');

View File

@@ -29,13 +29,18 @@ trait SupportsGroupProcessingTrait
protected function processRules(Collection $set, string $type): void
{
Log::debug(sprintf('Will now processRules("%s") for %d journal(s)', $type, $set->count()));
if (0 === $set->count()) {
return;
}
$array = $set->pluck('id')->toArray();
/** @var TransactionJournal $first */
$first = $set->first();
$journalIds = implode(',', $array);
$user = $first->user;
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// collect rules:
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
@@ -63,10 +68,16 @@ trait SupportsGroupProcessingTrait
return;
}
// find the earliest date in the set, based on date and _internal_previous_date
$earliest = $objects->transactionJournals->pluck('date')->sort()->first();
$fromInternalDate = $this->getFromInternalDate($objects->transactionJournals->pluck('id')->toArray());
$earliest = $fromInternalDate->lt($earliest) ? $fromInternalDate : $earliest;
if (0 === $objects->accounts->count()) {
return;
}
$earliest = today()->subDays(2);
if ($objects->transactionJournals->count() > 0) {
// find the earliest date in the set, based on date and _internal_previous_date
$earliest = $objects->transactionJournals->pluck('date')->sort()->first();
$fromInternalDate = $this->getFromInternalDate($objects->transactionJournals->pluck('id')->toArray());
$earliest = $fromInternalDate->lt($earliest) ? $fromInternalDate : $earliest;
}
Log::debug(sprintf('Found earliest date: %s', $earliest->toW3cString()));
Log::debug('Found accounts to process', $objects->accounts->pluck('id')->toArray());
@@ -78,6 +89,8 @@ trait SupportsGroupProcessingTrait
{
if (!auth()->check()) {
Log::debug('Will NOT remove period statistics for all objects, because no user detected.');
return;
}
Log::debug('Will now remove period statistics for all objects.');
@@ -122,6 +135,10 @@ trait SupportsGroupProcessingTrait
{
Log::debug(sprintf('Will now create webhook messages for %d group(s)', $groups->count()));
if (0 === $groups->count()) {
return;
}
/** @var TransactionGroup $first */
$first = $groups->first();
$user = $first->user;

View File

@@ -56,7 +56,7 @@ class MFABackupFewLeftNotification extends Notification
*/
public function toMail(User $notifiable): MailMessage
{
$subject = (string) trans('email.mfa_few_backups_left_subject', ['count' => $this->count]);
$subject = (string) trans('email.mfa_few_backups_left_subject', ['count' => $this->count]);
$ip = Request::ip();
$host = Steam::getHostName($ip);
$userAgent = Request::userAgent();

View File

@@ -53,7 +53,7 @@ class MFAManyFailedAttemptsNotification extends Notification
*/
public function toMail(User $notifiable): MailMessage
{
$subject = (string) trans('email.mfa_many_failed_subject', ['count' => $this->count]);
$subject = (string) trans('email.mfa_many_failed_subject', ['count' => $this->count]);
$ip = Request::ip();
$host = Steam::getHostName($ip);
$userAgent = Request::userAgent();

View File

@@ -58,7 +58,7 @@ class SubscriptionsOverdueReminder extends Notification
$info = [];
$count = 0;
foreach ($this->overdue as $item) {
$current = ['bill' => $item['bill']];
$current = ['bill' => $item['bill']];
$current['pay_dates'] = array_map(static fn (string $date): string => new Carbon($date)->isoFormat((string) trans(
'config.month_and_day_moment_js'
)), $item['dates']['pay_dates']);

View File

@@ -178,7 +178,7 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$currencies = [$primaryCurrency->id => $primaryCurrency];
$currencies = [$primaryCurrency->id => $primaryCurrency];
$report = ['accounts' => [], 'sums' => []];
/** @var array $journal */
@@ -232,7 +232,7 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$currencies = [$primaryCurrency->id => $primaryCurrency];
$currencies = [$primaryCurrency->id => $primaryCurrency];
$report = ['accounts' => [], 'sums' => []];
/** @var array $journal */

View File

@@ -91,7 +91,14 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup
/** @var BudgetLimit $budgetLimit */
foreach ($set as $budgetLimit) {
$result = bcadd((string) $budgetLimit->amount, $result);
if ($budgetLimit->start_date->isSameDay($start) && $budgetLimit->end_date->isSameDay($end)) {
$result = bcadd((string) $budgetLimit->amount, $result);
continue;
}
$period = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
$amountPerDay = $this->getDailyAmount($budgetLimit);
$result = bcadd($result, bcmul((string) $period->length(), $amountPerDay));
}
return $result;

View File

@@ -85,7 +85,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
?Collection $budgets = null,
?TransactionCurrency $currency = null
): array {
Log::debug(sprintf('Start of %s(%s, %s, array, array, "%s").', __METHOD__, $start->toW3cString(), $end->toW3cString(), $currency?->code));
// Log::debug(sprintf('Start of %s(%s, %s, array, array, "%s").', __METHOD__, $start->toW3cString(), $end->toW3cString(), $currency?->code));
// this collector excludes all transfers TO liabilities (which are also withdrawals)
// because those expenses only become expenses once they move from the liability to the friend.
// 2024-12-24 disable the exclusion for now.
@@ -204,7 +204,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$currencySymbol = $primaryCurrency->symbol;
$currencyDecimalPlaces = $primaryCurrency->decimal_places;
$converter = new ExchangeRateConverter();
$currencies = [$currencyId => $primaryCurrency];
$currencies = [$currencyId => $primaryCurrency];
foreach ($journals as $journal) {
$amount = Steam::negative($journal['amount']);
@@ -275,7 +275,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
TransactionCurrency $transactionCurrency,
bool $convertToPrimary = false
): array {
Log::debug(sprintf('Start of %s.', __METHOD__));
// Log::debug(sprintf('Start of %s.', __METHOD__));
$summarizer = new TransactionSummarizer($this->user);
$summarizer->setConvertToPrimary($convertToPrimary);
@@ -290,7 +290,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
public function sumCollectedExpensesByBudget(array $expenses, Budget $budget, bool $convertToPrimary = false): array
{
Log::debug(sprintf('Start of %s.', __METHOD__));
// Log::debug(sprintf('Start of %s.', __METHOD__));
$summarizer = new TransactionSummarizer($this->user);
$summarizer->setConvertToPrimary($convertToPrimary);
@@ -311,7 +311,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
?TransactionCurrency $currency = null,
bool $convertToPrimary = false
): array {
Log::debug(sprintf('Start of %s(date, date, array, array, "%s", %s).', __METHOD__, $currency?->code, var_export($convertToPrimary, true)));
// Log::debug(sprintf('Start of %s(date, date, array, array, "%s", %s).', __METHOD__, $currency?->code, var_export($convertToPrimary, true)));
// this collector excludes all transfers TO liabilities (which are also withdrawals)
// because those expenses only become expenses once they move from the liability to the friend.
// 2024-12-24 disable the exclusion for now.

View File

@@ -60,6 +60,7 @@ class CategoryRepository implements CategoryRepositoryInterface, UserGroupInterf
public function categoryStartsWith(string $query, int $limit): Collection
{
Log::debug(sprintf('Find a category that starts with "%s"', $query));
$search = $this->user->categories();
if ('' !== $query) {
$search->whereLike('name', sprintf('%s%%', $query));

View File

@@ -372,7 +372,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
public function sumCollectedTransactionsByCategory(array $expenses, Category $category, string $method, bool $convertToPrimary = false): array
{
Log::debug(sprintf('Start of %s.', __METHOD__));
// Log::debug(sprintf('Start of %s.', __METHOD__));
$summarizer = new TransactionSummarizer($this->user);
$summarizer->setConvertToPrimary($convertToPrimary);

View File

@@ -95,6 +95,11 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
#[Override]
public function deleteStatisticsForPrefix(string $prefix, Collection $dates): void
{
if (null === $this->userGroup) {
Log::warning('No user group, so cannot continue.');
return;
}
$count = $this->userGroup
->periodStatistics()
->where(function (Builder $q) use ($dates): void {

View File

@@ -144,7 +144,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
public function getActiveRules(RuleGroup $group): Collection
{
return $group->rules()->where('rules.active', true)->get(['rules.*']);
return $group->rules()->where('rules.active', true)->orderBy('rules.order', 'ASC')->get(['rules.*']);
}
public function getActiveStoreRules(RuleGroup $group): Collection
@@ -195,21 +195,21 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
if (null === $filter) {
return $groups;
}
Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
// Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
return $groups->map(static function (RuleGroup $group) use ($filter): RuleGroup { // @phpstan-ignore-line
Log::debug(sprintf('Now filtering group #%d', $group->id));
// Log::debug(sprintf('Now filtering group #%d', $group->id));
// filter the rules in the rule group:
$group->rules = $group->rules->filter(static function (Rule $rule) use ($filter, $group): bool {
Log::debug(sprintf('Now filtering rule #%d', $rule->id));
$group->rules = $group->rules->filter(static function (Rule $rule) use ($filter): bool {
// Log::debug(sprintf('Now filtering rule #%d', $rule->id));
foreach ($rule->ruleTriggers as $trigger) {
if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) {
Log::debug(sprintf('Rule #%d triggers on %s, include it in rule group #%d.', $rule->id, $filter, $group->id));
// Log::debug(sprintf('Rule #%d triggers on %s, include it in rule group #%d.', $rule->id, $filter, $group->id));
return true;
}
}
Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
// Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
return false;
});
@@ -251,18 +251,18 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
return $groups->map(static function (RuleGroup $group) use ($filter): RuleGroup { // @phpstan-ignore-line
Log::debug(sprintf('Now filtering group #%d', $group->id));
// Log::debug(sprintf('Now filtering group #%d', $group->id));
// filter the rules in the rule group:
$group->rules = $group->rules->filter(static function (Rule $rule) use ($filter, $group): bool {
Log::debug(sprintf('Now filtering rule #%d', $rule->id));
$group->rules = $group->rules->filter(static function (Rule $rule) use ($filter): bool {
// Log::debug(sprintf('Now filtering rule #%d', $rule->id));
foreach ($rule->ruleTriggers as $trigger) {
if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) {
Log::debug(sprintf('Rule #%d triggers on %s, include it in rule group #%d.', $rule->id, $filter, $group->id));
// Log::debug(sprintf('Rule #%d triggers on %s, include it in rule group #%d.', $rule->id, $filter, $group->id));
return true;
}
}
Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
// Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
return false;
});

View File

@@ -27,6 +27,10 @@ namespace FireflyIII\Services\Internal\Support;
use Carbon\Carbon;
use Deprecated;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup;
use FireflyIII\Exceptions\DuplicateTransactionException;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountMetaFactory;
@@ -58,8 +62,8 @@ trait AccountServiceTrait
if (null === $iban) {
return null;
}
$data = ['iban' => $iban];
$rules = ['iban' => 'required|iban'];
$data = ['iban' => $iban];
$rules = ['iban' => 'required|iban'];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
Log::info(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban));
@@ -224,7 +228,7 @@ trait AccountServiceTrait
$sourceId = $account->id;
$sourceName = null;
$destId = null;
$destName = trans('firefly.liability_credit_description', ['account' => $account->name], $language);
$destName = trans('firefly.liability_credit_description', ['account' => $account->name], $language);
}
// amount must be positive for the transaction to work.
@@ -253,7 +257,7 @@ trait AccountServiceTrait
'order' => 0,
'amount' => $amount,
'foreign_amount' => null,
'description' => trans('firefly.liability_credit_description', ['account' => $account->name]),
'description' => trans('firefly.liability_credit_description', ['account' => $account->name]),
'budget_id' => null,
'budget_name' => null,
'category_id' => null,
@@ -348,7 +352,7 @@ trait AccountServiceTrait
'order' => 0,
'amount' => $amount,
'foreign_amount' => null,
'description' => trans('firefly.initial_balance_description', ['account' => $account->name]),
'description' => trans('firefly.initial_balance_description', ['account' => $account->name]),
'budget_id' => null,
'budget_name' => null,
'category_id' => null,
@@ -384,15 +388,15 @@ trait AccountServiceTrait
protected function createOBGroupV2(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup
{
Log::debug('Now going to create an OB group.');
$language = Preferences::getForUser($account->user, 'language', 'en_US')->data;
$language = Preferences::getForUser($account->user, 'language', 'en_US')->data;
if (is_array($language)) {
$language = 'en_US';
}
$language = (string) $language;
$sourceId = null;
$sourceName = null;
$destId = null;
$destName = null;
$language = (string) $language;
$sourceId = null;
$sourceName = null;
$destId = null;
$destName = null;
// amount is positive.
if (1 === bccomp($openingBalance, '0')) {
@@ -414,16 +418,16 @@ trait AccountServiceTrait
}
// make amount positive, regardless:
$amount = Steam::positive($openingBalance);
$amount = Steam::positive($openingBalance);
// get or grab currency:
$currency = $this->accountRepository->getAccountCurrency($account);
$currency = $this->accountRepository->getAccountCurrency($account);
if (null === $currency) {
$currency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
}
// submit to factory:
$submission = [
$submission = [
'group_title' => null,
'user' => $account->user,
'user_group' => $account->user->userGroup,
@@ -440,7 +444,7 @@ trait AccountServiceTrait
'order' => 0,
'amount' => $amount,
'foreign_amount' => null,
'description' => trans('firefly.initial_balance_description', ['account' => $account->name]),
'description' => trans('firefly.initial_balance_description', ['account' => $account->name]),
'budget_id' => null,
'budget_name' => null,
'category_id' => null,
@@ -455,7 +459,7 @@ trait AccountServiceTrait
Log::debug('Going for submission in createOBGroupV2', $submission);
/** @var TransactionGroupFactory $factory */
$factory = app(TransactionGroupFactory::class);
$factory = app(TransactionGroupFactory::class);
$factory->setUser($account->user);
try {
@@ -466,6 +470,13 @@ trait AccountServiceTrait
throw new FireflyException($e->getMessage(), 0, $e);
}
Preferences::mark();
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($group);
$flags = new TransactionGroupEventFlags();
$flags->applyRules = false;
$flags->fireWebhooks = false;
$flags->batchSubmission = false;
event(new CreatedSingleTransactionGroup($flags, $objects));
return $group;
}
@@ -610,23 +621,23 @@ trait AccountServiceTrait
{
Log::debug(sprintf('Now in %s', __METHOD__));
// create if not exists:
$obGroup = $this->getOBGroup($account);
$obGroup = $this->getOBGroup($account);
if (null === $obGroup) {
return $this->createOBGroupV2($account, $openingBalance, $openingBalanceDate);
}
Log::debug('Update OB group');
// if exists, update:
$currency = $this->accountRepository->getAccountCurrency($account);
$currency = $this->accountRepository->getAccountCurrency($account);
if (null === $currency) {
$currency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
}
// simply grab the first journal and change it:
$journal = $this->getObJournal($obGroup);
$obTransaction = $this->getOBTransaction($journal, $account);
$accountTransaction = $this->getNotOBTransaction($journal, $account);
$journal->date = $openingBalanceDate;
$journal = $this->getObJournal($obGroup);
$obTransaction = $this->getOBTransaction($journal, $account);
$accountTransaction = $this->getNotOBTransaction($journal, $account);
$journal->date = $openingBalanceDate;
$journal->transactionCurrency()->associate($currency);
// if amount is negative:
@@ -656,6 +667,13 @@ trait AccountServiceTrait
$journal->save();
$obGroup->refresh();
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($obGroup);
$flags = new TransactionGroupEventFlags();
$flags->applyRules = false;
$flags->fireWebhooks = false;
$flags->batchSubmission = false;
event(new UpdatedSingleTransactionGroup($flags, $objects));
return $obGroup;
}

View File

@@ -78,9 +78,11 @@ class AccountUpdateService
{
Log::debug(sprintf('Now in %s', __METHOD__));
$this->accountRepository->setUser($account->user);
$this->user = $account->user;
$account = $this->updateAccount($account, $data);
$account = $this->updateAccountOrder($account, $data);
$this->user = $account->user;
$oldData = $account->toArray();
$oldData['account_number'] = $this->accountRepository->getMetaValue($account, 'account_number');
$account = $this->updateAccount($account, $data);
$account = $this->updateAccountOrder($account, $data);
// find currency, or use default currency instead.
if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) {
@@ -109,7 +111,7 @@ class AccountUpdateService
// update preferences if inactive:
$this->updatePreferences($account);
event(new UpdatedExistingAccount($account));
event(new UpdatedExistingAccount($account, $oldData));
return $account;
}

View File

@@ -551,7 +551,7 @@ class JournalUpdateService
event(
new TransactionGroupRequestsAuditLogEntry(
$group->user,
$group,
$this->transactionJournal,
'update_amount',
[
'currency_symbol' => $recordCurrency->symbol,
@@ -767,7 +767,7 @@ class JournalUpdateService
event(
new TransactionGroupRequestsAuditLogEntry(
$group->user,
$group,
$this->transactionJournal,
'update_foreign_amount',
[
'currency_symbol' => $recordCurrency->symbol,

View File

@@ -38,6 +38,15 @@ class Calculator
private static ?SplObjectStorage $intervalMap = null; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private static array $intervals = [];
public function isAvailablePeriodicity(Periodicity $periodicity): bool

View File

@@ -81,14 +81,14 @@ class WholePeriodChartGenerator
$code = $currency['currency_code'];
$name = $currency['currency_name'];
$chartData[sprintf('spent-in-%s', $code)] = [
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $name]),
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $name]),
'entries' => [],
'type' => 'bar',
'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
];
$chartData[sprintf('earned-in-%s', $code)] = [
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $name]),
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $name]),
'entries' => [],
'type' => 'bar',
'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green

View File

@@ -94,6 +94,24 @@ class ExportDataGenerator
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
public function __construct()
{
$this->accounts = new Collection();

View File

@@ -49,7 +49,7 @@ class PiggyBankForm
$piggyBanks = $repository->getPiggyBanksWithAmount();
$title = (string) trans('firefly.default_group_title_name');
$array = [];
$subList = [0 => ['group' => ['title' => $title], 'piggies' => [(string) trans('firefly.none_in_select_list')]]];
$subList = [0 => ['group' => ['title' => $title], 'piggies' => [(string) trans('firefly.none_in_select_list')]]];
/** @var PiggyBank $piggy */
foreach ($piggyBanks as $piggy) {
@@ -60,7 +60,7 @@ class PiggyBankForm
$groupTitle = $group->title;
$groupOrder = $group->order;
}
$subList[$groupOrder] ??= ['group' => ['title' => $groupTitle], 'piggies' => []];
$subList[$groupOrder] ??= ['group' => ['title' => $groupTitle], 'piggies' => []];
$subList[$groupOrder]['piggies'][$piggy->id] = $piggy->name;
}
ksort($subList);

View File

@@ -170,7 +170,7 @@ class AccountBalanceGrouped
{
$this->primary = $primary;
$primaryCurrencyId = $primary->id;
$this->currencies = [$primary->id => $primary]; // currency cache
$this->currencies = [$primary->id => $primary]; // currency cache
$this->data[$primaryCurrencyId] = [
'currency_id' => (string) $primaryCurrencyId,
'currency_symbol' => $primary->symbol,

View File

@@ -157,7 +157,7 @@ trait GetConfigurationData
// previous year:
$yearBegin = today(config('app.timezone'))->subYear()->startOfYear();
$index = (string) trans('firefly.previous_year', ['year' => $yearBegin->year]);
$index = (string) trans('firefly.previous_year', ['year' => $yearBegin->year]);
$ranges[$index] = [$yearBegin, $yearBegin->clone()->endOfYear()];
// everything

View File

@@ -83,12 +83,39 @@ trait PeriodOverview
private Collection $statistics; // temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
private array $transactions; // temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
/**
* This method returns "period entries", so nov-2015, dec-2015, etc. (this depends on the users session range)
* and for each period, the amount of money spent and earned. This is a complex operation which is cached for

View File

@@ -92,7 +92,7 @@ class AccountEnrichment implements EnrichmentInterface
*/
public function enrich(Collection $collection): Collection
{
Log::debug(sprintf('Now doing account enrichment for %d account(s)', $collection->count()));
// Log::debug(sprintf('Now doing account enrichment for %d account(s)', $collection->count()));
// prep local fields
$this->collection = $collection;

View File

@@ -43,9 +43,27 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
private Collection $collection; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private readonly bool $convertToPrimary; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $currencies = [];
private array $currencyIds = [];
private array $ids = [];

View File

@@ -43,6 +43,15 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private readonly bool $convertToPrimary; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $currencies = [];
private array $currencyIds = [];
private Carbon $end;

View File

@@ -63,7 +63,7 @@ class CategoryEnrichment implements EnrichmentInterface
public function enrichSingle(array|Model $model): array|Model
{
Log::debug(__METHOD__);
// Log::debug(__METHOD__);
$collection = new Collection()->push($model);
$collection = $this->enrich($collection);

View File

@@ -45,9 +45,27 @@ class PiggyBankEnrichment implements EnrichmentInterface
private array $accountIds = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $accounts = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $amounts = [];
private Collection $collection;
private array $currencies = [];

View File

@@ -40,9 +40,27 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
private array $accountCurrencies = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $accountIds = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private Collection $collection;
private array $currencies = [];
private array $groupIds = [];

View File

@@ -49,6 +49,15 @@ class SubscriptionEnrichment implements EnrichmentInterface
private Collection $collection; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private readonly bool $convertToPrimary;
private ?Carbon $end = null;
private array $mappedObjects = [];

View File

@@ -59,6 +59,24 @@ class TransactionGroupEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
public function __construct()
{
$this->dateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
@@ -68,7 +86,7 @@ class TransactionGroupEnrichment implements EnrichmentInterface
#[Override]
public function enrich(Collection $collection): Collection
{
Log::debug(sprintf('Now doing account enrichment for %d transaction group(s)', $collection->count()));
// Log::debug(sprintf('Now doing account enrichment for %d transaction group(s)', $collection->count()));
// prep local fields
$this->collection = $collection;
$this->collectJournalIds();

View File

@@ -45,9 +45,27 @@ class WebhookEnrichment implements EnrichmentInterface
private array $deliveries = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $ids = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $responses = [];
private array $triggers = [];
private array $webhookDeliveries = [];

View File

@@ -78,7 +78,7 @@ class AccountBalanceCalculator
]);
if (null === $first) {
Log::debug(sprintf('Found no transactions for currency #%d and account #%d, return 0.', $currencyId, $accountId));
// Log::debug(sprintf('Found no transactions for currency #%d and account #%d, return 0.', $currencyId, $accountId));
return '0';
}

View File

@@ -75,6 +75,17 @@ class Preferences
return true;
}
public function deleteForUser(User $user, string $name): bool
{
$fullName = sprintf('preference%s%s', auth()->user()->id, $name);
if (Cache::has($fullName)) {
Cache::forget($fullName);
}
Preference::where('user_id', $user->id)->where('name', $name)->delete();
return true;
}
/**
* Find by name, has no user ID in it, because the method is called from an unauthenticated route any way.
*/

View File

@@ -45,7 +45,7 @@ class TransactionSummarizer
public function groupByCurrencyId(array $journals, string $method = 'negative', bool $includeForeign = true): array
{
Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s", %s)', count($journals), $method, var_export($includeForeign, true)));
// Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s", %s)', count($journals), $method, var_export($includeForeign, true)));
$array = [];
foreach ($journals as $journal) {
$field = 'amount';
@@ -146,7 +146,7 @@ class TransactionSummarizer
// $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], \FireflyIII\Support\Facades\Steam::{$method}($amount));
// Log::debug(sprintf('Journal #%d adds amount %s %s', $journal['transaction_journal_id'], $currencyCode, $amount));
}
Log::debug('End of sumExpenses.', $array);
// Log::debug('End of sumExpenses.', $array);
return $array;
}
@@ -234,7 +234,7 @@ class TransactionSummarizer
public function setConvertToPrimary(bool $convertToPrimary): void
{
Log::debug(sprintf('Overrule convertToPrimary to become %s', var_export($convertToPrimary, true)));
// Log::debug(sprintf('Overrule convertToPrimary to become %s', var_export($convertToPrimary, true)));
$this->convertToPrimary = $convertToPrimary;
}

View File

@@ -39,14 +39,14 @@ trait ChecksLogin
*/
public function authorize(): bool
{
Log::debug(sprintf('Now in %s', __METHOD__));
// Log::debug(sprintf('Now in %s', __METHOD__));
// Only allow logged-in users
$check = auth()->check();
if (!$check) {
return false;
}
if (!property_exists($this, 'acceptedRoles')) { // @phpstan-ignore-line
Log::debug('Request class has no acceptedRoles array');
Log::debug(sprintf('Request class %s has no acceptedRoles array', static::class));
return true; // check for false already took place.
}
@@ -81,15 +81,15 @@ trait ChecksLogin
{
/** @var User $user */
$user = auth()->user();
Log::debug('Now in getUserGroup()');
// Log::debug('Now in getUserGroup()');
/** @var null|UserGroup $userGroup */
$userGroup = $this->route()?->parameter('userGroup');
if (null === $userGroup) {
Log::debug('Request class has no userGroup parameter, but perhaps there is a parameter.');
// Log::debug('Request class has no userGroup parameter, but perhaps there is a parameter.');
$userGroupId = (int) $this->get('user_group_id');
if (0 === $userGroupId) {
Log::debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id));
// Log::debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id));
$userGroupId = (int) $user->user_group_id;
}
$userGroup = UserGroup::find($userGroupId);
@@ -98,7 +98,8 @@ trait ChecksLogin
return null;
}
Log::debug('Request class has valid user_group_id.');
// Log::debug('Request class has valid user_group_id.');
}
return $userGroup;

View File

@@ -122,11 +122,11 @@ class OperatorQuerySearch implements SearchInterface
if (str_starts_with($original, '-')) {
$return = sprintf('-%s', $config['alias_for']);
}
Log::debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return));
// Log::debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return));
return $return;
}
Log::debug(sprintf('"%s" is not an alias.', $operator));
// Log::debug(sprintf('"%s" is not an alias.', $operator));
return $original;
}
@@ -174,7 +174,7 @@ class OperatorQuerySearch implements SearchInterface
*/
public function parseQuery(string $query): void
{
Log::debug(sprintf('Now in parseQuery("%s")', $query));
// Log::debug(sprintf('Now in parseQuery("%s")', $query));
/** @var QueryParserInterface $parser */
$parser = app(QueryParserInterface::class);

View File

@@ -39,7 +39,7 @@ class QueryParser implements QueryParserInterface
public function parse(string $query): NodeGroup
{
Log::debug(sprintf('Parsing query in QueryParser: "%s"', $query));
// Log::debug(sprintf('Parsing query in QueryParser: "%s"', $query));
$this->query = $query;
$this->position = 0;
@@ -57,12 +57,13 @@ class QueryParser implements QueryParserInterface
while ($this->position < $count) {
$char = $chrArray[$this->position];
$nextChar = $chrArray[$this->position + 1] ?? '';
$prevChar = $chrArray[$this->position - 1] ?? '';
// Log::debug(sprintf('Char #%d: %s', $this->position, $char));
// If we're in a quoted string, we treat all characters except another quote as ordinary characters
if ($inQuotes) {
if ('\\' === $char && '"' === $nextChar) {
// Log::debug('BACKSLASH!');
if ('\\' === $char && '"' === $nextChar && '\\' !== $prevChar) {
// Log::debug('Found a backslash and the next one is a double quote.');
// escaped quote, pretend it's a normal char and continue two places (skipping the actual character).
$tokenUnderConstruction .= '\\'.$nextChar;
$this->position += 2;
@@ -77,7 +78,7 @@ class QueryParser implements QueryParserInterface
}
// char is "
++$this->position;
Log::debug(sprintf('Constructed token: %s', $tokenUnderConstruction));
// Log::debug(sprintf('Constructed token: %s', $tokenUnderConstruction));
return new NodeResult($this->createNode($tokenUnderConstruction, $fieldName, $prohibited), false);
}
@@ -199,11 +200,11 @@ class QueryParser implements QueryParserInterface
$token = rtrim($token, '"');
}
$token = str_replace('\"', '"', $token);
Log::debug(sprintf('Create FieldNode %s:%s (%s)', $fieldName, $token, var_export($prohibited, true)));
// Log::debug(sprintf('Create FieldNode %s:%s (%s)', $fieldName, $token, var_export($prohibited, true)));
return new FieldNode(trim($fieldName), trim($token), $prohibited);
}
Log::debug(sprintf('Create StringNode "%s" (%s)', $token, var_export($prohibited, true)));
// F Now in handleSearchNodeLog::debug(sprintf('Create StringNode "%s" (%s)', $token, var_export($prohibited, true)));
return new StringNode(trim($token), $prohibited);
}

View File

@@ -60,7 +60,7 @@ class SearchRuleEngine implements RuleEngineInterface
public function addOperator(array $operator): void
{
Log::debug('Add extra operator: ', $operator);
// Log::debug('Add extra operator: ', $operator);
$this->operators[] = $operator;
}

View File

@@ -3,6 +3,52 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## v6.4.18
### Fixed
- [Discussion 11671](https://github.com/orgs/firefly-iii/discussions/11671) (Subscriptions Next Expected Match) started by @idgaron
- [Issue 11667](https://github.com/firefly-iii/firefly-iii/issues/11667) (Account names and numbers are not corrected in rules when the account is updated) reported by @Kage1
- [Issue 11668](https://github.com/firefly-iii/firefly-iii/issues/11668) (Auto-budget cron crashes on develop: Call to a member function budgets() on null (BudgetLimitRepository.php:311)) reported by @sykmer
## v6.4.17 - 2026-02-06
### Added
- Batch processing. Firefly III now has a setting (under `/settings`) that allows you to send `"batch_processing": true` with new transactions over the API. If this setting is enabled and the value is `true`, Firefly III will not fire rules, webhooks or other events untill you either send `false` with a transaction OR use the [API end point](https://api-docs.firefly-iii.org/) `/v1/api/batch/finish`. This should speed up (large) data imports. It's a little experimental, use at your own risk.
- [Issue 11614](https://github.com/firefly-iii/firefly-iii/issues/11614) (Add New Taiwan Dollar to Currency Seeder) reported by @nick322
- [Issue 11246](https://github.com/firefly-iii/firefly-iii/issues/11246) (Distinguish automatically converted amount from foreign amount) reported by @jfpedroza
### Changed
- A lot of code in Firefly III is code responding to changes made by other code. These lines of code are called events, listeners, observers, handlers, etc. They were a bit of a mess and I cleaned them all up. This should greatly improve the reliability of debt amounts and running balance consistency. Bugs are always possible, let me know.
### Deprecated
- PHP 8.4 is still on my list to be disabled, beware.
### Fixed
- [Issue 11399](https://github.com/firefly-iii/firefly-iii/issues/11399) (Unusual behavior in audit logs (multi-currency)) reported by @jgmm81
- [Discussion 11431](https://github.com/orgs/firefly-iii/discussions/11431) (Settings don't get saved) started by @PVTejas
- [Issue 11541](https://github.com/firefly-iii/firefly-iii/issues/11541) (Display running balance fails for transactions between accounts with different currencies) reported by @SledgehammerPL
- [Issue 11544](https://github.com/firefly-iii/firefly-iii/issues/11544) (Clean up events and handlers) reported by @JC5
- [Issue 11546](https://github.com/firefly-iii/firefly-iii/issues/11546) (Wrong invitation expiry time) reported by @GunoH
- [Issue 11563](https://github.com/firefly-iii/firefly-iii/issues/11563) (Tag Report/Insight API Endpoint for Tags Non Functional) reported by @Unsantae
- [PR 11569](https://github.com/firefly-iii/firefly-iii/pull/11569) (Fix layout overflow issues with long content in v1 and v2 layouts) reported by @gian21391
- [PR 11589](https://github.com/firefly-iii/firefly-iii/pull/11589) (apply user-selected light/dark mode to form elements (checkboxes, date picker) [Issue 8613](https://github.com/firefly-iii/firefly-iii/issues/8613) (Some minor color issues) reported by @rumpff [Issue 7620](https://github.com/firefly-iii/firefly-iii/issues/7620) (Issues with light mode) reported by @rchl) reported by @mateuszkulapl
- [Issue 11597](https://github.com/firefly-iii/firefly-iii/issues/11597) (Changing category doesn't recompute stats) reported by @jlauwers
- [Issue 11601](https://github.com/firefly-iii/firefly-iii/issues/11601) (Only ungrouped piggy banks are listed when creating a transaction) reported by @jgmm81
- [Issue 11620](https://github.com/firefly-iii/firefly-iii/issues/11620) (Add database indexes to improve reporting query performance) reported by @Zakmaf
- [PR 11632](https://github.com/firefly-iii/firefly-iii/pull/11632) (fix v2 layout dashboard transactions load) reported by @mateuszkulapl
- [Issue 11657](https://github.com/firefly-iii/firefly-iii/issues/11657) (since v6.4.16: Backslash in category names cannot be matched in rules) reported by @37-b-j
- [Issue 11660](https://github.com/firefly-iii/firefly-iii/issues/11660) (Display of negative values of transfers) reported by @Robubble
- Confirming your new email address would result in an error.
### API
- [API end point](https://api-docs.firefly-iii.org/) `/v1/api/batch/finish`.
## v6.4.16 - 2026-01-18
> [!WARNING]

View File

@@ -99,7 +99,7 @@
"league/commonmark": "^2",
"league/csv": "^9.10",
"league/fractal": "0.*",
"mailersend/laravel-driver": "^2.12",
"mailersend/laravel-driver": "^3.0",
"nunomaduro/collision": "^8",
"pragmarx/google2fa": "^8.0",
"predis/predis": "^3",

116
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "72404106289a876b0046dceacbaccf24",
"content-hash": "c4e7dd2df7bae96ea6e4df82530411b9",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -130,16 +130,16 @@
},
{
"name": "brick/math",
"version": "0.14.6",
"version": "0.14.7",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "32498d5e1897e7642c0b961ace2df6d7dc9a3bc3"
"reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/32498d5e1897e7642c0b961ace2df6d7dc9a3bc3",
"reference": "32498d5e1897e7642c0b961ace2df6d7dc9a3bc3",
"url": "https://api.github.com/repos/brick/math/zipball/07ff363b16ef8aca9692bba3be9e73fe63f34e50",
"reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50",
"shasum": ""
},
"require": {
@@ -178,7 +178,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.14.6"
"source": "https://github.com/brick/math/tree/0.14.7"
},
"funding": [
{
@@ -186,7 +186,7 @@
"type": "github"
}
],
"time": "2026-02-05T07:59:58+00:00"
"time": "2026-02-07T10:57:35+00:00"
},
{
"name": "carbonphp/carbon-doctrine-types",
@@ -3486,29 +3486,29 @@
},
{
"name": "mailersend/laravel-driver",
"version": "v2.12.0",
"version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/mailersend/mailersend-laravel-driver.git",
"reference": "15e1ec41e29e65d3ca226929c65804190aaa93eb"
"reference": "cbda1bdcbd35e54f27a329df3c011389636eb783"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mailersend/mailersend-laravel-driver/zipball/15e1ec41e29e65d3ca226929c65804190aaa93eb",
"reference": "15e1ec41e29e65d3ca226929c65804190aaa93eb",
"url": "https://api.github.com/repos/mailersend/mailersend-laravel-driver/zipball/cbda1bdcbd35e54f27a329df3c011389636eb783",
"reference": "cbda1bdcbd35e54f27a329df3c011389636eb783",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/support": "^9.0 || ^10.0 || ^11.0 || ^12.0",
"mailersend/mailersend": "^0.35.0",
"illuminate/support": "^10.0 || ^11.0 || ^12.0",
"mailersend/mailersend": "^0.36.0",
"nyholm/psr7": "^1.5",
"php": ">=8.0",
"php": ">=8.2",
"php-http/guzzle7-adapter": "^1.0",
"symfony/mailer": "^6.0 || ^7.0"
},
"require-dev": {
"orchestra/testbench": "^7.0 || ^9.0 || ^10.0",
"orchestra/testbench": "^9.0 || ^10.0",
"phpunit/phpunit": "^9.0 || ^10.5 || ^12.0"
},
"type": "library",
@@ -3549,28 +3549,28 @@
],
"support": {
"issues": "https://github.com/mailersend/mailersend-laravel-driver/issues",
"source": "https://github.com/mailersend/mailersend-laravel-driver/tree/v2.12.0"
"source": "https://github.com/mailersend/mailersend-laravel-driver/tree/v3.0.0"
},
"time": "2025-10-28T14:59:16+00:00"
"time": "2026-02-05T12:32:37+00:00"
},
{
"name": "mailersend/mailersend",
"version": "v0.35.0",
"version": "v0.36.0",
"source": {
"type": "git",
"url": "https://github.com/mailersend/mailersend-php.git",
"reference": "f1696cf9e727e9503fbc5882d2a111bd966ad276"
"reference": "0a28852bba61f31c3668a2af6426dc0b2c866432"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mailersend/mailersend-php/zipball/f1696cf9e727e9503fbc5882d2a111bd966ad276",
"reference": "f1696cf9e727e9503fbc5882d2a111bd966ad276",
"url": "https://api.github.com/repos/mailersend/mailersend-php/zipball/0a28852bba61f31c3668a2af6426dc0b2c866432",
"reference": "0a28852bba61f31c3668a2af6426dc0b2c866432",
"shasum": ""
},
"require": {
"beberlei/assert": "^3.2",
"ext-json": "*",
"php": "^7.4 || ^8.0 <8.5",
"php": "^7.4 || ^8.0 <8.6",
"php-http/client-common": "^2.2",
"php-http/discovery": "^1.9",
"php-http/httplug": "^2.1",
@@ -3615,9 +3615,9 @@
],
"support": {
"issues": "https://github.com/mailersend/mailersend-php/issues",
"source": "https://github.com/mailersend/mailersend-php/tree/v0.35.0"
"source": "https://github.com/mailersend/mailersend-php/tree/v0.36.0"
},
"time": "2025-10-28T13:11:43+00:00"
"time": "2026-02-02T09:26:45+00:00"
},
{
"name": "monolog/monolog",
@@ -3829,16 +3829,16 @@
},
{
"name": "nette/schema",
"version": "v1.3.3",
"version": "v1.3.4",
"source": {
"type": "git",
"url": "https://github.com/nette/schema.git",
"reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004"
"reference": "086497a2f34b82fede9b5a41cc8e131d087cd8f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004",
"reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004",
"url": "https://api.github.com/repos/nette/schema/zipball/086497a2f34b82fede9b5a41cc8e131d087cd8f7",
"reference": "086497a2f34b82fede9b5a41cc8e131d087cd8f7",
"shasum": ""
},
"require": {
@@ -3846,8 +3846,8 @@
"php": "8.1 - 8.5"
},
"require-dev": {
"nette/tester": "^2.5.2",
"phpstan/phpstan-nette": "^2.0@stable",
"nette/tester": "^2.6",
"phpstan/phpstan": "^2.0@stable",
"tracy/tracy": "^2.8"
},
"type": "library",
@@ -3888,9 +3888,9 @@
],
"support": {
"issues": "https://github.com/nette/schema/issues",
"source": "https://github.com/nette/schema/tree/v1.3.3"
"source": "https://github.com/nette/schema/tree/v1.3.4"
},
"time": "2025-10-30T22:57:59+00:00"
"time": "2026-02-08T02:54:00+00:00"
},
{
"name": "nette/utils",
@@ -10530,16 +10530,16 @@
},
{
"name": "fruitcake/laravel-debugbar",
"version": "v4.0.6",
"version": "v4.0.7",
"source": {
"type": "git",
"url": "https://github.com/fruitcake/laravel-debugbar.git",
"reference": "0cbf2986de59f66870cee565491b81eb89f8d25e"
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/0cbf2986de59f66870cee565491b81eb89f8d25e",
"reference": "0cbf2986de59f66870cee565491b81eb89f8d25e",
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/a9cc62c81cd0bda4ca7410229487638d7df786be",
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be",
"shasum": ""
},
"require": {
@@ -10616,7 +10616,7 @@
],
"support": {
"issues": "https://github.com/fruitcake/laravel-debugbar/issues",
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.6"
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.7"
},
"funding": [
{
@@ -10628,7 +10628,7 @@
"type": "github"
}
],
"time": "2026-02-04T11:48:53+00:00"
"time": "2026-02-06T20:53:50+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@@ -11198,16 +11198,16 @@
},
{
"name": "php-debugbar/php-debugbar",
"version": "v3.3.0",
"version": "v3.3.1",
"source": {
"type": "git",
"url": "https://github.com/php-debugbar/php-debugbar.git",
"reference": "e22287890107602af6a113dc7975b3d77c542e5f"
"reference": "afdaa2e56aca9d56b5bb2bad041bd2f6002017cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/e22287890107602af6a113dc7975b3d77c542e5f",
"reference": "e22287890107602af6a113dc7975b3d77c542e5f",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/afdaa2e56aca9d56b5bb2bad041bd2f6002017cf",
"reference": "afdaa2e56aca9d56b5bb2bad041bd2f6002017cf",
"shasum": ""
},
"require": {
@@ -11284,7 +11284,7 @@
],
"support": {
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.3.0"
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.3.1"
},
"funding": [
{
@@ -11296,7 +11296,7 @@
"type": "github"
}
],
"time": "2026-01-28T12:57:47+00:00"
"time": "2026-02-06T21:09:38+00:00"
},
{
"name": "php-debugbar/symfony-bridge",
@@ -11908,16 +11908,16 @@
},
{
"name": "phpunit/phpunit",
"version": "12.5.9",
"version": "12.5.11",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "83d4c158526c879b4c5cf7149d27958b6d912373"
"reference": "9b518cb40f9474572c9f0178e96ff3dc1cf02bf1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/83d4c158526c879b4c5cf7149d27958b6d912373",
"reference": "83d4c158526c879b4c5cf7149d27958b6d912373",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9b518cb40f9474572c9f0178e96ff3dc1cf02bf1",
"reference": "9b518cb40f9474572c9f0178e96ff3dc1cf02bf1",
"shasum": ""
},
"require": {
@@ -11931,7 +11931,7 @@
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.3",
"phpunit/php-code-coverage": "^12.5.2",
"phpunit/php-code-coverage": "^12.5.3",
"phpunit/php-file-iterator": "^6.0.1",
"phpunit/php-invoker": "^6.0.0",
"phpunit/php-text-template": "^5.0.0",
@@ -11986,7 +11986,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.9"
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.11"
},
"funding": [
{
@@ -12010,25 +12010,25 @@
"type": "tidelift"
}
],
"time": "2026-02-05T08:01:09+00:00"
"time": "2026-02-10T12:32:02+00:00"
},
{
"name": "rector/rector",
"version": "2.3.5",
"version": "2.3.6",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "9442f4037de6a5347ae157fe8e6c7cda9d909070"
"reference": "ca9ebb81d280cd362ea39474dabd42679e32ca6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/9442f4037de6a5347ae157fe8e6c7cda9d909070",
"reference": "9442f4037de6a5347ae157fe8e6c7cda9d909070",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/ca9ebb81d280cd362ea39474dabd42679e32ca6b",
"reference": "ca9ebb81d280cd362ea39474dabd42679e32ca6b",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0",
"phpstan/phpstan": "^2.1.36"
"phpstan/phpstan": "^2.1.38"
},
"conflict": {
"rector/rector-doctrine": "*",
@@ -12062,7 +12062,7 @@
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.3.5"
"source": "https://github.com/rectorphp/rector/tree/2.3.6"
},
"funding": [
{
@@ -12070,7 +12070,7 @@
"type": "github"
}
],
"time": "2026-01-28T15:22:48+00:00"
"time": "2026-02-06T14:25:06+00:00"
},
{
"name": "sebastian/cli-parser",

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/2026-02-06',
'build_time' => 1770383155,
'version' => 'develop/2026-02-10',
'build_time' => 1770727063,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

View File

@@ -33,16 +33,12 @@ class ChangesFor3101 extends Migration
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.
*
* @SuppressWarnings("PHPMD.ShortMethodName")
*/
public function up(): void
{
}
public function up(): void {}
}

View File

@@ -39,9 +39,7 @@ class FixNullables extends Migration
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.

View File

@@ -34,16 +34,12 @@ class ChangesForV474 extends Migration
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.
*
* @SuppressWarnings("PHPMD.ShortMethodName")
*/
public function up(): void
{
}
public function up(): void {}
}

View File

@@ -37,9 +37,7 @@ class ChangesForV4711 extends Migration
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.

View File

@@ -37,9 +37,7 @@ class ChangesForV4712 extends Migration
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.

View File

@@ -35,9 +35,7 @@ class ExtendCurrencyInfo extends Migration
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.

View File

@@ -34,9 +34,7 @@ return new class() extends Migration {
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.
@@ -67,11 +65,19 @@ return new class() extends Migration {
foreach ($set as $table => $fields) {
foreach ($fields as $field) {
try {
Schema::table($table, static function (Blueprint $blueprint) use ($field): void {
$blueprint->index($field);
Schema::table($table, static function (Blueprint $blueprint) use ($table, $field): void {
if (!Schema::hasIndex($table, $field)) {
$blueprint->index($field);
}
});
} catch (QueryException $e) {
app('log')->error(sprintf(self::QUERY_ERROR, $table, $field, $e->getMessage()));
$message = $e->getMessage();
// ignore duplicate key name as error.
if (str_contains($message, ' Duplicate key name')) {
continue;
}
app('log')->error(sprintf(self::QUERY_ERROR, $table, $field, $message));
app('log')->error(self::EXPL);
}
}

View File

@@ -31,9 +31,7 @@ return new class() extends Migration {
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.

View File

@@ -51,9 +51,7 @@ return new class() extends Migration {
/**
* Reverse the migrations.
*/
public function down(): void
{
}
public function down(): void {}
/**
* Run the migrations.

View File

@@ -23,6 +23,7 @@
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\QueryException;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
@@ -97,10 +98,14 @@ return new class() extends Migration {
} catch (RuntimeException $e) {
Log::error('Could not drop foreign key "piggy_banks_account_id_foreign". Probably not an issue.');
}
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 2. make column nullable.
$table->unsignedInteger('account_id')->nullable()->change();
});
try {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 2. make column nullable.
$table->unsignedInteger('account_id')->nullable()->change();
});
} catch (QueryException $e) {
app('log')->error($e->getMessage());
}
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 3. add currency
if (!Schema::hasColumn('piggy_banks', 'transaction_currency_id')) {

View File

@@ -1,7 +1,9 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\QueryException;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
/**
@@ -18,15 +20,44 @@ return new class extends Migration {
public function up(): void
{
Schema::table('transactions', static function (Blueprint $blueprint): void {
$blueprint->index(['transaction_journal_id', 'amount'], 'idx_tx_journal_amount');
});
try {
Schema::table('transactions', static function (Blueprint $blueprint): void {
$blueprint->index(['transaction_journal_id', 'amount'], 'idx_tx_journal_amount');
});
} catch (QueryException $e) {
$message = $e->getMessage();
Schema::table('tag_transaction_journal', static function (Blueprint $blueprint): void {
$blueprint->index(['transaction_journal_id', 'tag_id'], 'idx_ttj_journal_tag');
});
Schema::table('transaction_journals', static function (Blueprint $blueprint): void {
$blueprint->index(['deleted_at'], 'idx_tj_deleted');
});
// ignore duplicate key name as error.
if (str_contains($message, ' Duplicate key name')) {
return;
}
Log::error(sprintf('Error when creating index: %s', $e->getMessage()));
}
try {
Schema::table('tag_transaction_journal', static function (Blueprint $blueprint): void {
$blueprint->index(['transaction_journal_id', 'tag_id'], 'idx_ttj_journal_tag');
});
} catch (QueryException $e) {
$message = $e->getMessage();
// ignore duplicate key name as error.
if (str_contains($message, ' Duplicate key name')) {
return;
}
Log::error(sprintf('Error when creating index: %s', $e->getMessage()));
}
try {
Schema::table('transaction_journals', static function (Blueprint $blueprint): void {
$blueprint->index(['deleted_at'], 'idx_tj_deleted');
});
} catch (QueryException $e) {
$message = $e->getMessage();
// ignore duplicate key name as error.
if (str_contains($message, ' Duplicate key name')) {
return;
}
Log::error(sprintf('Error when creating index: %s', $e->getMessage()));
}
}
};

70
package-lock.json generated
View File

@@ -3246,9 +3246,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.2.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz",
"integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==",
"version": "25.2.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.2.tgz",
"integrity": "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3364,42 +3364,42 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.27.tgz",
"integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==",
"version": "3.5.28",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.28.tgz",
"integrity": "sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/shared": "3.5.27",
"entities": "^7.0.0",
"@babel/parser": "^7.29.0",
"@vue/shared": "3.5.28",
"entities": "^7.0.1",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz",
"integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==",
"version": "3.5.28",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.28.tgz",
"integrity": "sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.27",
"@vue/shared": "3.5.27"
"@vue/compiler-core": "3.5.28",
"@vue/shared": "3.5.28"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz",
"integrity": "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==",
"version": "3.5.28",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.28.tgz",
"integrity": "sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/compiler-core": "3.5.27",
"@vue/compiler-dom": "3.5.27",
"@vue/compiler-ssr": "3.5.27",
"@vue/shared": "3.5.27",
"@babel/parser": "^7.29.0",
"@vue/compiler-core": "3.5.28",
"@vue/compiler-dom": "3.5.28",
"@vue/compiler-ssr": "3.5.28",
"@vue/shared": "3.5.28",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.21",
"postcss": "^8.5.6",
@@ -3407,14 +3407,14 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz",
"integrity": "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==",
"version": "3.5.28",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.28.tgz",
"integrity": "sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.27",
"@vue/shared": "3.5.27"
"@vue/compiler-dom": "3.5.28",
"@vue/shared": "3.5.28"
}
},
"node_modules/@vue/component-compiler-utils": {
@@ -3496,9 +3496,9 @@
"license": "MIT"
},
"node_modules/@vue/shared": {
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.27.tgz",
"integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==",
"version": "3.5.28",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.28.tgz",
"integrity": "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ==",
"dev": true,
"license": "MIT"
},
@@ -4033,14 +4033,14 @@
}
},
"node_modules/axios": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
"integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==",
"version": "1.13.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
}
},

View File

@@ -149,13 +149,13 @@ OK, that was a joke. But for real, when you feel Firefly III made your life bett
### Sponsorships
Firefly III is sponsored by LamdaTest. Their support allows me to test Firefly III more easily and introduce even fewer bugs with every release.
Firefly III is sponsored by TestMu AI. Their support allows me to test Firefly III more easily and introduce even fewer bugs with every release.
<p style="font-size:21px; color:black;">Browser testing via
<a href="https://www.lambdatest.com/?utm_source=fireflyiii&utm_medium=sponsor" target="_blank">
<img src="https://www.lambdatest.com/blue-logo.png" style="vertical-align: middle;" width="250" height="45" />
Browser testing via:
<a href="https://www.testmuai.com/?utm_source=fireflyiii&utm_medium=sponsor" target="_blank">
<img src=".github/assets/img/testmu.png" alt="Testmu" style="vertical-align: middle;" width="250" />
</a>
</p>
<!-- END OF SPONSOR TEXT -->

Some files were not shown because too many files have changed in this diff Show More