From a08ba43c235db34f784e0d45547b9bb168dc765b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 18 Jan 2026 10:17:50 +0100 Subject: [PATCH] For multiple subscriptions payment missed warning #11544 --- .../SubscriptionsAreOverdueForPayment.php | 33 ++++++++ app/Jobs/WarnAboutBills.php | 15 ++-- .../NotifiesAboutOverdueSubscriptions.php} | 43 ++++------- app/Providers/EventServiceProvider.php | 77 +++++++++---------- 4 files changed, 95 insertions(+), 73 deletions(-) create mode 100644 app/Events/Model/Subscription/SubscriptionsAreOverdueForPayment.php rename app/{Handlers/Events/BillEventHandler.php => Listeners/Model/Subscription/NotifiesAboutOverdueSubscriptions.php} (72%) diff --git a/app/Events/Model/Subscription/SubscriptionsAreOverdueForPayment.php b/app/Events/Model/Subscription/SubscriptionsAreOverdueForPayment.php new file mode 100644 index 0000000000..3abbbcf774 --- /dev/null +++ b/app/Events/Model/Subscription/SubscriptionsAreOverdueForPayment.php @@ -0,0 +1,33 @@ +. + */ + +namespace FireflyIII\Events\Model\Subscription; + +use FireflyIII\Events\Event; +use FireflyIII\User; +use Illuminate\Queue\SerializesModels; + +class SubscriptionsAreOverdueForPayment extends Event +{ + use SerializesModels; + + public function __construct(public User $user, public array $overdue) {} +} diff --git a/app/Jobs/WarnAboutBills.php b/app/Jobs/WarnAboutBills.php index 9e9e7c5210..777d596e08 100644 --- a/app/Jobs/WarnAboutBills.php +++ b/app/Jobs/WarnAboutBills.php @@ -25,7 +25,6 @@ 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\Models\Bill; @@ -57,12 +56,12 @@ class WarnAboutBills implements ShouldQueue */ public function __construct(?Carbon $date) { - $newDate = new Carbon(); + $newDate = new Carbon(); $newDate->startOfDay(); - $this->date = $newDate; + $this->date = $newDate; if ($date instanceof Carbon) { - $newDate = clone $date; + $newDate = clone $date; $newDate->startOfDay(); $this->date = $newDate; } @@ -149,7 +148,7 @@ class WarnAboutBills implements ShouldQueue public function setDate(Carbon $date): void { - $newDate = clone $date; + $newDate = clone $date; $newDate->startOfDay(); $this->date = $newDate; } @@ -171,7 +170,7 @@ class WarnAboutBills implements ShouldQueue $enrichment->setEnd($end); /** @var Bill $single */ - $single = $enrichment->enrichSingle($bill); + $single = $enrichment->enrichSingle($bill); return [ 'pay_dates' => $single->meta['pay_dates'] ?? [], @@ -181,7 +180,7 @@ class WarnAboutBills implements ShouldQueue private function needsOverdueAlert(array $dates): bool { - $count = count($dates['pay_dates']) - count($dates['paid_dates']); + $count = count($dates['pay_dates']) - count($dates['paid_dates']); if (0 === $count || 0 === count($dates['pay_dates'])) { return false; } @@ -189,7 +188,7 @@ class WarnAboutBills implements ShouldQueue $earliest = new Carbon($dates['pay_dates'][0]); $earliest->startOfDay(); Log::debug(sprintf('Earliest expected pay date is %s', $earliest->toAtomString())); - $diff = $earliest->diffInDays($this->date); + $diff = $earliest->diffInDays($this->date); Log::debug(sprintf('Difference in days is %s', $diff)); return $diff >= 6; // FIXME hard coded value. diff --git a/app/Handlers/Events/BillEventHandler.php b/app/Listeners/Model/Subscription/NotifiesAboutOverdueSubscriptions.php similarity index 72% rename from app/Handlers/Events/BillEventHandler.php rename to app/Listeners/Model/Subscription/NotifiesAboutOverdueSubscriptions.php index 10d4f0901c..3f1b16821d 100644 --- a/app/Handlers/Events/BillEventHandler.php +++ b/app/Listeners/Model/Subscription/NotifiesAboutOverdueSubscriptions.php @@ -1,8 +1,7 @@ . */ -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\SubscriptionNeedsExtensionOrRenewal; +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))); + $overdue = $event->overdue; + $user = $event->user; + $toBeWarned = []; + Log::debug(sprintf('%d subscriptions to warn about.', count($overdue))); foreach ($overdue as $item) { /** @var Bill $bill */ - $bill = $item['bill']; - $key = sprintf('bill_overdue_%s_%s', $bill->id, substr(hash('sha256', json_encode($item['dates']['pay_dates'], JSON_THROW_ON_ERROR)), 0, 10)); - $pref = Preferences::getForUser($bill->user, $key, false); + $bill = $item['bill']; + $key = sprintf('bill_overdue_%s_%s', $bill->id, substr(hash('sha256', json_encode($item['dates']['pay_dates'], JSON_THROW_ON_ERROR)), 0, 10)); + $pref = Preferences::getForUser($bill->user, $key, false); if (true === $pref->data) { Log::debug(sprintf('User #%d has already been warned about overdue subscription #%d.', $bill->user->id, $bill->id)); @@ -63,12 +53,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; } @@ -106,4 +96,5 @@ class BillEventHandler } + } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index cfb4ac1344..52b54cc0ca 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -27,7 +27,6 @@ use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Events\Admin\InvitationCreated; use FireflyIII\Events\DestroyedTransactionGroup; use FireflyIII\Events\DetectedNewIPAddress; -use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions; use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Events\Model\PiggyBank\ChangedName; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; @@ -72,90 +71,90 @@ 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', ], @@ -163,54 +162,54 @@ class EventServiceProvider extends ServiceProvider // SubscriptionNeedsExtensionOrRenewal::class => [ // 'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill', // ], - WarnUserAboutOverdueSubscriptions::class => [ - 'FireflyIII\Handlers\Events\BillEventHandler@warnAboutOverdueSubscriptions', - ], + // 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 => [ + ChangedAmount::class => [ 'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount', ], - ChangedName::class => [ + ChangedName::class => [ 'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName', ], // rule actions - RuleActionFailedOnArray::class => [ + RuleActionFailedOnArray::class => [ 'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray', ], - RuleActionFailedOnObject::class => [ + 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', ], ];