mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-03-30 08:54:59 +00:00
Compare commits
57 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3fc8673d3 | ||
|
|
db156ffcf2 | ||
|
|
59510a9acc | ||
|
|
7f9640087e | ||
|
|
29c51ad0e2 | ||
|
|
bf8c40d502 | ||
|
|
99912483de | ||
|
|
a3f4ab9b1b | ||
|
|
953fe7d9eb | ||
|
|
41a7890c7a | ||
|
|
b8e07c1df4 | ||
|
|
2a3d9001d1 | ||
|
|
bc7453e204 | ||
|
|
8c9ad9da83 | ||
|
|
c8bd8d5113 | ||
|
|
8ce5429e06 | ||
|
|
c1f8fb2f45 | ||
|
|
5fb4330c20 | ||
|
|
485eb224d2 | ||
|
|
de7033ee63 | ||
|
|
4c20547863 | ||
|
|
3881cd3e39 | ||
|
|
a3e7fa008d | ||
|
|
205b5bd3bf | ||
|
|
21c3dc3f56 | ||
|
|
aa4f5d5a2e | ||
|
|
9c1f79110c | ||
|
|
bd62957908 | ||
|
|
a7e4252b46 | ||
|
|
c18691d6b3 | ||
|
|
b4dbcdcfba | ||
|
|
b0a6acb334 | ||
|
|
a0c423b9ed | ||
|
|
2844929351 | ||
|
|
7bfba6a239 | ||
|
|
c0d4a70d46 | ||
|
|
a62f8bbaff | ||
|
|
b115b4ad92 | ||
|
|
dc60025097 | ||
|
|
d77769b2f4 | ||
|
|
c6497960f8 | ||
|
|
4eee0c79cd | ||
|
|
e333c3254b | ||
|
|
9d244883a1 | ||
|
|
a17131c2f7 | ||
|
|
898459198d | ||
|
|
6466bc9272 | ||
|
|
dd8a8dba85 | ||
|
|
ee16888317 | ||
|
|
56a2580fd7 | ||
|
|
2ab0225223 | ||
|
|
83662415c3 | ||
|
|
cf976b2ab1 | ||
|
|
bf79c9db72 | ||
|
|
29f4c09a7b | ||
|
|
22ef456dca | ||
|
|
974c84a877 |
9
.github/pull_request_template.md
vendored
9
.github/pull_request_template.md
vendored
@@ -6,6 +6,8 @@
|
||||
2. If your PR is more than 25 lines, talk to me FIRST.
|
||||
3. If you fix spelling or code comments, talk to me FIRST.
|
||||
|
||||
This is to prevent AI bots, low-effort PRs and spam. Sorry about that.
|
||||
|
||||
Wanna talk to me? Open a GitHub Issue, Discussion, or email me: james@firefly-iii.org
|
||||
|
||||
👀 Please ensure you have taken a look at the contribution guidelines:
|
||||
@@ -17,7 +19,9 @@ Remember that your PR may be CLOSED:
|
||||
2. If you open a PR on the main branch, your PR will be CLOSED.
|
||||
3. If you only fix a spelling error or code comment, your PR will be CLOSED.
|
||||
|
||||
Thanks again, and happy developing!
|
||||
Again, this is to prevent AI bots, low-effort PRs and spam. I apologize for the harsh tone.
|
||||
|
||||
But if you made it this far thanks again for contributing, and happy developing!
|
||||
|
||||
-->
|
||||
|
||||
@@ -48,3 +52,6 @@ I used AI assistance for:
|
||||
<!--
|
||||
Thanks for contributing!
|
||||
-->
|
||||
|
||||
@JC5
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2026
|
||||
- Joe Longendyke
|
||||
- Daniel Holøien
|
||||
- Matthew Grove
|
||||
- Cinnamon Pyro
|
||||
|
||||
@@ -116,6 +116,7 @@ final class PiggyBankController extends Controller
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'object_group_id' => null === $objectGroup ? null : (string) $objectGroup->id,
|
||||
'object_group_title' => $objectGroup?->title,
|
||||
'object_group_order' => $objectGroup?->order,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Transformers\CurrencyTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
/**
|
||||
@@ -154,7 +153,6 @@ final class UpdateController extends Controller
|
||||
public function update(UpdateRequest $request, TransactionCurrency $currency): JsonResponse
|
||||
{
|
||||
$data = $request->getAll();
|
||||
Log::debug(__METHOD__, $data);
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
@@ -28,6 +28,7 @@ use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Rules\IsValidPositiveAmount;
|
||||
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
|
||||
use FireflyIII\Rules\LessThanPiggyTarget;
|
||||
use FireflyIII\Rules\PiggyBank\IsEnoughInAccounts;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
@@ -84,7 +85,7 @@ class UpdateRequest extends FormRequest
|
||||
'accounts' => 'array',
|
||||
'accounts.*' => 'array',
|
||||
'accounts.*.account_id' => ['required', 'numeric', 'belongsToUser:accounts,id'],
|
||||
'accounts.*.current_amount' => ['numeric', 'nullable', new IsValidZeroOrMoreAmount(true)],
|
||||
'accounts.*.current_amount' => ['numeric', 'nullable', new IsValidZeroOrMoreAmount(true), new IsEnoughInAccounts($piggyBank, $this->getAll())],
|
||||
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
|
||||
'object_group_title' => ['min:1', 'max:255'],
|
||||
'transaction_currency_id' => 'exists:transaction_currencies,id|nullable',
|
||||
|
||||
@@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
@@ -45,15 +46,23 @@ class UpdateRequest extends FormRequest
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
// return nothing that isn't explicitly in the array:
|
||||
$fields = [
|
||||
'name' => ['name', 'convertString'],
|
||||
'code' => ['code', 'convertString'],
|
||||
'symbol' => ['symbol', 'convertString'],
|
||||
'decimal_places' => ['decimal_places', 'convertInteger'],
|
||||
'default' => ['default', 'boolean'],
|
||||
'enabled' => ['enabled', 'boolean'],
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$isAdmin = $user->hasRole('owner');
|
||||
|
||||
$fields = [
|
||||
'enabled' => ['enabled', 'boolean'],
|
||||
];
|
||||
if ($isAdmin) {
|
||||
$fields = [
|
||||
'name' => ['name', 'convertString'],
|
||||
'code' => ['code', 'convertString'],
|
||||
'symbol' => ['symbol', 'convertString'],
|
||||
'decimal_places' => ['decimal_places', 'convertInteger'],
|
||||
'default' => ['default', 'boolean'],
|
||||
'enabled' => ['enabled', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
return $this->getAllData($fields);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CorrectsGroupAccounts extends Command
|
||||
{
|
||||
@@ -46,6 +47,7 @@ class CorrectsGroupAccounts extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
Log::debug('Start of correction:group-accounts');
|
||||
$groups = [];
|
||||
$res = TransactionJournal::groupBy('transaction_group_id')->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
|
||||
|
||||
@@ -59,13 +61,16 @@ class CorrectsGroupAccounts extends Command
|
||||
$flags->applyRules = false;
|
||||
$flags->fireWebhooks = false;
|
||||
$flags->recalculateCredit = true;
|
||||
$flags->unifyOnly = true;
|
||||
$objects = new TransactionGroupEventObjects();
|
||||
foreach ($groups as $groupId) {
|
||||
$group = TransactionGroup::find($groupId);
|
||||
$objects->appendFromTransactionGroup($group);
|
||||
}
|
||||
Log::debug(sprintf('Fire event for %d transaction group(s)', count($groups)));
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
Log::debug('End of correction:group-accounts');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,4 +30,5 @@ class TransactionGroupEventFlags
|
||||
public bool $fireWebhooks = true;
|
||||
public bool $batchSubmission = false;
|
||||
public bool $recalculateCredit = true;
|
||||
public bool $unifyOnly = false;
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ class Handler extends ExceptionHandler
|
||||
'method' => request()->method(),
|
||||
'headers' => $headers,
|
||||
// @mago-expect lint:no-request-all
|
||||
'post' => 'POST' === request()->method() ? json_encode(request()->all()) : '',
|
||||
'post' => 'PUT' === request()->method() || 'POST' === request()->method() ? json_encode(request()->all()) : '',
|
||||
];
|
||||
|
||||
// create job that will mail.
|
||||
|
||||
@@ -134,6 +134,13 @@ class PiggyBankFactory
|
||||
$previous = $toBeLinked[$account->id]['current_amount'] ?? '0';
|
||||
$diff = bcsub($info['current_amount'], $previous);
|
||||
|
||||
// if money is added, check if we can!
|
||||
if (1 === bccomp($diff, '0') && !$this->piggyBankRepository->canAddAmount($piggyBank, $account, $diff)) {
|
||||
Log::debug(sprintf('Cannot add amount %s to piggy bank #%d ("%s")', $diff, $piggyBank->id, $piggyBank->name));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// create event for difference.
|
||||
if (0 !== bccomp($diff, '0')) {
|
||||
// 2025-10-01 for issue #10990 disable this event.
|
||||
|
||||
@@ -132,7 +132,7 @@ final class NotificationController extends Controller
|
||||
return redirect(route('settings.notification.index'));
|
||||
}
|
||||
|
||||
$all = $request->only(['channel']);
|
||||
$all = $request->only(['test_submit']);
|
||||
$channel = $all['test_submit'] ?? '';
|
||||
|
||||
switch ($channel) {
|
||||
|
||||
@@ -246,14 +246,14 @@ final class PreferencesController extends Controller
|
||||
$all = $request->only($keys);
|
||||
foreach (config('notifications.notifications.user') as $key => $info) {
|
||||
$key = sprintf('notification_%s', $key);
|
||||
if (array_key_exists($key, $all)) {
|
||||
if (array_key_exists($key, $all) && false === auth()->user()->hasRole('demo')) {
|
||||
Log::debug(sprintf('update notification to true: %s', $key));
|
||||
Preferences::set($key, true);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists($key, $all)) {
|
||||
Log::debug(sprintf('update notification to false: %s', $key));
|
||||
Preferences::set($key, false);
|
||||
}
|
||||
Log::debug(sprintf('update notification to false: %s', $key));
|
||||
Preferences::set($key, false);
|
||||
}
|
||||
unset($all);
|
||||
|
||||
@@ -369,6 +369,12 @@ final class PreferencesController extends Controller
|
||||
$all = $request->only(['channel']);
|
||||
$channel = $all['channel'] ?? '';
|
||||
|
||||
if (true === auth()->user()->hasRole('demo')) {
|
||||
session()->flash('error', (string) trans('firefly.not_available_demo_user'));
|
||||
|
||||
return redirect(route('preferences.index'));
|
||||
}
|
||||
|
||||
switch ($channel) {
|
||||
default:
|
||||
session()->flash('error', (string) trans('firefly.notification_test_failed', ['channel' => $channel]));
|
||||
|
||||
@@ -170,7 +170,7 @@ final class MassController extends Controller
|
||||
*/
|
||||
public function update(MassEditJournalRequest $request): RedirectResponse
|
||||
{
|
||||
$journalIds = $request->get('journals');
|
||||
$journalIds = $request->input('journals');
|
||||
if (!is_array($journalIds)) {
|
||||
// TODO this is a weird error, should be caught.
|
||||
throw new FireflyException('This is not an array.');
|
||||
@@ -250,6 +250,8 @@ final class MassController extends Controller
|
||||
private function updateJournal(int $journalId, MassEditJournalRequest $request): void
|
||||
{
|
||||
$journal = $this->repository->find($journalId);
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($journal->transactionGroup);
|
||||
|
||||
if (!$journal instanceof TransactionJournal) {
|
||||
throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId));
|
||||
}
|
||||
@@ -274,8 +276,9 @@ final class MassController extends Controller
|
||||
// call service to update.
|
||||
$service->setData($data);
|
||||
$service->update();
|
||||
$updated = $service->getTransactionJournal();
|
||||
$objects->appendFromTransactionGroup($updated->transactionGroup);
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($journal->transactionGroup);
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ use FireflyIII\User;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class IsAdmin.
|
||||
@@ -41,6 +42,8 @@ class IsAdminApi
|
||||
* @param null|string $guard
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, $guard = null)
|
||||
{
|
||||
@@ -58,6 +61,8 @@ class IsAdminApi
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
if (!$repository->hasRole($user, 'owner')) {
|
||||
Log::error(sprintf('Cannot access %s?%s.', $request->url(), $request->getQueryString()));
|
||||
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class ProcessesUpdatedTransactionGroup
|
||||
public function handle(UpdatedSingleTransactionGroup $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now handling event %s', get_class($event)));
|
||||
$this->unifyAccounts($event);
|
||||
$effect = $this->unifyAccounts($event);
|
||||
|
||||
Log::debug(sprintf('Transaction journal count is %d', $event->objects->transactionJournals->count()));
|
||||
if (!$event->flags->applyRules) {
|
||||
@@ -63,7 +63,13 @@ class ProcessesUpdatedTransactionGroup
|
||||
$this->createWebhookMessages($event->objects->transactionGroups, WebhookTrigger::UPDATE_TRANSACTION);
|
||||
}
|
||||
$this->removePeriodStatistics($event->objects);
|
||||
$this->recalculateRunningBalance($event->objects);
|
||||
if (0 === $effect && true === $event->flags->unifyOnly) {
|
||||
Log::debug('Effect = 0, will not recalculate running balance.');
|
||||
}
|
||||
if (0 !== $effect || false === $event->flags->unifyOnly) {
|
||||
Log::debug(sprintf('Effect is != 0 (%d) OR unifyOnly = false, will recalc running balance', $effect));
|
||||
$this->recalculateRunningBalance($event->objects);
|
||||
}
|
||||
|
||||
Log::debug('Done with handle() for UpdatedSingleTransactionGroup');
|
||||
}
|
||||
@@ -71,23 +77,26 @@ class ProcessesUpdatedTransactionGroup
|
||||
/**
|
||||
* This method will make sure all source / destination accounts are the same.
|
||||
*/
|
||||
protected function unifyAccounts(UpdatedSingleTransactionGroup $updatedGroupEvent): void
|
||||
protected function unifyAccounts(UpdatedSingleTransactionGroup $updatedGroupEvent): int
|
||||
{
|
||||
Log::debug('Now in unifyAccounts()');
|
||||
$effect = 0;
|
||||
|
||||
/** @var TransactionGroup $group */
|
||||
foreach ($updatedGroupEvent->objects->transactionGroups as $group) {
|
||||
$this->unifyAccountsForGroup($group);
|
||||
$effect += $this->unifyAccountsForGroup($group);
|
||||
}
|
||||
Log::debug('Done with unifyAccounts()');
|
||||
Log::debug(sprintf('Done with unifyAccounts(%d)', $effect));
|
||||
|
||||
return $effect;
|
||||
}
|
||||
|
||||
private function unifyAccountsForGroup(TransactionGroup $group): void
|
||||
private function unifyAccountsForGroup(TransactionGroup $group): int
|
||||
{
|
||||
if (1 === $group->transactionJournals->count()) {
|
||||
Log::debug('Nothing to do in unifyAccounts()');
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// first journal:
|
||||
@@ -104,7 +113,7 @@ class ProcessesUpdatedTransactionGroup
|
||||
if (null === $first) {
|
||||
Log::warning(sprintf('Group #%d has no transaction journals.', $group->id));
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$all = $group->transactionJournals()->get()->pluck('id')->toArray();
|
||||
@@ -116,13 +125,30 @@ class ProcessesUpdatedTransactionGroup
|
||||
$destAccount = $first->transactions()->where('amount', '>', '0')->first()->account;
|
||||
|
||||
$type = $first->transactionType->type;
|
||||
$effect = 0;
|
||||
if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::WITHDRAWAL->value === $type) {
|
||||
// set all source transactions to source account:
|
||||
Transaction::whereIn('transaction_journal_id', $all)->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]);
|
||||
$effect += Transaction::whereIn('transaction_journal_id', $all)
|
||||
->where('account_id', '!=', $sourceAccount->id)
|
||||
->where('amount', '<', 0)
|
||||
->update(['account_id' => $sourceAccount->id])
|
||||
;
|
||||
}
|
||||
if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::DEPOSIT->value === $type) {
|
||||
// set all destination transactions to destination account:
|
||||
Transaction::whereIn('transaction_journal_id', $all)->where('amount', '>', 0)->update(['account_id' => $destAccount->id]);
|
||||
$effect += Transaction::whereIn('transaction_journal_id', $all)
|
||||
->where('account_id', '!=', $destAccount->id)
|
||||
->where('amount', '>', 0)
|
||||
->update(['account_id' => $destAccount->id])
|
||||
;
|
||||
}
|
||||
if (0 === $effect) {
|
||||
Log::debug(sprintf('Had nothing to do in unifyAccounts(#%d)', $group->id));
|
||||
|
||||
return 0;
|
||||
}
|
||||
Log::debug(sprintf('Updated %d transaction(s) in unifyAccounts(#%d)', $effect, $group->id));
|
||||
|
||||
return $effect;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,13 @@ trait SupportsGroupProcessingTrait
|
||||
return;
|
||||
}
|
||||
|
||||
$array = $set->pluck('id')->toArray();
|
||||
$array = array_unique($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('Fire rule engine for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
@@ -56,6 +56,7 @@ trait SupportsGroupProcessingTrait
|
||||
$newRuleEngine->setUser($user);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
foreach ($array as $journalId) {
|
||||
Log::debug(sprintf('Fire rule engine for journal #%d', $journalId));
|
||||
$newRuleEngine->removeOperator('journal_id');
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalId]);
|
||||
$newRuleEngine->fire();
|
||||
|
||||
75
app/Rules/PiggyBank/IsEnoughInAccounts.php
Normal file
75
app/Rules/PiggyBank/IsEnoughInAccounts.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* IsEnoughInAccounts.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\Rules\PiggyBank;
|
||||
|
||||
use Closure;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Override;
|
||||
|
||||
class IsEnoughInAccounts implements ValidationRule
|
||||
{
|
||||
public function __construct(
|
||||
private readonly PiggyBank $piggyBank,
|
||||
private readonly array $data
|
||||
) {}
|
||||
|
||||
#[Override]
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
// TODO: Implement validate() method.
|
||||
if (!array_key_exists('accounts', $this->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
||||
/** @var PiggyBankRepositoryInterface $piggyRepos */
|
||||
$piggyRepos = app(PiggyBankRepositoryInterface::class);
|
||||
|
||||
$accounts = $this->data['accounts'];
|
||||
foreach ($accounts as $info) {
|
||||
$account = $repository->find((int) $info['account_id']);
|
||||
$amount = $info['current_amount'] ?? '0';
|
||||
if (null === $account) {
|
||||
$fail('validation.no_asset_account')->translate();
|
||||
|
||||
return;
|
||||
}
|
||||
if ('' === $amount || 0 === bccomp($amount, '0')) {
|
||||
$fail('validation.more_than_zero_correct')->translate();
|
||||
|
||||
return;
|
||||
}
|
||||
$diff = bcsub($amount, $piggyRepos->getCurrentAmount($this->piggyBank, $account));
|
||||
if (1 === bccomp($diff, '0') && !$piggyRepos->canAddAmount($this->piggyBank, $account, $amount)) {
|
||||
$fail('validation.cannot_add_piggy_amount')->translate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,7 @@ class PrimaryAmountRecalculationService
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($set as $account) {
|
||||
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()->data;
|
||||
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()?->data;
|
||||
if ($groupCurrency->id === $currencyId) {
|
||||
Log::debug(sprintf('Account "%s" is in group currency %s. Skip.', $account->name, $groupCurrency->code));
|
||||
|
||||
@@ -225,7 +225,7 @@ class PrimaryAmountRecalculationService
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($set as $account) {
|
||||
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()->data;
|
||||
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()?->data;
|
||||
if ($groupCurrency->id === $currencyId) {
|
||||
Log::debug(sprintf('Account "%s" is in group currency %s. Skip.', $account->name, $groupCurrency->code));
|
||||
|
||||
|
||||
@@ -111,6 +111,11 @@ class JournalUpdateService
|
||||
$this->transactionGroupRepository = app(TransactionGroupRepositoryInterface::class);
|
||||
}
|
||||
|
||||
public function getTransactionJournal(): ?TransactionJournal
|
||||
{
|
||||
return $this->transactionJournal;
|
||||
}
|
||||
|
||||
public function isCompareHashChanged(): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
@@ -774,8 +779,8 @@ class JournalUpdateService
|
||||
$this->transactionJournal,
|
||||
'update_foreign_amount',
|
||||
[
|
||||
'currency_symbol' => $oldForeignCurrency->symbol,
|
||||
'decimal_places' => $oldForeignCurrency->decimal_places,
|
||||
'currency_symbol' => $oldForeignCurrency?->symbol,
|
||||
'decimal_places' => $oldForeignCurrency?->decimal_places,
|
||||
'amount' => $originalSourceAmount,
|
||||
],
|
||||
[
|
||||
|
||||
@@ -70,7 +70,7 @@ class Date implements BinderInterface
|
||||
try {
|
||||
$result = new Carbon($value);
|
||||
} catch (InvalidDateException|InvalidFormatException $e) {
|
||||
$message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage());
|
||||
$message = sprintf('Could not parse date "%s" for user #%d: %s', $value, (int) auth()->user()?->id, $e->getMessage());
|
||||
Log::error($message);
|
||||
|
||||
throw new NotFoundHttpException('Could not parse value', $e);
|
||||
|
||||
@@ -32,6 +32,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -60,6 +61,7 @@ trait ChartGeneration
|
||||
}
|
||||
Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
|
||||
$locale = Steam::getLocale();
|
||||
$converter = new ExchangeRateConverter();
|
||||
|
||||
/** @var GeneratorInterface $generator */
|
||||
$generator = app(GeneratorInterface::class);
|
||||
@@ -76,10 +78,6 @@ trait ChartGeneration
|
||||
foreach ($accounts as $account) {
|
||||
Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name));
|
||||
$currency = $accountRepos->getAccountCurrency($account) ?? $primary;
|
||||
$usePrimary = $convertToPrimary && $primary->id !== $currency->id;
|
||||
$field = $convertToPrimary ? 'pc_balance' : 'balance';
|
||||
$currency = $usePrimary ? $primary : $currency;
|
||||
Log::debug(sprintf('Will use field %s', $field));
|
||||
$currentSet = ['label' => $account->name, 'currency_symbol' => $currency->symbol, 'entries' => []];
|
||||
|
||||
$currentStart = clone $start;
|
||||
@@ -90,9 +88,16 @@ trait ChartGeneration
|
||||
$format = $currentStart->format('Y-m-d');
|
||||
$label = trim($currentStart->isoFormat((string) trans('config.month_and_day_js', [], $locale)));
|
||||
$balance = $range[$format] ?? $previous;
|
||||
$converted = $balance['balance'] ?? '0';
|
||||
|
||||
// convert balance if necessary:
|
||||
if ($convertToPrimary) {
|
||||
$converted = $converter->convert($currency, $primary, $currentStart, $balance['balance']);
|
||||
}
|
||||
|
||||
$previous = $balance;
|
||||
$currentStart->addDay();
|
||||
$currentSet['entries'][$label] = $balance[$field] ?? '0';
|
||||
$currentSet['entries'][$label] = $converted;
|
||||
}
|
||||
$chartData[] = $currentSet;
|
||||
}
|
||||
|
||||
@@ -474,7 +474,10 @@ trait ConvertsDataTypes
|
||||
if (!array_key_exists('current_amount', $entry)) {
|
||||
$amount = null;
|
||||
}
|
||||
$return[] = ['account_id' => $this->integerFromValue((string) ($entry['account_id'] ?? '0')), 'current_amount' => $amount];
|
||||
$return[] = [
|
||||
'account_id' => $this->integerFromValue((string) ($entry['account_id'] ?? '0')),
|
||||
'current_amount' => $amount,
|
||||
];
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
||||
13
changelog.md
13
changelog.md
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## v6.5.9 - 2026-03-23
|
||||
|
||||
<!-- summary: Bug fixes mainly, but also updated dependencies and new wording in the instructions you see when you open a PR. -->
|
||||
|
||||
### Fixed
|
||||
- [Issue 12004](https://github.com/firefly-iii/firefly-iii/issues/12004) (Test notification buttons always generate an error) reported by @IDevJoe
|
||||
- [Issue 12014](https://github.com/firefly-iii/firefly-iii/issues/12014) (Converting a transaction to a transfer and setting the destination account to one with a different currency breaks the audit log) reported by @avee87
|
||||
|
||||
# Changed
|
||||
- [Issue 12000](https://github.com/firefly-iii/firefly-iii/issues/12000) (Improved transaction pagination for large data sets) reported by @christiaanderidder
|
||||
|
||||
## v6.5.8 - 2026-03-22
|
||||
|
||||
<!-- summary: This release fixes a regression bug in user registration. -->
|
||||
@@ -2680,7 +2691,7 @@ problems:
|
||||
|
||||
## x.x.x - 20xx-xx-xx
|
||||
|
||||
<!-- summary: If you can read this I forgot to update the summary! -->
|
||||
<!-- summary: This release fixes ... If you can read this I forgot to update the summary! -->
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"jc5/google2fa-laravel": "^2.0",
|
||||
"jc5/recovery": "^2",
|
||||
"laravel-notification-channels/pushover": "^4.0",
|
||||
"laravel-notification-channels/pushover": "^5.0",
|
||||
"laravel/framework": "^12",
|
||||
"laravel/passport": "^12.0",
|
||||
"laravel/slack-notification-channel": "^3.3",
|
||||
|
||||
131
composer.lock
generated
131
composer.lock
generated
@@ -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": "93912463b9c00da1cf4afbd38edd36a1",
|
||||
"content-hash": "7e221e2496a89469f121036f554178ac",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@@ -1009,16 +1009,16 @@
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"version": "v7.0.3",
|
||||
"version": "v7.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/firebase/php-jwt.git",
|
||||
"reference": "28aa0694bcfdfa5e2959c394d5a1ee7a5083629e"
|
||||
"reference": "e41f1bd7dbe3c5455c3f72d4338cfeb083b71931"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/28aa0694bcfdfa5e2959c394d5a1ee7a5083629e",
|
||||
"reference": "28aa0694bcfdfa5e2959c394d5a1ee7a5083629e",
|
||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/e41f1bd7dbe3c5455c3f72d4338cfeb083b71931",
|
||||
"reference": "e41f1bd7dbe3c5455c3f72d4338cfeb083b71931",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1026,6 +1026,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"phpfastcache/phpfastcache": "^9.2",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"psr/cache": "^2.0||^3.0",
|
||||
@@ -1066,9 +1067,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||
"source": "https://github.com/firebase/php-jwt/tree/v7.0.3"
|
||||
"source": "https://github.com/firebase/php-jwt/tree/v7.0.4"
|
||||
},
|
||||
"time": "2026-02-25T22:16:40+00:00"
|
||||
"time": "2026-03-27T21:17:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fruitcake/php-cors",
|
||||
@@ -1812,28 +1813,28 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel-notification-channels/pushover",
|
||||
"version": "4.1.2",
|
||||
"version": "5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel-notification-channels/pushover.git",
|
||||
"reference": "53be939273e79e832a417d5863c1d443f0b3c665"
|
||||
"reference": "a276d3cdbfede11bb5f4013c24ad1ef2a06bcb39"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel-notification-channels/pushover/zipball/53be939273e79e832a417d5863c1d443f0b3c665",
|
||||
"reference": "53be939273e79e832a417d5863c1d443f0b3c665",
|
||||
"url": "https://api.github.com/repos/laravel-notification-channels/pushover/zipball/a276d3cdbfede11bb5f4013c24ad1ef2a06bcb39",
|
||||
"reference": "a276d3cdbfede11bb5f4013c24ad1ef2a06bcb39",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"illuminate/notifications": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
|
||||
"illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
|
||||
"php": "^8.1"
|
||||
"illuminate/notifications": "^11.0 || ^12.0 || ^13.0",
|
||||
"illuminate/support": "^11.0 || ^12.0 || ^13.0",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"orchestra/testbench": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0",
|
||||
"phpunit/phpunit": "^9.3 || ^10.5 || ^11.5.3"
|
||||
"orchestra/testbench": "^9.0 || ^10.0 || ^11.0",
|
||||
"phpunit/phpunit": "^11.5.3 || ^12.5.12"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-exif": "Required for image attachment support"
|
||||
@@ -1873,22 +1874,22 @@
|
||||
"homepage": "https://github.com/laravel-notification-channels/pushover",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel-notification-channels/pushover/issues",
|
||||
"source": "https://github.com/laravel-notification-channels/pushover/tree/4.1.2"
|
||||
"source": "https://github.com/laravel-notification-channels/pushover/tree/5.0.0"
|
||||
},
|
||||
"time": "2025-09-09T09:14:17+00:00"
|
||||
"time": "2026-03-18T11:30:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.55.1",
|
||||
"version": "v12.56.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "6d9185a248d101b07eecaf8fd60b18129545fd33"
|
||||
"reference": "dac16d424b59debb2273910dde88eb7050a2a709"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/6d9185a248d101b07eecaf8fd60b18129545fd33",
|
||||
"reference": "6d9185a248d101b07eecaf8fd60b18129545fd33",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/dac16d424b59debb2273910dde88eb7050a2a709",
|
||||
"reference": "dac16d424b59debb2273910dde88eb7050a2a709",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2097,7 +2098,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2026-03-18T14:28:59+00:00"
|
||||
"time": "2026-03-26T14:51:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/passport",
|
||||
@@ -2177,16 +2178,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
"version": "v0.3.15",
|
||||
"version": "v0.3.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/prompts.git",
|
||||
"reference": "4bb8107ec97651fd3f17f897d6489dbc4d8fb999"
|
||||
"reference": "11e7d5f93803a2190b00e145142cb00a33d17ad2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/prompts/zipball/4bb8107ec97651fd3f17f897d6489dbc4d8fb999",
|
||||
"reference": "4bb8107ec97651fd3f17f897d6489dbc4d8fb999",
|
||||
"url": "https://api.github.com/repos/laravel/prompts/zipball/11e7d5f93803a2190b00e145142cb00a33d17ad2",
|
||||
"reference": "11e7d5f93803a2190b00e145142cb00a33d17ad2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2230,9 +2231,9 @@
|
||||
"description": "Add beautiful and user-friendly forms to your command-line applications.",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/prompts/issues",
|
||||
"source": "https://github.com/laravel/prompts/tree/v0.3.15"
|
||||
"source": "https://github.com/laravel/prompts/tree/v0.3.16"
|
||||
},
|
||||
"time": "2026-03-17T13:45:17+00:00"
|
||||
"time": "2026-03-23T14:35:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
@@ -2896,16 +2897,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "3.32.0",
|
||||
"version": "3.33.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem.git",
|
||||
"reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725"
|
||||
"reference": "570b8871e0ce693764434b29154c54b434905350"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/254b1595b16b22dbddaaef9ed6ca9fdac4956725",
|
||||
"reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/570b8871e0ce693764434b29154c54b434905350",
|
||||
"reference": "570b8871e0ce693764434b29154c54b434905350",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2973,9 +2974,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/flysystem/issues",
|
||||
"source": "https://github.com/thephpleague/flysystem/tree/3.32.0"
|
||||
"source": "https://github.com/thephpleague/flysystem/tree/3.33.0"
|
||||
},
|
||||
"time": "2026-02-25T17:01:41+00:00"
|
||||
"time": "2026-03-25T07:59:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-local",
|
||||
@@ -10173,22 +10174,22 @@
|
||||
},
|
||||
{
|
||||
"name": "cloudcreativity/json-api-testing",
|
||||
"version": "v6.2.0",
|
||||
"version": "v6.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cloudcreativity/json-api-testing.git",
|
||||
"reference": "bb2ff0a87013d40781ca7f959023491d6f1b4ed2"
|
||||
"reference": "c6f1460dae648a28217b182195f8ca4ed0ee9bd5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cloudcreativity/json-api-testing/zipball/bb2ff0a87013d40781ca7f959023491d6f1b4ed2",
|
||||
"reference": "bb2ff0a87013d40781ca7f959023491d6f1b4ed2",
|
||||
"url": "https://api.github.com/repos/cloudcreativity/json-api-testing/zipball/c6f1460dae648a28217b182195f8ca4ed0ee9bd5",
|
||||
"reference": "c6f1460dae648a28217b182195f8ca4ed0ee9bd5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/contracts": "^10.0|^11.0|^12.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0",
|
||||
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
|
||||
"php": "^8.2",
|
||||
"phpunit/phpunit": "^10.5|^11.0|^12.0"
|
||||
},
|
||||
@@ -10225,9 +10226,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cloudcreativity/json-api/issues",
|
||||
"source": "https://github.com/cloudcreativity/json-api-testing/tree/v6.2.0"
|
||||
"source": "https://github.com/cloudcreativity/json-api-testing/tree/v6.3.0"
|
||||
},
|
||||
"time": "2025-04-09T19:15:46+00:00"
|
||||
"time": "2026-03-28T18:04:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/class-map-generator",
|
||||
@@ -10835,28 +10836,28 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel-json-api/testing",
|
||||
"version": "v3.1.0",
|
||||
"version": "v3.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel-json-api/testing.git",
|
||||
"reference": "bbf3b31b977955eff93c47ff101c4a134a3ffa8f"
|
||||
"reference": "506d434a47ebd9bcc7dfca0096c63b79d28427a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel-json-api/testing/zipball/bbf3b31b977955eff93c47ff101c4a134a3ffa8f",
|
||||
"reference": "bbf3b31b977955eff93c47ff101c4a134a3ffa8f",
|
||||
"url": "https://api.github.com/repos/laravel-json-api/testing/zipball/506d434a47ebd9bcc7dfca0096c63b79d28427a8",
|
||||
"reference": "506d434a47ebd9bcc7dfca0096c63b79d28427a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cloudcreativity/json-api-testing": "^6.1",
|
||||
"cloudcreativity/json-api-testing": "^6.3",
|
||||
"ext-json": "*",
|
||||
"illuminate/http": "^11.0|^12.0",
|
||||
"illuminate/support": "^11.0|^12.0",
|
||||
"illuminate/testing": "^11.0|^12.0",
|
||||
"illuminate/http": "^11.0|^12.0|^13.0",
|
||||
"illuminate/support": "^11.0|^12.0|^13.0",
|
||||
"illuminate/testing": "^11.0|^12.0|^13.0",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/framework": "^11.0|^12.0",
|
||||
"laravel/framework": "^11.0|^12.0|^13.0",
|
||||
"phpunit/phpunit": "^10.5|^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
@@ -10881,7 +10882,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Christopher Gammie",
|
||||
"email": "contact@gammie.co.uk"
|
||||
"email": "chris@cloudcreativity.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "Test helpers for JSON:API compliant APIs.",
|
||||
@@ -10894,9 +10895,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel-json-api/testing/issues",
|
||||
"source": "https://github.com/laravel-json-api/testing/tree/v3.1.0"
|
||||
"source": "https://github.com/laravel-json-api/testing/tree/v3.2.0"
|
||||
},
|
||||
"time": "2025-02-24T20:39:08+00:00"
|
||||
"time": "2026-03-28T18:06:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
@@ -11219,16 +11220,16 @@
|
||||
},
|
||||
{
|
||||
"name": "php-debugbar/php-debugbar",
|
||||
"version": "v3.5.1",
|
||||
"version": "v3.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-debugbar/php-debugbar.git",
|
||||
"reference": "486b32fd98efe9a3c10f0b24c0caabc187f78f04"
|
||||
"reference": "31f7d2e7943a82e72a670335d408d967b05efa5a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/486b32fd98efe9a3c10f0b24c0caabc187f78f04",
|
||||
"reference": "486b32fd98efe9a3c10f0b24c0caabc187f78f04",
|
||||
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/31f7d2e7943a82e72a670335d408d967b05efa5a",
|
||||
"reference": "31f7d2e7943a82e72a670335d408d967b05efa5a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11305,7 +11306,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
|
||||
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.5.1"
|
||||
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.6.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11317,7 +11318,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-05T20:37:33+00:00"
|
||||
"time": "2026-03-29T17:26:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-debugbar/symfony-bridge",
|
||||
@@ -11435,11 +11436,11 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.42",
|
||||
"version": "2.1.44",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0",
|
||||
"reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/4a88c083c668b2c364a425c9b3171b2d9ea5d218",
|
||||
"reference": "4a88c083c668b2c364a425c9b3171b2d9ea5d218",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11484,7 +11485,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-17T14:58:32+00:00"
|
||||
"time": "2026-03-25T17:34:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-deprecation-rules",
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)envDefaultWhenEmpty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-03-21',
|
||||
'build_time' => 1774102650,
|
||||
'version' => 'develop/2026-03-29',
|
||||
'build_time' => 1774808978,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
|
||||
1459
package-lock.json
generated
1459
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^1",
|
||||
"laravel-vite-plugin": "^2",
|
||||
"laravel-vite-plugin": "^3",
|
||||
"patch-package": "^8",
|
||||
"sass": "^1",
|
||||
"vite": "^7",
|
||||
"vite": "^8",
|
||||
"vite-plugin-manifest-sri": "^0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -70,6 +70,7 @@ return [
|
||||
'rule_action_value' => 'This value is invalid for the selected action.',
|
||||
'file_already_attached' => 'Uploaded file ":name" is already attached to this object.',
|
||||
'file_attached' => 'Successfully uploaded file ":name".',
|
||||
'cannot_add_piggy_amount' => 'This amount cannot be added to the piggy bank.',
|
||||
'file_zero' => 'The file is zero bytes in size.',
|
||||
'must_exist' => 'The ID in field :attribute does not exist in the database.',
|
||||
'all_accounts_equal' => 'All accounts in this field must be equal.',
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
{% if objectType != 'liabilities' %}
|
||||
<th class="hidden-sm hidden-xs hidden-md">{{ trans('list.lastActivity') }}</th>
|
||||
{% endif %}
|
||||
<th class="fifteen"
|
||||
class="hidden-sm hidden-xs hidden-md">{{ trans('list.balanceDiff') }}</th>
|
||||
<th
|
||||
class="fifteen hidden-sm hidden-xs hidden-md">{{ trans('list.balanceDiff') }}</th>
|
||||
<th class="hidden-sm hidden-xs"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -282,7 +282,8 @@
|
||||
{% if transaction.source_account_id == account.id %}
|
||||
<span title="Deposit, source">{{ formatAmountBySymbol(transaction.source_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }}</span>
|
||||
{% else %}
|
||||
<span title="Deposit, dest">{{ formatAmountBySymbol(transaction.destination_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }}</span>
|
||||
{# changed from normal currency_symbol to foreign_currency_symbol for #12043 #}
|
||||
<span title="Deposit, dest">{{ formatAmountBySymbol(transaction.destination_balance_after, transaction.foreign_currency_symbol, transaction.foreign_currency_decimal_places) }}</span>
|
||||
{% endif %}
|
||||
|
||||
{% elseif transaction.transaction_type_type == 'Withdrawal' %}
|
||||
|
||||
@@ -347,7 +347,6 @@ Route::group(
|
||||
'namespace' => 'FireflyIII\Api\V1\Controllers\Models\UserGroup',
|
||||
'prefix' => 'v1/user-groups',
|
||||
'as' => 'api.v1.user-groups.',
|
||||
'middleware' => ['api-admin'],
|
||||
],
|
||||
static function (): void {
|
||||
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
||||
@@ -636,6 +635,7 @@ Route::group(
|
||||
],
|
||||
static function (): void {
|
||||
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']);
|
||||
Route::put('{currency_code?}', ['uses' => 'UpdateController@update', 'as' => 'update']);
|
||||
Route::get('primary', ['uses' => 'ShowController@showPrimary', 'as' => 'show.primary']);
|
||||
Route::get('default', ['uses' => 'ShowController@showPrimary', 'as' => 'show.default']);
|
||||
Route::get('{currency_code}', ['uses' => 'ShowController@show', 'as' => 'show']);
|
||||
@@ -666,7 +666,6 @@ Route::group(
|
||||
static function (): void {
|
||||
Route::delete('{currency_code}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']);
|
||||
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
|
||||
Route::put('{currency_code?}', ['uses' => 'UpdateController@update', 'as' => 'update']);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user