From 9a806a01528b718cb94bf4854098797826ddcb43 Mon Sep 17 00:00:00 2001 From: phil-l-brockwell Date: Wed, 27 Nov 2024 16:54:39 +0000 Subject: [PATCH] Refactor existing Interactors --- .../admin_actions/add_collaborator.rb | 102 +++++++-- .../search_collaborator_candidates.rb | 15 +- app/models/form_answer.rb | 8 + app/models/user.rb | 8 - .../admin_actions/add_collaborator_spec.rb | 197 ++++++++++++++++++ 5 files changed, 295 insertions(+), 35 deletions(-) create mode 100644 spec/interactors/admin_actions/add_collaborator_spec.rb diff --git a/app/interactors/admin_actions/add_collaborator.rb b/app/interactors/admin_actions/add_collaborator.rb index 99a6d3773..ac1a044f0 100644 --- a/app/interactors/admin_actions/add_collaborator.rb +++ b/app/interactors/admin_actions/add_collaborator.rb @@ -1,20 +1,24 @@ module AdminActions class AddCollaborator - attr_reader :form_answer, - :user, - :success, - :errors + attr_reader :account, :role, :transfer_form_answers, :new_owner_id, :collaborator, :success, :errors - def initialize(form_answer, user) - @form_answer = form_answer - @user = user + def initialize(account:, collaborator:, params:, existing_account: collaborator.account) + @account = account + @collaborator = collaborator + @existing_account = existing_account + @role = params.fetch(:role, "regular") + @transfer_form_answers = params.fetch(:transfer_form_answers, false) + @new_owner_id = params.fetch(:new_owner_id, nil) + @errors = [] end def run - if user.can_be_added_to_collaborators_to_another_account? - persist! + if account_will_be_orphaned? + @errors << "User account has active users, ownership of the account must be transferred" + elsif progressed_form_answers_will_be_orphaned? + @errors << "User has applications in progress, and there are no other users on the account to transfer them to" else - @errors = "can't be added as linked with another account!" + persist! end self @@ -24,17 +28,83 @@ def success? @success.present? end + def error_messages + @errors.join(", ") + end + private def persist! - user.role = "regular" - user.account = form_answer.account - - if user.save + ActiveRecord::Base.transaction do + transfer_collaborator! + transfer_ownership! + handle_form_answers! + delete_existing_account! @success = true - else - @errors = user.errors.full_messages.join(", ") end + rescue ActiveRecord::RecordInvalid => e + @errors << e.message + end + + def existing_account_has_other_collaborators? + return unless @existing_account + + @existing_account.collaborators_without(collaborator).any? + end + + def account_will_be_orphaned? + collaborator_is_owner? && existing_account_has_other_collaborators? && new_owner_id.blank? + end + + def collaborator_is_owner? + return unless @existing_account + + @existing_account.owner == collaborator + end + + def progressed_form_answers_will_be_orphaned? + keep_form_answers_on_original_account? && progressed_form_answers? && !existing_account_has_other_collaborators? + end + + def progressed_form_answers? + collaborator.form_answers.any? && collaborator.form_answers.any?(&:any_progress?) + end + + def transfer_form_answers? + transfer_form_answers + end + + def keep_form_answers_on_original_account? + !transfer_form_answers? + end + + def handle_form_answers! + return unless @collaborator.form_answers.any? + + if transfer_form_answers? + @collaborator.form_answers.each { |f| f.update!(account: @account) } + elsif existing_account_has_other_collaborators? + @collaborator.form_answers.each { |f| f.update!(user: @existing_account.owner) } + elsif !progressed_form_answers? + @collaborator.form_answers.each(&:destroy!) + end + end + + def transfer_ownership! + return unless @new_owner_id + + @existing_account.update!(owner_id: @new_owner_id) + end + + def transfer_collaborator! + collaborator.update!(role: role, account: account) + end + + def delete_existing_account! + return unless @existing_account + return if existing_account_has_other_collaborators? + + @existing_account.destroy! end end end diff --git a/app/interactors/admin_actions/search_collaborator_candidates.rb b/app/interactors/admin_actions/search_collaborator_candidates.rb index 766adb27c..4a41816b1 100644 --- a/app/interactors/admin_actions/search_collaborator_candidates.rb +++ b/app/interactors/admin_actions/search_collaborator_candidates.rb @@ -1,17 +1,10 @@ module AdminActions class SearchCollaboratorCandidates - attr_accessor :form_answer, - :account, - :existing_collaborators, - :candidates, - :query, - :error + attr_accessor :account, :existing_collaborators, :candidates, :query, :error - def initialize(form_answer, query = nil) - @query = query[:query] - @form_answer = form_answer - @account = form_answer.account - @existing_collaborators = account.users + def initialize(existing_collaborators:, params: {}) + @query = params[:query] + @existing_collaborators = existing_collaborators end def run diff --git a/app/models/form_answer.rb b/app/models/form_answer.rb index 7347dfd5e..cf62e80a9 100644 --- a/app/models/form_answer.rb +++ b/app/models/form_answer.rb @@ -34,6 +34,8 @@ class FormAnswer < ApplicationRecord }, } + ZERO_PROGRESS = 0.06666666666666667 + POSSIBLE_AWARDS = [ "trade", # International Trade Award "innovation", # Innovation Award @@ -341,6 +343,12 @@ def fill_progress_in_percents ((fill_progress || 0) * 100).floor.to_s + "%" end + def any_progress? + return unless fill_progress + + fill_progress > ZERO_PROGRESS + end + def performance_years case award_type when "innovation" diff --git a/app/models/user.rb b/app/models/user.rb index 2e946b64d..80919aec7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -142,14 +142,6 @@ def reset_password_period_valid? end end - def can_be_added_to_collaborators_to_another_account? - account.blank? || ( - account.present? && - form_answers.blank? && - account.form_answers.blank? - ) - end - def new_member? created_at > 3.days.ago end diff --git a/spec/interactors/admin_actions/add_collaborator_spec.rb b/spec/interactors/admin_actions/add_collaborator_spec.rb new file mode 100644 index 000000000..dba2ec71f --- /dev/null +++ b/spec/interactors/admin_actions/add_collaborator_spec.rb @@ -0,0 +1,197 @@ +require "rails_helper" + +describe AdminActions::AddCollaborator do + describe "#run" do + let(:result) { described_class.new(account:, collaborator:, params:, existing_account:).run } + let(:account) { create(:account) } + let(:collaborator) { create(:user, form_answers: form_answers, account: collaborator_account, role: "regular") } + let(:existing_account) { collaborator.account } + let(:collaborator_account) { create(:account) } + let(:collaborator_account_other_users) { [] } + let(:form_answers) { [] } + let(:params) { { role: role, transfer_form_answers: transfer_form_answers, new_owner_id: new_owner_id } } + let(:role) { "account_admin" } + let(:new_owner_id) { nil } + + context "when transfer_form_answers is true" do + let(:transfer_form_answers) { true } + + context "when the collaborator has form_answers" do + let(:form_answers) { create_list(:form_answer, 3) } + + context "when the collaborator_account has no other users" do + it "transfers the collaborator and form_answers, and the collaborator account is deleted" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect { collaborator_account.reload }.to raise_error(ActiveRecord::RecordNotFound) + form_answers.each { |f| expect(f.account).to eq(account) } + end + end + + context "when the collaborator_account has other users" do + let!(:collaborator_account_other_users) { create_list(:user, 2, account: collaborator_account) } + let(:new_owner_id) { collaborator_account_other_users.first.id } + + it "transfers the collaborator and form_answers, and ownership of the collaborator account is transferred" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect(collaborator_account.owner).to eq(collaborator_account_other_users.first) + expect(collaborator_account.users).not_to include(collaborator) + form_answers.each { |f| expect(f.account).to eq(account) } + end + end + end + end + + context "when transfer_form_answers is false" do + let(:transfer_form_answers) { false } + + context "when the existing_account is nil" do + let(:existing_account) { nil } + + it "transfers the collaborator" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + end + end + + context "when the collaborator does not have form_answers" do + let(:form_answers) { [] } + + it "transfers the collaborator and the collaborator account is deleted" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect { collaborator_account.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + context "when the collaborator is the owner of the account" do + before do + collaborator_account.owner = collaborator + collaborator_account.save + end + + it "transfers the collaborator and the collaborator account is deleted" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect { collaborator_account.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context "when the collaborator_account has other users" do + let!(:collaborator_account_other_users) { create_list(:user, 2, account: collaborator_account) } + + it "transfers the collaborator and the collaborator account is not deleted" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect(collaborator_account.users).to eq(collaborator_account_other_users) + end + + context "when the collaborator is the owner of the account" do + let(:new_owner_id) { collaborator_account_other_users.first.id } + + before do + collaborator_account.owner = collaborator + collaborator_account.save + end + + it "transfers the collaborator and ownership of the collaborator_account" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect(collaborator_account.owner).to eq(collaborator_account_other_users.first) + expect(collaborator_account.users).not_to include(collaborator) + end + + context "when the new_owner_id is not specified" do + let(:new_owner_id) { nil } + + it "is not a success" do + expect(result).not_to be_success + expect(result.error_messages) + .to eq("User account has active users, ownership of the account must be transferred") + expect(collaborator.account).to eq(collaborator_account) + end + end + + context "when an invalid new_owner_id is specified" do + let(:new_owner_id) { 1000 } + + it "is not a success" do + expect(result).not_to be_success + expect(result.error_messages) + .to eq("Validation failed: Owner Owner is empty - it is a required field and must be filled in") + expect(collaborator.reload.account).to eq(collaborator_account) + end + end + end + end + end + context "when the collaborator has form_answers" do + let(:form_answers) { create_list(:form_answer, 3) } + + context "when the collaborator_account has no other users" do + it "transfers the collaborator, and the collaborator account and form_answers are deleted" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect { collaborator_account.reload }.to raise_error(ActiveRecord::RecordNotFound) + form_answers.each { |f| expect { f.reload }.to raise_error(ActiveRecord::RecordNotFound) } + end + end + + context "when the form_answers are in progress" do + let(:form_answers) { create_list(:form_answer, 3, :development) } + + context "when the collaborator_account has other users" do + let!(:collaborator_account_other_users) { create_list(:user, 2, account: collaborator_account) } + + context "when the new_owner_id is specified" do + let(:new_owner_id) { collaborator_account_other_users.first.id } + + it "transfers the collaborator and ownership of the form_answers to the new owner" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect(collaborator_account.owner).to eq(collaborator_account_other_users.first) + form_answers.each { |f| expect(f.user).to eq(collaborator_account_other_users.first) } + expect(collaborator_account.users).not_to include(collaborator) + end + end + + context "when the new_owner_id is not specified" do + let(:new_owner_id) { nil } + + before do + collaborator_account.owner = collaborator_account_other_users.first + collaborator_account.save + end + + it "transfers the collaborator and ownership of the form_answers to the existing owner" do + expect(result).to be_success + expect(collaborator.account).to eq(account) + expect(collaborator.role).to eq(role) + expect(collaborator_account.users).not_to include(collaborator) + form_answers.each { |f| expect(f.user).to eq(collaborator_account_other_users.first) } + end + end + end + + context "when the collaborator_account has no other users" do + it "is not a success" do + expect(result).not_to be_success + expect(result.error_messages) + .to eq("User has applications in progress, and there are no other users on the account to transfer them to") + expect(collaborator.account).to eq(collaborator_account) + end + end + end + end + end + end +end