Compare commits

...

18 Commits

Author SHA1 Message Date
github-actions[bot]
be6801654f Merge pull request #11555 from firefly-iii/release-1768805748
🤖 Automatically merge the PR into the develop branch.
2026-01-19 07:55:56 +01:00
JC5
93fe1f5558 🤖 Auto commit for release 'develop' on 2026-01-19 2026-01-19 07:55:48 +01:00
James Cole
ba5b6ff694 Merge pull request #11550 from firefly-iii/dependabot/composer/develop/larastan/larastan-3.9.0 2026-01-19 07:02:34 +01:00
dependabot[bot]
492aec0652 Bump larastan/larastan from 3.8.1 to 3.9.0
Bumps [larastan/larastan](https://github.com/larastan/larastan) from 3.8.1 to 3.9.0.
- [Release notes](https://github.com/larastan/larastan/releases)
- [Changelog](https://github.com/larastan/larastan/blob/3.x/RELEASE.md)
- [Commits](https://github.com/larastan/larastan/compare/v3.8.1...v3.9.0)

---
updated-dependencies:
- dependency-name: larastan/larastan
  dependency-version: 3.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-19 04:09:13 +00:00
James Cole
c81bb44552 Fix #11546 2026-01-18 13:32:05 +01:00
James Cole
0431c95b49 Move rule actions for rule events. #11544 2026-01-18 12:13:11 +01:00
James Cole
6408db5414 Remove unused class #11544 2026-01-18 12:08:05 +01:00
James Cole
5de88a70f8 Remove piggy bank event handler and replace name event. #11544 2026-01-18 12:07:03 +01:00
James Cole
968da83c67 Migrate PiggyBankAmountIsChanged handler to listener #11544 2026-01-18 12:00:41 +01:00
James Cole
e77b4ee4a6 Rename to PiggyBankAmountIsChanged for #11544 2026-01-18 11:58:20 +01:00
James Cole
a36c775bd2 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-01-18 11:57:23 +01:00
James Cole
7773862eb9 Remove unused class. #11544 2026-01-18 11:57:17 +01:00
github-actions[bot]
ae107b1776 Merge pull request #11545 from firefly-iii/release-1768729993
🤖 Automatically merge the PR into the develop branch.
2026-01-18 10:53:20 +01:00
JC5
d1b167447b 🤖 Auto commit for release 'develop' on 2026-01-18 2026-01-18 10:53:13 +01:00
James Cole
a08ba43c23 For multiple subscriptions payment missed warning #11544 2026-01-18 10:17:50 +01:00
James Cole
64325fbce7 Remove unused class #11544 2026-01-18 10:12:39 +01:00
James Cole
aeac59e851 Subscription reminder for extension or ending. https://github.com/firefly-iii/firefly-iii/issues/11544 2026-01-18 10:11:26 +01:00
James Cole
151be7df8a Fix #11541 2026-01-18 06:07:50 +01:00
23 changed files with 328 additions and 334 deletions

View File

@@ -1,35 +0,0 @@
<?php
/*
* Updated.php
* Copyright (c) 2024 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/.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Account;
use FireflyIII\Models\Account;
use Illuminate\Queue\SerializesModels;
class Updated
{
use SerializesModels;
public function __construct(public Account $account) {}
}

View File

@@ -34,7 +34,7 @@ use Illuminate\Support\Facades\Log;
/**
* Class ChangedAmount
*/
class ChangedAmount extends Event
class PiggyBankAmountIsChanged extends Event
{
use SerializesModels;

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
/*
* ChangedName.php
* Copyright (c) 2025 james@firefly-iii.org
* PiggyBankNameIsChanged.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -21,15 +21,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\PiggyBank;
use FireflyIII\Events\Event;
use FireflyIII\Models\PiggyBank;
use Illuminate\Queue\SerializesModels;
class ChangedName extends Event
/**
* Needs to be an event because system needs old value as well as the new value.
*/
class PiggyBankNameIsChanged extends Event
{
use SerializesModels;

View File

@@ -1,10 +1,8 @@
<?php
/*
* WarnUserAboutBill.php
* Copyright (c) 2025 james@firefly-iii.org
* SubscriptionNeedsExtensionOrRenewal.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -24,18 +22,15 @@
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;
namespace FireflyIII\Events\Model\Subscription;
use FireflyIII\Events\Event;
use FireflyIII\Models\Bill;
use Illuminate\Queue\SerializesModels;
/**
* Class WarnUserAboutBill.
*/
class WarnUserAboutBill extends Event
class SubscriptionNeedsExtensionOrRenewal extends Event
{
use SerializesModels;
public function __construct(public Bill $bill, public string $field, public int $diff) {}
public function __construct(public Bill $subscription, public string $field, public int $diff) {}
}

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
/*
* WarnUserAboutOverdueSubscriptions.php
* Copyright (c) 2025 james@firefly-iii.org
* SubscriptionIsOverdueForPayment.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -21,18 +21,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;
namespace FireflyIII\Events\Model\Subscription;
use FireflyIII\Events\Event;
use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
class WarnUserAboutOverdueSubscriptions extends Event
class SubscriptionsAreOverdueForPayment extends Event
{
use SerializesModels;
public function __construct(public User $user, public array $overdue) {}
}

View File

@@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Factory;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\PiggyBankAmountIsChanged;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\ObjectGroup;
@@ -305,7 +305,7 @@ class PiggyBankFactory
if (0 !== bccomp($oldSavedAmount, $newSavedAmount)) {
Log::debug('Amount changed, will create event for it.');
// create event for difference.
event(new ChangedAmount($piggyBank, bcsub($newSavedAmount, $oldSavedAmount), null, null));
event(new PiggyBankAmountIsChanged($piggyBank, bcsub($newSavedAmount, $oldSavedAmount), null, null));
}
}
if (0 === count($toBeLinked)) {

View File

@@ -25,8 +25,8 @@ declare(strict_types=1);
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
use FireflyIII\Events\Model\Subscription\SubscriptionNeedsExtensionOrRenewal;
use FireflyIII\Events\Model\Subscription\SubscriptionsAreOverdueForPayment;
use FireflyIII\Models\Bill;
use FireflyIII\Support\Facades\Navigation;
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
@@ -143,7 +143,7 @@ class WarnAboutBills implements ShouldQueue
{
$diff = $this->getDiff($bill, $field);
Log::debug('Will now send warning!');
event(new WarnUserAboutBill($bill, $field, $diff));
event(new SubscriptionNeedsExtensionOrRenewal($bill, $field, $diff));
}
public function setDate(Carbon $date): void
@@ -197,8 +197,8 @@ class WarnAboutBills implements ShouldQueue
private function sendOverdueAlerts(User $user, array $overdue): void
{
if (count($overdue) > 0) {
Log::debug(sprintf('Will now send warning about overdue bill for user #%d.', $user->id));
event(new WarnUserAboutOverdueSubscriptions($user, $overdue));
Log::debug(sprintf('Will now send warning about overdue subscription(s) for user #%d.', $user->id));
event(new SubscriptionsAreOverdueForPayment($user, $overdue));
}
}
}

View File

@@ -1,8 +1,9 @@
<?php
declare(strict_types=1);
/*
* PiggyBankEventHandler.php
* Copyright (c) 2023 james@firefly-iii.org
* CreatesPiggyBankEventForChangedAmount.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,43 +21,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Listeners\Model\PiggyBank;
namespace FireflyIII\Handlers\Events\Model;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Models\Account;
use FireflyIII\Events\Model\PiggyBank\PiggyBankAmountIsChanged;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionGroup;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
/**
* Class PiggyBankEventHandler
*/
class PiggyBankEventHandler
class CreatesPiggyBankEventForChangedAmount implements ShouldQueue
{
public function changedPiggyBankName(ChangedName $event): void
{
// loop all accounts, collect all user's rules.
/** @var Account $account */
foreach ($event->piggyBank->accounts as $account) {
/** @var Rule $rule */
foreach ($account->user->rules as $rule) {
/** @var RuleAction $ruleAction */
foreach ($rule->ruleActions()->where('action_type', 'update_piggy')->get() as $ruleAction) {
if ($event->oldName === $ruleAction->action_value) {
$ruleAction->action_value = $event->newName;
$ruleAction->save();
}
}
}
}
}
public function changePiggyAmount(ChangedAmount $event): void
public function handle(PiggyBankAmountIsChanged $event): void
{
// find journal if group is present.
$journal = $event->transactionJournal;

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/*
* UpdatesRulesForChangedPiggyBankName.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\Listeners\Model\PiggyBank;
use FireflyIII\Events\Model\PiggyBank\PiggyBankNameIsChanged;
use FireflyIII\Models\Account;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use Illuminate\Contracts\Queue\ShouldQueue;
class UpdatesRulesForChangedPiggyBankName implements ShouldQueue
{
public function handle(PiggyBankNameIsChanged $event): void
{
// loop all accounts, collect all user's rules.
/** @var Account $account */
foreach ($event->piggyBank->accounts as $account) {
/** @var Rule $rule */
foreach ($account->user->rules as $rule) {
/** @var RuleAction $ruleAction */
foreach ($rule->ruleActions()->where('action_type', 'update_piggy')->get() as $ruleAction) {
if ($event->oldName === $ruleAction->action_value) {
$ruleAction->action_value = $event->newName;
$ruleAction->save();
}
}
}
}
}
}

View File

@@ -1,8 +1,9 @@
<?php
declare(strict_types=1);
/*
* RuleHandler.php
* Copyright (c) 2023 james@firefly-iii.org
* NotifiesUserAboutFailedRuleAction.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,24 +21,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Handlers\Events\Model;
namespace FireflyIII\Listeners\Model\Rule;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Notifications\User\RuleActionFailed;
use FireflyIII\Support\Facades\Preferences;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
/**
* Class RuleHandler
*/
class RuleHandler
class NotifiesUserAboutFailedRuleAction implements ShouldQueue
{
public function ruleActionFailedOnArray(RuleActionFailedOnArray $event): void
public function handle(RuleActionFailedOnArray|RuleActionFailedOnObject $event): void
{
$ruleAction = $event->ruleAction;
$rule = $ruleAction->rule;
@@ -52,9 +49,12 @@ class RuleHandler
$error = $event->error;
$user = $ruleAction->rule->user;
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal['transaction_group_id'], 'error' => $error]);
$groupTitle = $journal['description'] ?? '';
$groupLink = route('transactions.show', [$journal['transaction_group_id']]);
$groupId = is_array($journal) ? $journal['transaction_group_id'] : $journal->transaction_group_id;
$groupTitle = is_array($journal) ? ($journal['description'] ?? '') : ($journal->description ?? '');
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $groupId, 'error' => $error]);
$groupLink = route('transactions.show', [$groupId]);
$ruleTitle = $rule->title;
$ruleLink = route('rules.edit', [$rule->id]);
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
@@ -65,33 +65,4 @@ class RuleHandler
Log::error(sprintf('[a] Error sending notification that the rule action failed: %s', $e->getMessage()));
}
}
public function ruleActionFailedOnObject(RuleActionFailedOnObject $event): void
{
$ruleAction = $event->ruleAction;
$rule = $ruleAction->rule;
/** @var bool $preference */
$preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data;
if (false === $preference) {
return;
}
Log::debug('Now in ruleActionFailedOnObject');
$journal = $event->journal;
$error = $event->error;
$user = $ruleAction->rule->user;
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal->transaction_group_id, 'error' => $error]);
$groupTitle = $journal->description ?? '';
$groupLink = route('transactions.show', [$journal->transaction_group_id]);
$ruleTitle = $rule->title;
$ruleLink = route('rules.edit', [$rule->id]);
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
try {
Notification::send($user, new RuleActionFailed($params));
} catch (ClientException $e) {
Log::error(sprintf('[b] Error sending notification that the rule action failed: %s', $e->getMessage()));
}
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/*
* NotifiesAboutExtensionOrRenewal.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\Listeners\Model\Subscription;
use Exception;
use FireflyIII\Events\Model\Subscription\SubscriptionNeedsExtensionOrRenewal;
use FireflyIII\Notifications\User\BillReminder;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
class NotifiesAboutExtensionOrRenewal implements ShouldQueue
{
public function handle(SubscriptionNeedsExtensionOrRenewal $event): void
{
Log::debug(sprintf('Now in %s', __METHOD__));
$subscription = $event->subscription;
/** @var bool $preference */
$preference = Preferences::getForUser($subscription->user, 'notification_bill_reminder', true)->data;
if (true === $preference) {
Log::debug('Subscription reminder is true!');
try {
Notification::send($subscription->user, new BillReminder($subscription, $event->field, $event->diff));
} catch (Exception $e) {
$message = $e->getMessage();
if (str_contains($message, 'Bcc')) {
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
if (str_contains($message, 'RFC 2822')) {
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
return;
}
Log::debug('User has disabled subscription reminders.');
}
}

View File

@@ -1,8 +1,9 @@
<?php
declare(strict_types=1);
/*
* BillEventHandler.php
* Copyright (c) 2022 james@firefly-iii.org
* NotifiesAboutOverdueSubscription.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,35 +21,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Handlers\Events;
namespace FireflyIII\Listeners\Model\Subscription;
use Exception;
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
use FireflyIII\Events\Model\Subscription\SubscriptionsAreOverdueForPayment;
use FireflyIII\Models\Bill;
use FireflyIII\Notifications\User\BillReminder;
use FireflyIII\Notifications\User\SubscriptionsOverdueReminder;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use function Safe\json_encode;
/**
* Class BillEventHandler
*/
class BillEventHandler
class NotifiesAboutOverdueSubscriptions implements ShouldQueue
{
public function warnAboutOverdueSubscriptions(WarnUserAboutOverdueSubscriptions $event): void
public function handle(SubscriptionsAreOverdueForPayment $event): void
{
Log::debug(sprintf('Now in %s', __METHOD__));
// make sure user does not get the warning twice.
$overdue = $event->overdue;
$user = $event->user;
$toBeWarned = [];
Log::debug(sprintf('%d bills to warn about.', count($overdue)));
Log::debug(sprintf('%d subscriptions to warn about.', count($overdue)));
foreach ($overdue as $item) {
/** @var Bill $bill */
$bill = $item['bill'];
@@ -62,12 +55,12 @@ class BillEventHandler
$toBeWarned[] = $item;
}
unset($bill);
Log::debug(sprintf('Now %d bills to warn about.', count($toBeWarned)));
Log::debug(sprintf('Now %d subscription(s) to warn about.', count($toBeWarned)));
/** @var bool $sendNotification */
$sendNotification = Preferences::getForUser($user, 'notification_bill_reminder', true)->data;
if (false === $sendNotification) {
Log::debug('User has disabled bill reminders.');
Log::debug('User has disabled subscription reminders.');
return;
}
@@ -105,39 +98,4 @@ class BillEventHandler
}
public function warnAboutBill(WarnUserAboutBill $event): void
{
Log::debug(sprintf('Now in %s', __METHOD__));
$bill = $event->bill;
/** @var bool $preference */
$preference = Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
if (true === $preference) {
Log::debug('Bill reminder is true!');
try {
Notification::send($bill->user, new BillReminder($bill, $event->field, $event->diff));
} catch (Exception $e) {
$message = $e->getMessage();
if (str_contains($message, 'Bcc')) {
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
if (str_contains($message, 'RFC 2822')) {
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
return;
}
Log::debug('User has disabled bill reminders.');
}
}

View File

@@ -27,12 +27,6 @@ use FireflyIII\Events\ActuallyLoggedIn;
use FireflyIII\Events\Admin\InvitationCreated;
use FireflyIII\Events\DestroyedTransactionGroup;
use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup;
use FireflyIII\Events\NewVersionAvailable;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
@@ -73,145 +67,145 @@ class EventServiceProvider extends ServiceProvider
protected $listen
= [
// is a User related event.
RegisteredUser::class => [
RegisteredUser::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail',
'FireflyIII\Handlers\Events\UserEventHandler@sendAdminRegistrationNotification',
'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole',
'FireflyIII\Handlers\Events\UserEventHandler@createGroupMembership',
'FireflyIII\Handlers\Events\UserEventHandler@createExchangeRates',
],
UserAttemptedLogin::class => [
UserAttemptedLogin::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendLoginAttemptNotification',
],
// is a User related event.
Login::class => [
Login::class => [
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
],
ActuallyLoggedIn::class => [
ActuallyLoggedIn::class => [
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
],
DetectedNewIPAddress::class => [
DetectedNewIPAddress::class => [
'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress',
],
RequestedVersionCheckStatus::class => [
RequestedVersionCheckStatus::class => [
'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates',
],
RequestedReportOnJournals::class => [
RequestedReportOnJournals::class => [
'FireflyIII\Handlers\Events\AutomationHandler@reportJournals',
],
// is a User related event.
RequestedNewPassword::class => [
RequestedNewPassword::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword',
],
UserTestNotificationChannel::class => [
UserTestNotificationChannel::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendTestNotification',
],
// is a User related event.
UserChangedEmail::class => [
UserChangedEmail::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeConfirmMail',
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail',
],
// admin related
OwnerTestNotificationChannel::class => [
OwnerTestNotificationChannel::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification',
],
NewVersionAvailable::class => [
NewVersionAvailable::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendNewVersion',
],
InvitationCreated::class => [
InvitationCreated::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification',
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
],
UnknownUserAttemptedLogin::class => [
UnknownUserAttemptedLogin::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendLoginAttemptNotification',
],
// is a Transaction Journal related event.
StoredTransactionGroup::class => [
StoredTransactionGroup::class => [
'FireflyIII\Handlers\Events\StoredGroupEventHandler@runAllHandlers',
],
TriggeredStoredTransactionGroup::class => [
TriggeredStoredTransactionGroup::class => [
'FireflyIII\Handlers\Events\StoredGroupEventHandler@triggerRulesManually',
],
// is a Transaction Journal related event.
UpdatedTransactionGroup::class => [
UpdatedTransactionGroup::class => [
'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@runAllHandlers',
],
DestroyedTransactionGroup::class => [
DestroyedTransactionGroup::class => [
'FireflyIII\Handlers\Events\DestroyedGroupEventHandler@runAllHandlers',
],
// API related events:
AccessTokenCreated::class => [
AccessTokenCreated::class => [
'FireflyIII\Handlers\Events\APIEventHandler@accessTokenCreated',
],
// Webhook related event:
RequestedSendWebhookMessages::class => [
RequestedSendWebhookMessages::class => [
'FireflyIII\Handlers\Events\WebhookEventHandler@sendWebhookMessages',
],
// account related events:
StoredAccount::class => [
StoredAccount::class => [
'FireflyIII\Handlers\Events\StoredAccountEventHandler@recalculateCredit',
],
UpdatedAccount::class => [
UpdatedAccount::class => [
'FireflyIII\Handlers\Events\UpdatedAccountEventHandler@recalculateCredit',
],
// bill related events:
WarnUserAboutBill::class => [
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill',
],
WarnUserAboutOverdueSubscriptions::class => [
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutOverdueSubscriptions',
],
// subscription related events:
// SubscriptionNeedsExtensionOrRenewal::class => [
// 'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill',
// ],
// WarnUserAboutOverdueSubscriptions::class => [
// 'FireflyIII\Handlers\Events\BillEventHandler@warnAboutOverdueSubscriptions',
// ],
// audit log events:
TriggeredAuditLog::class => [
TriggeredAuditLog::class => [
'FireflyIII\Handlers\Events\AuditEventHandler@storeAuditEvent',
],
// piggy bank related events:
ChangedAmount::class => [
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount',
],
ChangedName::class => [
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
],
// PiggyBankAmountIsChanged::class => [
// 'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount',
// ],
// ChangedName::class => [
// 'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
// ],
// rule actions
RuleActionFailedOnArray::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
],
RuleActionFailedOnObject::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject',
],
// RuleActionFailedOnArray::class => [
// 'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
// ],
// RuleActionFailedOnObject::class => [
// 'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject',
// ],
// security related
EnabledMFA::class => [
EnabledMFA::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAEnabledMail',
],
DisabledMFA::class => [
DisabledMFA::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFADisabledMail',
],
MFANewBackupCodes::class => [
MFANewBackupCodes::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendNewMFABackupCodesMail',
],
MFAUsedBackupCode::class => [
MFAUsedBackupCode::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendUsedBackupCodeMail',
],
MFABackupFewLeft::class => [
MFABackupFewLeft::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupFewLeftMail',
],
MFABackupNoLeft::class => [
MFABackupNoLeft::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupNoLeftMail',
],
MFAManyFailedAttempts::class => [
MFAManyFailedAttempts::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAFailedAttemptsMail',
],
// preferences
UserGroupChangedPrimaryCurrency::class => [
UserGroupChangedPrimaryCurrency::class => [
'FireflyIII\Handlers\Events\PreferencesEventHandler@resetPrimaryCurrencyAmounts',
],
];

View File

@@ -25,8 +25,8 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\PiggyBank;
use Exception;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Events\Model\PiggyBank\PiggyBankAmountIsChanged;
use FireflyIII\Events\Model\PiggyBank\PiggyBankNameIsChanged;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\PiggyBankFactory;
use FireflyIII\Models\Account;
@@ -68,7 +68,7 @@ trait ModifiesPiggyBanks
{
$currentAmount = $this->getCurrentAmount($piggyBank, $account);
$pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot;
$pivot->current_amount = bcsub((string) $currentAmount, $amount);
$pivot->current_amount = bcsub((string)$currentAmount, $amount);
$pivot->native_current_amount = null;
// also update native_current_amount.
@@ -82,7 +82,7 @@ trait ModifiesPiggyBanks
$pivot->save();
Log::debug('ChangedAmount: removeAmount [a]: Trigger change for negative amount.');
event(new ChangedAmount($piggyBank, bcmul($amount, '-1'), $journal, null));
event(new PiggyBankAmountIsChanged($piggyBank, bcmul($amount, '-1'), $journal, null));
return true;
}
@@ -91,7 +91,7 @@ trait ModifiesPiggyBanks
{
$currentAmount = $this->getCurrentAmount($piggyBank, $account);
$pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot;
$pivot->current_amount = bcadd((string) $currentAmount, $amount);
$pivot->current_amount = bcadd((string)$currentAmount, $amount);
$pivot->native_current_amount = null;
// also update native_current_amount.
@@ -105,7 +105,7 @@ trait ModifiesPiggyBanks
$pivot->save();
Log::debug('ChangedAmount: addAmount [b]: Trigger change for positive amount.');
event(new ChangedAmount($piggyBank, $amount, $journal, null));
event(new PiggyBankAmountIsChanged($piggyBank, $amount, $journal, null));
return true;
}
@@ -123,13 +123,13 @@ trait ModifiesPiggyBanks
if (0 !== bccomp($piggyBank->target_amount, '0')) {
$leftToSave = bcsub($piggyBank->target_amount, (string) $savedSoFar);
$maxAmount = 1 === bccomp((string) $leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount;
$leftToSave = bcsub($piggyBank->target_amount, (string)$savedSoFar);
$maxAmount = 1 === bccomp((string)$leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount;
Log::debug(sprintf('Left to save: %s', $leftToSave));
Log::debug(sprintf('Maximum amount: %s', $maxAmount));
}
$compare = bccomp($amount, (string) $maxAmount);
$compare = bccomp($amount, (string)$maxAmount);
$result = $compare <= 0;
Log::debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true)));
@@ -141,7 +141,7 @@ trait ModifiesPiggyBanks
{
$savedSoFar = $this->getCurrentAmount($piggyBank, $account);
return bccomp($amount, (string) $savedSoFar) <= 0;
return bccomp($amount, (string)$savedSoFar) <= 0;
}
/**
@@ -178,11 +178,11 @@ trait ModifiesPiggyBanks
if (-1 === bccomp($difference, '0')) {
Log::debug('ChangedAmount: addAmount [c]: Trigger change for negative amount.');
event(new ChangedAmount($piggyBank, $difference, null, null));
event(new PiggyBankAmountIsChanged($piggyBank, $difference, null, null));
}
if (1 === bccomp($difference, '0')) {
Log::debug('ChangedAmount: addAmount [d]: Trigger change for positive amount.');
event(new ChangedAmount($piggyBank, $difference, null, null));
event(new PiggyBankAmountIsChanged($piggyBank, $difference, null, null));
}
return $piggyBank;
@@ -234,9 +234,9 @@ trait ModifiesPiggyBanks
// if the piggy bank is now smaller than the sum of the money saved,
// remove money from all accounts until the piggy bank is the right amount.
$currentAmount = $this->getCurrentAmount($piggyBank);
if (1 === bccomp((string) $currentAmount, (string)$piggyBank->target_amount) && 0 !== bccomp((string)$piggyBank->target_amount, '0')) {
if (1 === bccomp((string)$currentAmount, (string)$piggyBank->target_amount) && 0 !== bccomp((string)$piggyBank->target_amount, '0')) {
Log::debug(sprintf('Current amount is %s, target amount is %s', $currentAmount, $piggyBank->target_amount));
$difference = bcsub((string)$piggyBank->target_amount, (string) $currentAmount);
$difference = bcsub((string)$piggyBank->target_amount, (string)$currentAmount);
// an amount will be removed, create "negative" event:
// Log::debug(sprintf('ChangedAmount: is triggered with difference "%s"', $difference));
@@ -283,7 +283,7 @@ trait ModifiesPiggyBanks
private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank
{
if (array_key_exists('name', $data) && '' !== $data['name']) {
event(new ChangedName($piggyBank, $piggyBank->name, $data['name']));
event(new PiggyBankNameIsChanged($piggyBank, $piggyBank->name, $data['name']));
$piggyBank->name = $data['name'];
}
if (array_key_exists('transaction_currency_id', $data) && is_int($data['transaction_currency_id'])) {

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\PiggyBank;
use Carbon\Carbon;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\PiggyBankAmountIsChanged;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\PiggyBankFactory;
use FireflyIII\Models\Account;
@@ -448,7 +448,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
$piggyBank->piggyBankEvents()->delete();
foreach ($piggyBank->accounts as $account) {
if (0 !== bccomp('0', (string) $account->pivot->current_amount)) {
event(new ChangedAmount($piggyBank, $account->pivot->current_amount, null, null));
event(new PiggyBankAmountIsChanged($piggyBank, $account->pivot->current_amount, null, null));
}
}
}

View File

@@ -255,7 +255,7 @@ class UserRepository implements UserRepositoryInterface
if (!$user instanceof User) {
throw new FireflyException('User is not a User object.');
}
$now = today(config('app.timezone'));
$now = now(config('app.timezone'));
$now->addDays(2);
$invitee = new InvitedUser();
$invitee->user()->associate($user);

View File

@@ -32,6 +32,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
@@ -97,10 +98,9 @@ class AccountBalanceCalculator
return '0';
}
Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d')));
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereNull('transactions.deleted_at')
->where('transaction_journals.transaction_currency_id', $currencyId)
->where('transactions.transaction_currency_id', $currencyId)
->whereNull('transaction_journals.deleted_at')
// this order is the same as GroupCollector
->orderBy('transaction_journals.date', 'DESC')
@@ -110,21 +110,32 @@ class AccountBalanceCalculator
->orderBy('transactions.amount', 'DESC')
->where('transactions.account_id', $accountId)
;
$notBefore->startOfDay();
$query->where('transaction_journals.date', '<', $notBefore);
$first = $query->first(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount', 'transactions.balance_after']);
if (null === $first) {
Log::debug(sprintf('Found no transactions for currency #%d and account #%d, return 0.', $currencyId, $accountId));
return '0';
}
$balance = (string)($first->balance_after ?? '0');
Log::debug(sprintf('getLatestBalance: found balance: %s in transaction #%d', $balance, $first->id ?? 0));
Log::debug(sprintf('getLatestBalance: found balance: %s in transaction #%d on moment %s', Steam::bcround($balance, 2), $first->id ?? 0, $notBefore->format('Y-m-d H:i:s')));
return $balance;
}
private function optimizedCalculation(Collection $accounts, ?Carbon $notBefore = null): void
{
Log::debug('start of optimizedCalculation');
if ($notBefore instanceof Carbon) {
$notBefore->startOfDay();
}
Log::debug(sprintf('start of optimizedCalculation with date "%s"', $notBefore?->format('Y-m-d H:i:s')));
if ($accounts->count() > 0) {
Log::debug(sprintf('Limited to %d account(s)', $accounts->count()));
Log::debug(sprintf('Limited to %d account(s): %s', $accounts->count(), implode(', ', $accounts->pluck('id')->toArray())));
}
// collect all transactions and the change they make.
$balances = [];
@@ -143,18 +154,18 @@ class AccountBalanceCalculator
$query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray());
}
if ($notBefore instanceof Carbon) {
$notBefore->startOfDay();
$query->where('transaction_journals.date', '>=', $notBefore);
}
$set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']);
Log::debug(sprintf('Counted %d transaction(s)', $set->count()));
Log::debug(sprintf('Found %d transaction(s)', $set->count()));
// the balance value is an array.
// first entry is the balance, second is the date.
/** @var Transaction $entry */
foreach ($set as $entry) {
Log::debug(sprintf('Processing transaction #%d with currency #%d and amount %s', $entry->id, $entry->transaction_currency_id, Steam::bcround($entry->amount)));
// start with empty array:
$balances[$entry->account_id] ??= [];
$balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null];
@@ -162,6 +173,9 @@ class AccountBalanceCalculator
// before and after are easy:
$before = $balances[$entry->account_id][$entry->transaction_currency_id][0];
$after = bcadd($before, (string)$entry->amount);
Log::debug(sprintf('Before:%s, after:%s', Steam::bcround($before, 2), Steam::bcround($after, 2)));
if (true === $entry->balance_dirty || $accounts->count() > 0) {
// update the transaction:
$entry->balance_before = $before;

12
composer.lock generated
View File

@@ -10706,16 +10706,16 @@
},
{
"name": "larastan/larastan",
"version": "v3.8.1",
"version": "v3.9.0",
"source": {
"type": "git",
"url": "https://github.com/larastan/larastan.git",
"reference": "ff3725291bc4c7e6032b5a54776e3e5104c86db9"
"reference": "82c18890d0d5b012bc39a3432531e5b6cd1b4b3a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/ff3725291bc4c7e6032b5a54776e3e5104c86db9",
"reference": "ff3725291bc4c7e6032b5a54776e3e5104c86db9",
"url": "https://api.github.com/repos/larastan/larastan/zipball/82c18890d0d5b012bc39a3432531e5b6cd1b4b3a",
"reference": "82c18890d0d5b012bc39a3432531e5b6cd1b4b3a",
"shasum": ""
},
"require": {
@@ -10784,7 +10784,7 @@
],
"support": {
"issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.8.1"
"source": "https://github.com/larastan/larastan/tree/v3.9.0"
},
"funding": [
{
@@ -10792,7 +10792,7 @@
"type": "github"
}
],
"time": "2025-12-11T16:37:35+00:00"
"time": "2026-01-17T23:00:37+00:00"
},
{
"name": "laravel-json-api/testing",

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-01-17',
'build_time' => 1768662564,
'version' => 'develop/2026-01-19',
'build_time' => 1768805645,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

54
package-lock.json generated
View File

@@ -3350,42 +3350,42 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz",
"integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==",
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.27.tgz",
"integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/shared": "3.5.26",
"@vue/shared": "3.5.27",
"entities": "^7.0.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz",
"integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==",
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz",
"integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.26",
"@vue/shared": "3.5.26"
"@vue/compiler-core": "3.5.27",
"@vue/shared": "3.5.27"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz",
"integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/compiler-core": "3.5.26",
"@vue/compiler-dom": "3.5.26",
"@vue/compiler-ssr": "3.5.26",
"@vue/shared": "3.5.26",
"@vue/compiler-core": "3.5.27",
"@vue/compiler-dom": "3.5.27",
"@vue/compiler-ssr": "3.5.27",
"@vue/shared": "3.5.27",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.21",
"postcss": "^8.5.6",
@@ -3393,14 +3393,14 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz",
"integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.26",
"@vue/shared": "3.5.26"
"@vue/compiler-dom": "3.5.27",
"@vue/shared": "3.5.27"
}
},
"node_modules/@vue/component-compiler-utils": {
@@ -3482,9 +3482,9 @@
"license": "MIT"
},
"node_modules/@vue/shared": {
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz",
"integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==",
"version": "3.5.27",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.27.tgz",
"integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==",
"dev": true,
"license": "MIT"
},
@@ -4564,9 +4564,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001764",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz",
"integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==",
"version": "1.0.30001765",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz",
"integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==",
"dev": true,
"funding": [
{

View File

@@ -121,8 +121,10 @@ export default {
let srcType = this.source.type ? this.source.type.toLowerCase() : 'invalid';
let tType = this.transactionType ? this.transactionType.toLowerCase() : 'invalid';
let liabilities = ['loan', 'debt', 'mortgage'];
let asset = ['asset account'];
let sourceIsLiability = liabilities.indexOf(srcType) !== -1;
let destIsLiability = liabilities.indexOf(destType) !== -1;
let destIsAsset =asset.indexOf(destType) !== -1;
// console.log(srcType + ' (source) is a liability: ' + sourceIsLiability);
@@ -131,18 +133,15 @@ export default {
if (tType === 'transfer' || destIsLiability || sourceIsLiability) {
console.log('Source or dest is a liability.')
console.log('Source is liability OR dest is liability, OR transfer. Lock list on currency of destination.');
console.log(this.destination.type);
// console.log('Length of currencies is ' + this.currencies.length);
// console.log(this.currencies);
this.liability = true;
// lock dropdown list on currencyID of destination UNLESS dest is not liab
for (const key in this.currencies) {
if (this.currencies.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
if (
parseInt(this.currencies[key].id) === parseInt(this.destination.currency_id) || !destIsLiability
) {
console.log('Enable currency!!');
console.log(this.currencies[key]);
// console.log(this.destination);
if (parseInt(this.currencies[key].id) === parseInt(this.destination.currency_id) || (!destIsLiability && 'transfer' !== tType)) {
console.log('Enable currency: ' + this.currencies[key].attributes.code + '.');
this.enabledCurrencies.push(this.currencies[key]);
}
}

View File

@@ -1,11 +1,11 @@
{
"firefly": {
"administrations_page_title": "Financial administrations",
"administrations_index_menu": "Financial administrations",
"administrations_page_title": "Administrasi keuangan",
"administrations_index_menu": "Administrasi keuangan",
"expires_at": "Expires at",
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
"temp_administrations_introduction": "Firefly III segera akan memiliki kemampuan untuk mengelola beberapa administrasi keuangan. Saat ini, Anda hanya memiliki satu. Anda dapat mengatur judul administrasi ini serta mata uang utamanya. Pengaturan ini menggantikan pengaturan sebelumnya di mana Anda menetapkan \u201cmata uang default\u201d. Sekarang, pengaturan ini terkait langsung dengan masing-masing administrasi keuangan dan dapat berbeda untuk setiap administrasi.",
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
"administrations_page_edit_sub_title_js": "Edit financial administration \"{title}\"",
"administrations_page_edit_sub_title_js": "Edit administrasi keuangan \u201c{title}\u201d",
"table": "Meja",
"welcome_back": "Apa yang sedang dimainkan?",
"flash_error": "Kesalahan!",
@@ -20,7 +20,7 @@
"split": "Pisah",
"single_split": "Pisah",
"not_enough_currencies": "Not enough currencies",
"not_enough_currencies_enabled": "If you have just one currency enabled, there is no need to add exchange rates.",
"not_enough_currencies_enabled": "Jika Anda hanya mengaktifkan satu mata uang, tidak perlu menambahkan nilai tukar.",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
"webhook_stored_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID} (\"{title}\")<\/a> has been stored.",
"webhook_updated_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID}<\/a> (\"{title}\") has been updated.",
@@ -31,7 +31,7 @@
"apply_rules_checkbox": "Apply rules",
"fire_webhooks_checkbox": "Fire webhooks",
"no_budget_pointer": "Anda tampaknya belum memiliki anggaran. Anda harus membuat beberapa di halaman-<a href=\"budgets\">anggaran<\/a>. Anggaran dapat membantu anda melacak pengeluaran.",
"no_bill_pointer": "You seem to have no subscription yet. You should create some on the <a href=\"subscriptions\">subscription<\/a>-page. Subscriptions can help you keep track of expenses.",
"no_bill_pointer": "Sepertinya Anda belum memiliki langganan. Buat beberapa di halaman <a href=\"subscriptions\">Langganan<\/a>. Langganan dapat membantu Anda melacak pengeluaran.",
"source_account": "Akun sumber",
"hidden_fields_preferences": "You can enable more transaction options in your <a href=\"preferences\">preferences<\/a>.",
"destination_account": "Akun tujuan",
@@ -50,7 +50,7 @@
"category": "Kategori",
"attachments": "Lampiran",
"notes": "Notes",
"external_url": "URL luar",
"external_url": "URL eksternal",
"update_transaction": "Update transaction",
"after_update_create_another": "After updating, return here to continue editing.",
"store_as_new": "Store as a new transaction instead of updating.",
@@ -59,7 +59,7 @@
"no_piggy_bank": "(tidak ada celengan)",
"description": "Deskripsi",
"split_transaction_title_help": "If you create a split transaction, there must be a global description for all splits of the transaction.",
"destination_account_reconciliation": "You can't edit the destination account of a reconciliation transaction.",
"destination_account_reconciliation": "Anda tidak dapat mengubah rekening tujuan pada transaksi rekonsiliasi.",
"source_account_reconciliation": "Anda tidak dapat mengedit akun sumber dari transaksi rekonsiliasi.",
"budget": "Anggaran",
"bill": "Subscription",
@@ -146,14 +146,14 @@
"response": "Tanggapan",
"visit_webhook_url": "Visit webhook URL",
"reset_webhook_secret": "Reset webhook secret",
"header_exchange_rates": "Exchange rates",
"exchange_rates_intro": "Firefly III supports downloading and using exchange rates. Read more about this in <a href=\"https:\/\/docs.firefly-iii.org\/explanation\/financial-concepts\/exchange-rates\/\">the documentation<\/a>.",
"exchange_rates_from_to": "Between {from} and {to} (and the other way around)",
"exchange_rates_intro_rates": "Firefly III uses the following exchange rates. The inverse is automatically calculated when it is not provided. If no exchange rate exists for the date of the transaction, Firefly III will go back in time to find one. If none are present, the rate \"1\" will be used.",
"header_exchange_rates_rates": "Exchange rates",
"header_exchange_rates_table": "Table with exchange rates",
"header_exchange_rates": "Nilai tukar",
"exchange_rates_intro": "Firefly III mendukung pengunduhan dan penggunaan nilai tukar. Baca selengkapnya di <a href=\"https:\/\/docs.firefly-iii.org\/explanation\/financial-concepts\/exchange-rates\/\">dokumentasi<\/a>.",
"exchange_rates_from_to": "Antara {from} dan {to} (dan sebaliknya)",
"exchange_rates_intro_rates": "Firefly III menggunakan nilai tukar berikut. Nilai tukar terbalik akan dihitung secara otomatis jika tidak tersedia. Jika tidak ada nilai tukar untuk tanggal transaksi, Firefly III akan mencari nilai tukar sebelumnya. Jika tetap tidak ditemukan, nilai tukar \u201c1\u201d akan digunakan.",
"header_exchange_rates_rates": "Nilai tukar",
"header_exchange_rates_table": "Tabel dengan nilai tukar",
"help_rate_form": "On this day, how many {to} will you get for one {from}?",
"add_new_rate": "Add a new exchange rate",
"add_new_rate": "Tambahkan nilai tukar baru",
"save_new_rate": "Save new rate"
},
"form": {

View File

@@ -314,7 +314,12 @@
{% if account.id == transaction.source_account_id %}
<span title="Transfer, source">{{ formatAmountBySymbol(transaction.source_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }}</span>
{% else %}
<span title="Transfer, dest">{{ formatAmountBySymbol(transaction.destination_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }}</span>
{% if null == transaction.foreign_currency_id %}
<span title="Transfer, dest, normal currency">{{ formatAmountBySymbol(transaction.destination_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }}</span>
{% endif %}
{% if null != transaction.foreign_currency_id %}
<span title="Transfer, dest, foreign currency">{{ formatAmountBySymbol(transaction.destination_balance_after, transaction.foreign_currency_symbol, transaction.foreign_currency_decimal_places) }}</span>
{% endif %}
{% endif %}
{% else %}
&nbsp;