diff --git a/app/interactions/domains/force_delete/post_set_process.rb b/app/interactions/domains/force_delete/post_set_process.rb index 036fd3531..564ef6d8e 100644 --- a/app/interactions/domains/force_delete/post_set_process.rb +++ b/app/interactions/domains/force_delete/post_set_process.rb @@ -12,8 +12,6 @@ module Domains # Allow deletion statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED) domain.skip_whois_record_update = notify_by_email ? true : false - - domain.save(validate: false) end end end diff --git a/app/interactions/domains/force_delete/prepare_domain.rb b/app/interactions/domains/force_delete/prepare_domain.rb index be05ce480..32eae11af 100644 --- a/app/interactions/domains/force_delete/prepare_domain.rb +++ b/app/interactions/domains/force_delete/prepare_domain.rb @@ -9,7 +9,6 @@ module Domains domain.force_delete_domain_statuses_history = domain.statuses domain.statuses |= STATUSES_TO_SET domain.skip_whois_record_update = true - domain.save(validate: false) end end end diff --git a/app/interactions/domains/force_delete/set_force_delete.rb b/app/interactions/domains/force_delete/set_force_delete.rb index f7cde0cc3..fe50c8f99 100644 --- a/app/interactions/domains/force_delete/set_force_delete.rb +++ b/app/interactions/domains/force_delete/set_force_delete.rb @@ -3,12 +3,20 @@ module Domains class SetForceDelete < Base def execute compose(CheckDiscarded, inputs.to_h) - compose(PrepareDomain, inputs.to_h) - compose(SetStatus, inputs.to_h) - compose(PostSetProcess, inputs.to_h) - compose(NotifyRegistrar, inputs.to_h) - compose(NotifyByEmail, inputs.to_h) - compose(NotifyMultiyearsExpirationDomain, inputs.to_h) + + Domain.transaction do + compose(PrepareDomain, inputs.to_h) + compose(SetStatus, inputs.to_h) + compose(PostSetProcess, inputs.to_h) + + # Save the domain once with all accumulated changes + # This will create a single PaperTrail version + domain.save(validate: false) + + compose(NotifyRegistrar, inputs.to_h) + compose(NotifyByEmail, inputs.to_h) + compose(NotifyMultiyearsExpirationDomain, inputs.to_h) + end end end end diff --git a/app/interactions/domains/force_delete/set_status.rb b/app/interactions/domains/force_delete/set_status.rb index 76947dfa2..206f072c5 100644 --- a/app/interactions/domains/force_delete/set_status.rb +++ b/app/interactions/domains/force_delete/set_status.rb @@ -4,13 +4,12 @@ module Domains def execute domain.force_delete_type = type type == :fast_track ? force_delete_fast_track : force_delete_soft - domain.status_notes[DomainStatus::FORCE_DELETE] = "Company no: #{domain.registrant.ident}" if reason == 'invalid_company' + set_status_notes domain.skip_whois_record_update = true domain.force_delete_domain_statuses_history_data = { reason: domain.status_notes[DomainStatus::FORCE_DELETE], date: Time.zone.now } - domain.save(validate: false) end def force_delete_fast_track @@ -27,6 +26,11 @@ module Domains private + def set_status_notes + domain.status_notes[DomainStatus::FORCE_DELETE] = "Company no: #{domain.registrant.ident}" if reason == 'invalid_company' + domain.status_notes[DomainStatus::FORCE_DELETE] = email if reason == 'invalid_email' + end + def soft_forcedelete_dates(years) domain.force_delete_start = domain.valid_to - years.years domain.force_delete_date = domain.force_delete_start + diff --git a/app/interactions/domains/force_delete_email/base.rb b/app/interactions/domains/force_delete_email/base.rb index d75749b50..d105d8531 100644 --- a/app/interactions/domains/force_delete_email/base.rb +++ b/app/interactions/domains/force_delete_email/base.rb @@ -1,38 +1,56 @@ module Domains module ForceDeleteEmail + # Processes domains with invalid emails by flagging them for force deletion + # when email addresses are identified as invalid or bouncing class Base < ActiveInteraction::Base string :email, description: 'Bounced email to set ForceDelete from' def execute - domain_contacts = Contact.where(email: email).map(&:domain_contacts).flatten - registrant_ids = Registrant.where(email: email).pluck(:id) + # Return early if no affected domains or if any domains are on hold + affected_domains = find_affected_domains + return if should_skip_processing?(affected_domains) - domains = domain_contacts.map(&:domain).flatten + - Domain.where(registrant_id: registrant_ids) - - return if expired_or_hold_domains_exists?(domains) - - domains.each do |domain| - next if domain.expired? - - before_execute_force_delete(domain) - end + process_affected_domains(affected_domains) end private - def expired_or_hold_domains_exists?(domains) + def should_skip_processing?(domains) + domains.empty? || domains_on_hold_exist?(domains) + end + + def process_affected_domains(domains) + domains.each do |domain| + next if domain.expired? + + process_domain_for_force_delete(domain) + end + end + + def find_affected_domains + # Find domains through contacts + contact_domains = Contact.where(email: email).flat_map(&:domain_contacts) + .flat_map(&:domain) + + # Find domains through registrants + registrant_domains = Domain.where(registrant_id: Registrant.where(email: email).select(:id)) + + # Combine and remove duplicates + (contact_domains + registrant_domains).uniq + end + + def domains_on_hold_exist?(domains) domains.any? do |domain| domain.statuses.include?(DomainStatus::SERVER_HOLD) && email.include?(domain.name) end end - def before_execute_force_delete(domain) - if domain.force_delete_scheduled? && !domain.status_notes[DomainStatus::FORCE_DELETE].nil? - added_additional_email_into_notes(domain) + def process_domain_for_force_delete(domain) + if domain.force_delete_scheduled? && domain.status_notes[DomainStatus::FORCE_DELETE].present? + add_email_to_notes(domain) else - process_force_delete(domain) + schedule_force_delete(domain) end end @@ -43,32 +61,29 @@ module Domains purge_date: domain.purge_date, email: domain.status_notes[DomainStatus::FORCE_DELETE]) - return if domain.registrar.notifications.last.text.include? template + return if domain.registrar.notifications.last&.text&.include?(template) domain.registrar.notifications.create!(text: template) end - def process_force_delete(domain) - domain.schedule_force_delete(type: :soft, - notify_by_email: true, - reason: 'invalid_email', - email: email) - save_status_note(domain) + def schedule_force_delete(domain) + domain.schedule_force_delete( + type: :soft, + notify_by_email: true, + reason: 'invalid_email', + email: email + ) end - def added_additional_email_into_notes(domain) - return if domain.status_notes[DomainStatus::FORCE_DELETE].include? email + def add_email_to_notes(domain) + return if domain.status_notes[DomainStatus::FORCE_DELETE].include?(email) + # Uncomment if notification is needed # notify_registrar(domain) domain.status_notes[DomainStatus::FORCE_DELETE].concat(" #{email}") domain.save(validate: false) end - - def save_status_note(domain) - domain.status_notes[DomainStatus::FORCE_DELETE] = email - domain.save(validate: false) - end end end end diff --git a/test/interactions/force_delete_email/base_test.rb b/test/interactions/force_delete_email/base_test.rb index b5842850f..01094d284 100644 --- a/test/interactions/force_delete_email/base_test.rb +++ b/test/interactions/force_delete_email/base_test.rb @@ -28,8 +28,8 @@ class BaseTest < ActiveSupport::TestCase prepare_contact contact = @domain_airport.admin_contacts.first - Domains::ForceDeleteEmail::Base.run(email: contact.email) + @domain_airport.reload assert @domain_airport.force_delete_scheduled?