diff --git a/app/jobs/company_register_status_job.rb b/app/jobs/company_register_status_job.rb index f6add6c034..df39ea56bb 100644 --- a/app/jobs/company_register_status_job.rb +++ b/app/jobs/company_register_status_job.rb @@ -1,29 +1,34 @@ class CompanyRegisterStatusJob < ApplicationJob queue_as :default - def perform(contact, days_interval = 14.days) - return unless contact.org? + def perform(days_interval = 14, spam_time_delay=0.3) + registrants = Registrant.where(ident_type: 'org') + .where( + '(company_register_status IS NULL) OR + (company_register_status = ? AND (checked_company_at IS NULL OR checked_company_at <= ?)) OR + (company_register_status = ? AND (checked_company_at IS NULL OR checked_company_at <= ?))', + Contact::REGISTERED, days_interval.days.ago, + Contact::LIQUIDATED, 1.day.ago + ) - company_status = contact.return_company_status - return if company_status == Contact::REGISTERED || company_status == Contact::LIQUIDATED + registrants.find_in_batches(batch_size: 100) do |contacts| + contacts.each do |contact| - # TODO: - # Need search only registrants!!! - contacts = Contact.where(ident_type: 'org') - .where('checked_company_at IS NULL OR checked_company_at <= ?', days_interval.days.ago) + # avoid spamming company register + sleep spam_time_delay - contact.find_in_batches(batch_size: 100) do |contacts| - contacts.each do |contact| - # TODO: - # put some time interval here, otherwise it will be business registry spam - # check contact company for status - # if status is not registered or liquidated - # then force delete contact + company_status = contact.return_company_status + contact.company_register_status = company_status + contact.checked_company_at = Time.zone.now + contact.save! - # update checked_company_at field + next unless company_status == Contact::BANKRUPT || company_status == Contact::DELETED + + contact.domains.each do |domain| + domain.schedule_force_delete(type: :fast_track, notify_by_email: true, reason: 'invalid_company', email: contact.email) + end end end - end end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 3fa3bf627a..e0f98ea84e 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -32,7 +32,7 @@ def hold_status? def notification_template(explicit: nil) reason = explicit&.downcase - return reason if %w[invalid_email invalid_phone].include?(reason) + return reason if %w[invalid_email invalid_phone invalid_company].include?(reason) if contact_emails_verification_failed.present? 'invalid_email' diff --git a/app/models/contact/company_register.rb b/app/models/contact/company_register.rb index ed81e30237..6115cecc29 100644 --- a/app/models/contact/company_register.rb +++ b/app/models/contact/company_register.rb @@ -10,6 +10,12 @@ module Contact::CompanyRegister REGISTERED = 'registered' LIQUIDATED = 'liquidated' + BANKRUPT = 'bankrupt' + DELETED = 'deleted' + + def company_is_relevant? + company_register_status == REGISTERED && company_register_status == LIQUIDATED + end def return_company_status return unless return_company_data.present? diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_company.html.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_company.html.erb new file mode 100644 index 0000000000..3bb21a1c68 --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_company.html.erb @@ -0,0 +1,49 @@ +
Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt
+ +Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontakti(de) telefoni number või numbrid on puudulikud.
+ +Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.
+ +Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.
+ +Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.
+ +Lisaküsimuste korral võtke palun ühendust oma registripidajaga:
+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.et.html' %> + +Dear registrant/administrative contact of .ee domain,
+ +Estonian Internet Foundation has learned that contact(s) phone number data of the domain <%= @domain.name %> are invalid.
+ +Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.
+ +Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants
+ +If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.
+ +Should you have additional questions, please contact your registrar:
+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.en.html' %> +Уважаемый регистрант/административный контакт домена .ee
+ +Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @domain.name %> неверны - телефонные номера.
+ +Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.
+ +Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов
+ +Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.
+ +В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: +<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>
+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_company.text.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_company.text.erb new file mode 100644 index 0000000000..d2cb87e44a --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_company.text.erb @@ -0,0 +1,49 @@ +Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt
+ +Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontakti(de) telefoni number või numbrid on puudulikud.
+ +Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.
+ +Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.
+ +Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.
+ +Lisaküsimuste korral võtke palun ühendust oma registripidajaga:
+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.et.html' %> + +Dear registrant/administrative contact of .ee domain,
+ +Estonian Internet Foundation has learned that contact(s) phone number data of the domain <%= @domain.name %> are invalid.
+ +Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.
+ +Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants
+ +If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.
+ +Should you have additional questions, please contact your registrar:
+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.en.html' %> +Уважаемый регистрант/административный контакт домена .ee
+ +Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @domain.name %> неверны - телефонные номера.
+ +Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.
+ +Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов
+ +Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.
+ +В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: + <%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>
+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/db/migrate/20230711083811_add_company_register_status_to_contacts.rb b/db/migrate/20230711083811_add_company_register_status_to_contacts.rb new file mode 100644 index 0000000000..615151721e --- /dev/null +++ b/db/migrate/20230711083811_add_company_register_status_to_contacts.rb @@ -0,0 +1,5 @@ +class AddCompanyRegisterStatusToContacts < ActiveRecord::Migration[6.1] + def change + add_column :contacts, :company_register_status, :string + end +end diff --git a/db/structure.sql b/db/structure.sql index 0740fcea18..186ddbb19d 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -686,7 +686,8 @@ CREATE TABLE public.contacts ( disclosed_attributes character varying[] DEFAULT '{}'::character varying[] NOT NULL, email_history character varying, registrant_publishable boolean DEFAULT false, - checked_company_at timestamp without time zone + checked_company_at timestamp without time zone, + company_register_status character varying ); @@ -4713,6 +4714,7 @@ CREATE INDEX index_versions_on_item_type_and_item_id ON public.versions USING bt CREATE INDEX index_whois_records_on_domain_id ON public.whois_records USING btree (domain_id); + -- -- Name: index_whois_records_on_registrar_id; Type: INDEX; Schema: public; Owner: - -- @@ -5466,6 +5468,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230612094319'), ('20230612094326'), ('20230612094335'), -('20230710120154'); +('20230710120154'), +('20230711083811'); diff --git a/test/jobs/company_register_status_job_test.rb b/test/jobs/company_register_status_job_test.rb new file mode 100644 index 0000000000..a31f39ea1e --- /dev/null +++ b/test/jobs/company_register_status_job_test.rb @@ -0,0 +1,295 @@ +require "test_helper" + +Company = Struct.new(:registration_number, :company_name, :status) + +class CompanyRegisterStatusJobTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + + REGISTERED = 'R' + LIQUIDATED = 'L' + BANKRUPT = 'N' + DELETED = 'K' + + setup do + @registrant_acme = contacts(:acme_ltd).becomes(Registrant) + @registrant_jack = contacts(:jack).becomes(Registrant) + @registrant_william = contacts(:william).becomes(Registrant) + + contact = contacts(:john) + @registrant = contact.becomes(Registrant) + end + + def test_contact_who_never_checked_before + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', REGISTERED)] + end + object + end + + @registrant_acme.update!(company_register_status: nil, checked_company_at: nil) + @registrant_jack.update!(company_register_status: nil, checked_company_at: nil) + + @registrant_acme.reload && @registrant_jack.reload + + assert_nil @registrant_acme.checked_company_at + assert_nil @registrant_acme.company_register_status + assert_nil @registrant_jack.checked_company_at + assert_nil @registrant_jack.company_register_status + + CompanyRegisterStatusJob.perform_now + + @registrant_acme.reload && @registrant_jack.reload + + assert_not_nil @registrant_acme.checked_company_at + assert_not_nil @registrant_acme.company_register_status + assert_not_nil @registrant_jack.checked_company_at + assert_not_nil @registrant_jack.company_register_status + + CompanyRegister::Client.define_singleton_method(:new, original_new_method) + end + + def test_contact_who_was_checked_some_days_ago + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', REGISTERED)] + end + object + end + + interval_days = 14 + current_time = Time.zone.now + + @registrant_acme.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - (interval_days.days + 1.day)) + @registrant_jack.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - (interval_days.days + 2.days)) + + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal Contact::REGISTERED, @registrant_acme.company_register_status + assert_equal Contact::REGISTERED, @registrant_jack.company_register_status + assert_equal current_time.to_date, @registrant_acme.checked_company_at.to_date + assert_equal current_time.to_date, @registrant_jack.checked_company_at.to_date + + CompanyRegister::Client.define_singleton_method(:new, original_new_method) + end + + def test_it_should_not_check_contact_what_days_limit_not_reached + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', REGISTERED)] + end + object + end + + interval_days = 14 + current_time = Time.zone.now + + @registrant_acme.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - (interval_days.days - 1.day)) + @registrant_jack.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - (interval_days.days - 2.days)) + + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal (current_time - (interval_days.days - 1.day)).to_date, @registrant_acme.checked_company_at.to_date + assert_equal (current_time - (interval_days.days - 2.days)).to_date, @registrant_jack.checked_company_at.to_date + + CompanyRegister::Client.define_singleton_method(:new, original_new_method) + end + + def test_contacts_who_has_liquidated_company_status + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', LIQUIDATED)] + end + object + end + + interval_days = 5 + current_time = Time.zone.now + + @registrant_acme.update!(company_register_status: Contact::LIQUIDATED, checked_company_at: current_time - interval_days.days) + @registrant_jack.update!(company_register_status: Contact::LIQUIDATED, checked_company_at: current_time - (interval_days.days + 2.days)) + + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal Contact::LIQUIDATED, @registrant_acme.company_register_status + assert_equal Contact::LIQUIDATED, @registrant_jack.company_register_status + + assert_equal current_time.to_date, @registrant_acme.checked_company_at.to_date + assert_equal current_time.to_date, @registrant_jack.checked_company_at.to_date + + CompanyRegister::Client.define_singleton_method(:new, original_new_method) + end + + def test_liquided_and_registered_companies + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', LIQUIDATED)] + end + object + end + + interval_days = 5 + current_time = Time.zone.now + + @registrant_acme.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - interval_days.days) + @registrant_jack.update!(company_register_status: Contact::LIQUIDATED, checked_company_at: current_time - 2.days) + + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal Contact::LIQUIDATED, @registrant_acme.company_register_status + assert_equal Contact::LIQUIDATED, @registrant_jack.company_register_status + + assert_equal current_time.to_date, @registrant_acme.checked_company_at.to_date + assert_equal current_time.to_date, @registrant_jack.checked_company_at.to_date + + CompanyRegister::Client.define_singleton_method(:new, original_new_method) + end + + def test_put_force_delete_for_bankroupted_companies + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', BANKRUPT)] + end + object + end + + interval_days = 5 + current_time = Time.zone.now + + refute @registrant_acme.domains.any?(&:force_delete_scheduled?) + + @registrant_acme.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - interval_days.days) + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal Contact::BANKRUPT, @registrant_acme.company_register_status + assert_equal current_time.to_date, @registrant_acme.checked_company_at.to_date + + assert @registrant_acme.domains.all?(&:force_delete_scheduled?) + end + + def test_puts_force_delete_for_deleted_companies + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', DELETED)] + end + object + end + + interval_days = 5 + current_time = Time.zone.now + + refute @registrant_acme.domains.any?(&:force_delete_scheduled?) + + @registrant_acme.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - interval_days.days) + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal Contact::DELETED, @registrant_acme.company_register_status + assert_equal current_time.to_date, @registrant_acme.checked_company_at.to_date + + assert @registrant_acme.domains.all?(&:force_delete_scheduled?) + end + + def test_should_inform_contact_by_email_if_force_delete_has_been_set + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', DELETED)] + end + object + end + + ActionMailer::Base.deliveries.clear + assert_emails 0 + + interval_days = 5 + current_time = Time.zone.now + + refute @registrant_acme.domains.any?(&:force_delete_scheduled?) + + @registrant_acme.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - interval_days.days) + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal Contact::DELETED, @registrant_acme.company_register_status + assert_equal current_time.to_date, @registrant_acme.checked_company_at.to_date + + assert @registrant_acme.domains.all?(&:force_delete_scheduled?) + + assert_emails 4 + end + + def test_should_inform_contact_by_poll_message_if_force_delete_has_been_set + original_new_method = CompanyRegister::Client.method(:new) + CompanyRegister::Client.define_singleton_method(:new) do + object = original_new_method.call + def object.company_details(registration_number:) + [Company.new('1234567', 'ACME Ltd', DELETED)] + end + object + end + + @registrant_acme.registrar.notifications.destroy_all && @registrant_acme.reload + assert_equal @registrant_acme.registrar.notifications.count, 0 + + interval_days = 5 + current_time = Time.zone.now + + refute @registrant_acme.domains.any?(&:force_delete_scheduled?) + + @registrant_acme.update!(company_register_status: Contact::REGISTERED, checked_company_at: current_time - interval_days.days) + @registrant_acme.reload && @registrant_jack.reload + + CompanyRegisterStatusJob.perform_now(interval_days, 0) + + @registrant_acme.reload && @registrant_jack.reload + + assert_equal Contact::DELETED, @registrant_acme.company_register_status + assert_equal current_time.to_date, @registrant_acme.checked_company_at.to_date + + assert @registrant_acme.domains.all?(&:force_delete_scheduled?) + + assert_equal @registrant_acme.registrar.notifications.count, 2 + end +end \ No newline at end of file