Files
fizzy/app/models/notification/bundle.rb
T
Stanko Krtalić eceb6c9b43 Add self-service account deletion (#2246)
* Add self-service account deletion

* Disable access to cancelled accounts & implement Stripe interactions

* Add tests

* Fix failing tests

* Remove cancelled accounts from lists

* Fix incorrect redirect

* Use _path instead of _url for consistency

* Don't track how far the inicieration job got

We still want the step tracking so that the job can be interrupted. But, since the scope is idempotent, we don't need to track how far it got.

* Specify the exact time when the data will be deleted

* Fix crash due to unadvancable cursor

* Rename up_for_incineration to due_for_incineration

* Regenrate the schema

* Fix incorrect path check

* Migrate the SQLite schema

* Only show the cancel button on cancellable accounts

* Check that a subscirption method exists before calling it

* Ignore job failures due to missing records when an account gets deleted

* Skip sending notifications on cancelled accounts

* Use collbacks to integrate

* Add a blank line between queue_as and discard_on

* Break checks into methods

* Inline methods

* Rename Account::IncinerateJob

* Run migrations

* Migrate SQLite
2026-01-12 14:21:05 +01:00

84 lines
2.1 KiB
Ruby

class Notification::Bundle < ApplicationRecord
belongs_to :account, default: -> { user.account }
belongs_to :user
enum :status, %i[ pending processing delivered ]
scope :due, -> { pending.where("ends_at <= ?", Time.current) }
scope :containing, ->(notification) { where("starts_at <= ? AND ends_at > ?", notification.created_at, notification.created_at) }
scope :overlapping_with, ->(other_bundle) do
where(
"(starts_at <= ? AND ends_at >= ?) OR (starts_at <= ? AND ends_at >= ?) OR (starts_at >= ? AND ends_at <= ?)",
other_bundle.starts_at, other_bundle.starts_at,
other_bundle.ends_at, other_bundle.ends_at,
other_bundle.starts_at, other_bundle.ends_at
)
end
before_validation :set_default_window, if: :new_record?
validate :validate_no_overlapping
class << self
def deliver_all
due.in_batches do |batch|
jobs = batch.collect { DeliverJob.new(it) }
ActiveJob.perform_all_later jobs
end
end
def deliver_all_later
DeliverAllJob.perform_later
end
end
def notifications
user.notifications.where(created_at: window).unread
end
def deliver
user.in_time_zone do
Current.with_account(user.account) do
processing!
Notification::BundleMailer.notification(self).deliver if deliverable?
delivered!
end
end
end
def deliver_later
DeliverJob.perform_later(self)
end
def flush
update!(ends_at: Time.current)
deliver_later
end
def set_default_window
self.starts_at ||= Time.current
self.ends_at ||= self.starts_at + user.settings.bundle_aggregation_period
end
private
def window
starts_at..ends_at
end
def validate_no_overlapping
if overlapping_bundles.exists?
errors.add(:base, "Bundle window overlaps with an existing pending bundle with id #{overlapping_bundles.first.id}")
end
end
def deliverable?
user.settings.bundling_emails? && notifications.any? && account.active?
end
def overlapping_bundles
user.notification_bundles.where.not(id: id).overlapping_with(self)
end
end