From e69ed945131af1a41d9e0ecdf4c44afe01e4b4c5 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:09:38 +0100 Subject: [PATCH] CLDC-3382 Add support user functionality to merge organisations (#2566) * CLDC-2093 Merge organisations page - view all merge requests (#2561) * CLDC-3585 Update absorbing organisation question (#2564) * Update user permissions and absorbing org question order * Update absorbing organisation question and routing * CLDC 2094: View a merge request (#2565) * CLDC-3584 Update merging orgs question (#2567) * Update merging organisations question * Update rebase tests * Update routing between CYA and questions * CLDC-3586 Update merge date merge request question (#2571) * Remove unused paths and update merge date * lint * Create delete merge request functionality (#2568) * CLDC-3588: Add helpdesk ticket question (#2572) * Merge request fixes (#2578) * Add cancel button to merging orgs and update merging orgs selection * Update submit button text * Update back buttons * CLDC-2101 Add begin merge (#2575) * Calculate merge request status * Add start merge and merge request job * Update merge request when it gets processed * Refactor and display a banner for failed requests * update test * Update change links * Update copy for error message (#2583) * Do not display cancel button if there's no submit (#2584) * Update hint date (#2588) * CLDC-2100++ Add success notification after delete (#2589) * Update helpdesk_ticket.html.erb * CLDC-2094++ Merge details page adjustments (#2593) * Change page name and hide buttons during processing status * Update test * Make merge request status dynamic (#2592) * Make merge request status dynamic * Update tests * Keep check answers referrer when validation is hit (#2594) * Keep check answers referrer when validation is hit * Remove absorbing org from merging organisations list * Update text to handle singular merge requests * Add merge start confirmation page (#2601) * Set merge request as non processing if it fails (#2598) * Set merge request to no longer processing if it fails * clear last_failed_attempt as soon as we start processing the merge * CLDC-3603 View merged users outcomes (#2602) * Update user outcomes view link * Add user outcomes page * Set user outcomes before merge and on merge fail * Update hardcoded total user count * Account for singular user numbers * CLDC-3603 View scheme outcomes (#2604) * Update scheme outcomes view link * Add scheme outcomes page * Update scheme outcome before merge and on merge fail * Update back links in merge outcomes pages (#2611) * CLDC-3605 View relationships page (#2606) * CLDC-3609 Add existing absorbing organisation merge request question (#2600) * Add existing absorbing organisation page and field * Update status calculation and add new question to check answers * Call correct merge service flow * Update test * Update test * Return correct status when existing_absorbing_organisation is false (#2617) * Change styling of relationship outcomes page (#2619) * CLDC-3604 View logs outcomes (#2618) * Update view logs outcomes link * Add logs outcomes page * Set logs outcome before merge and on merge fail * Update logs outcomes text * Update line break * Fix incorrect filtering of relationships and count * Fix test * Fix test * Fix test * Make example date depend on collection year (#2620) --------- Co-authored-by: Manny Dinssa <44172848+Dinssa@users.noreply.github.com> --- app/controllers/merge_requests_controller.rb | 201 +++-- app/controllers/organisations_controller.rb | 4 + app/frontend/controllers/index.js | 4 + app/frontend/controllers/tabs_controller.js | 35 + app/helpers/merge_requests_helper.rb | 279 ++++++ app/helpers/tag_helper.rb | 8 + app/jobs/process_merge_request_job.rb | 15 + app/models/merge_request.rb | 178 +++- app/models/merge_request_organisation.rb | 13 +- app/models/organisation.rb | 8 + app/views/layouts/application.html.erb | 3 +- .../merge_requests/_details_list.html.erb | 33 + .../_merge_request_list.html.erb | 46 + .../_notification_banners.html.erb | 21 + .../merge_requests/_summary_card.html.erb | 8 + .../absorbing_organisation.html.erb | 40 +- .../confirm_telephone_number.html.erb | 41 - .../delete_confirmation.html.erb | 23 + .../existing_absorbing_organisation.html.erb | 27 + .../merge_requests/helpdesk_ticket.html.erb | 27 + .../merge_requests/logs_outcomes.html.erb | 27 + app/views/merge_requests/merge_date.html.erb | 24 +- .../merge_request.html.erb | 0 .../merge_start_confirmation.html.erb | 27 + .../merging_organisations.html.erb | 50 ++ .../new_organisation_address.html.erb | 33 - .../new_organisation_name.html.erb | 19 - ...new_organisation_telephone_number.html.erb | 20 - .../new_organisation_type.html.erb | 5 - .../merge_requests/organisations.html.erb | 51 -- .../relationship_outcomes.html.erb | 25 + .../merge_requests/scheme_outcomes.html.erb | 23 + app/views/merge_requests/show.html.erb | 26 + .../merge_requests/user_outcomes.html.erb | 23 + ...ated_organisation_select_question.html.erb | 2 +- .../add_managing_agent.html.erb | 1 + .../add_stock_owner.html.erb | 1 + app/views/organisations/index.html.erb | 25 +- config/locales/en.yml | 17 +- config/routes.rb | 21 +- ...134014_add_merge_date_to_merge_requests.rb | 5 + ...add_additional_fields_to_merge_requests.rb | 16 + ...13072041_remove_other_merging_org_field.rb | 5 + ...119_remove_new_org_merge_request_fields.rb | 27 + .../20240814083017_add_last_failed_attempt.rb | 5 + ..._update_merge_request_fields_for_status.rb | 17 + ...d_existing_absorbing_organisation_field.rb | 5 + db/schema.rb | 27 +- spec/factories/merge_request.rb | 8 + spec/factories/merge_request_organisation.rb | 6 + spec/features/schemes_spec.rb | 2 + spec/helpers/merge_requests_helper_spec.rb | 272 ++++++ spec/jobs/process_merge_request_job_spec.rb | 65 ++ spec/models/merge_request_spec.rb | 451 ++++++++++ spec/requests/merge_request_spec.rb | 28 + .../merge_requests_controller_spec.rb | 815 ++++++++++-------- .../requests/organisations_controller_spec.rb | 13 + .../merge_requests/show.html.erb_spec.rb | 93 ++ 58 files changed, 2573 insertions(+), 721 deletions(-) create mode 100644 app/frontend/controllers/tabs_controller.js create mode 100644 app/helpers/merge_requests_helper.rb create mode 100644 app/jobs/process_merge_request_job.rb create mode 100644 app/views/merge_requests/_details_list.html.erb create mode 100644 app/views/merge_requests/_merge_request_list.html.erb create mode 100644 app/views/merge_requests/_notification_banners.html.erb create mode 100644 app/views/merge_requests/_summary_card.html.erb delete mode 100644 app/views/merge_requests/confirm_telephone_number.html.erb create mode 100644 app/views/merge_requests/delete_confirmation.html.erb create mode 100644 app/views/merge_requests/existing_absorbing_organisation.html.erb create mode 100644 app/views/merge_requests/helpdesk_ticket.html.erb create mode 100644 app/views/merge_requests/logs_outcomes.html.erb rename app/views/{organisations => merge_requests}/merge_request.html.erb (100%) create mode 100644 app/views/merge_requests/merge_start_confirmation.html.erb create mode 100644 app/views/merge_requests/merging_organisations.html.erb delete mode 100644 app/views/merge_requests/new_organisation_address.html.erb delete mode 100644 app/views/merge_requests/new_organisation_name.html.erb delete mode 100644 app/views/merge_requests/new_organisation_telephone_number.html.erb delete mode 100644 app/views/merge_requests/new_organisation_type.html.erb delete mode 100644 app/views/merge_requests/organisations.html.erb create mode 100644 app/views/merge_requests/relationship_outcomes.html.erb create mode 100644 app/views/merge_requests/scheme_outcomes.html.erb create mode 100644 app/views/merge_requests/show.html.erb create mode 100644 app/views/merge_requests/user_outcomes.html.erb create mode 100644 db/migrate/20240808134014_add_merge_date_to_merge_requests.rb create mode 100644 db/migrate/20240809154241_add_additional_fields_to_merge_requests.rb create mode 100644 db/migrate/20240813072041_remove_other_merging_org_field.rb create mode 100644 db/migrate/20240813112119_remove_new_org_merge_request_fields.rb create mode 100644 db/migrate/20240814083017_add_last_failed_attempt.rb create mode 100644 db/migrate/20240819100411_update_merge_request_fields_for_status.rb create mode 100644 db/migrate/20240822080228_add_existing_absorbing_organisation_field.rb create mode 100644 spec/factories/merge_request.rb create mode 100644 spec/factories/merge_request_organisation.rb create mode 100644 spec/helpers/merge_requests_helper_spec.rb create mode 100644 spec/jobs/process_merge_request_job_spec.rb create mode 100644 spec/models/merge_request_spec.rb create mode 100644 spec/requests/merge_request_spec.rb create mode 100644 spec/views/merge_requests/show.html.erb_spec.rb diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index d717f6ee7..a21d42bbb 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -1,66 +1,77 @@ class MergeRequestsController < ApplicationController - before_action :find_resource, only: %i[ - update - organisations - update_organisations - remove_merging_organisation - absorbing_organisation - confirm_telephone_number - new_organisation_name - new_organisation_address - new_organisation_telephone_number - new_organisation_type - merge_date - ] + before_action :find_resource, exclude: %i[create new] before_action :authenticate_user! - before_action :authenticate_scope!, except: [:create] + before_action :authenticate_scope! + before_action :set_organisations_answer_options, only: %i[merging_organisations absorbing_organisation update_merging_organisations remove_merging_organisation update] def absorbing_organisation; end - def confirm_telephone_number; end - def new_organisation_name; end - def new_organisation_address; end - def new_organisation_telephone_number; end - def new_organisation_type; end def merge_date; end + def existing_absorbing_organisation; end + def helpdesk_ticket; end + def merge_start_confirmation; end + def user_outcomes; end + def relationship_outcomes; end + def scheme_outcomes; end + def logs_outcomes; end def create ActiveRecord::Base.transaction do - @merge_request = MergeRequest.create!(merge_request_params.merge(status: :unsubmitted)) - MergeRequestOrganisation.create!({ merge_request: @merge_request, merging_organisation: @merge_request.requesting_organisation }) + @merge_request = MergeRequest.create!(merge_request_params.merge(status: :incomplete, requester: current_user)) end - redirect_to organisations_merge_request_path(@merge_request) + redirect_to absorbing_organisation_merge_request_path(@merge_request) rescue ActiveRecord::RecordInvalid render_not_found end - def organisations - @answer_options = organisations_answer_options - end - def update validate_response if @merge_request.errors.blank? && @merge_request.update(merge_request_params) + add_merging_organsations if page == "merging_organisations" + remove_absorbing_org_from_merging_organisations if page == "absorbing_organisation" && @merge_request.absorbing_organisation_id.present? + redirect_to next_page_path else render previous_template, status: :unprocessable_entity end end - def update_organisations + def update_merging_organisations + @new_merging_org_ids = params["merge_request"]["new_merging_org_ids"].split(" ") merge_request_organisation = MergeRequestOrganisation.new(merge_request_organisation_params) - @answer_options = organisations_answer_options - if merge_request_organisation.save - render :organisations + if merge_request_organisation.valid? + @new_merging_org_ids.push(merge_request_organisation_params[:merging_organisation_id]) + render :merging_organisations else - render :organisations, status: :unprocessable_entity + render :merging_organisations, status: :unprocessable_entity end end def remove_merging_organisation + @new_merging_org_ids = params["merge_request"]["new_merging_org_ids"] || [] + org_id_to_remove = merge_request_organisation_params[:merging_organisation_id] + @new_merging_org_ids.delete(org_id_to_remove) MergeRequestOrganisation.find_by(merge_request_organisation_params)&.destroy! - @answer_options = organisations_answer_options - render :organisations + render :merging_organisations + end + + def delete + @merge_request.discard! + flash[:notice] = "The merge request has been deleted." + redirect_to organisations_path(tab: "merge-requests") + end + + def merging_organisations + @new_merging_org_ids = [] + end + + def start_merge + if @merge_request.status == "ready_to_merge" + @merge_request.start_merge! + ProcessMergeRequestJob.perform_later(merge_request: @merge_request) + end + + redirect_to merge_request_path(@merge_request) end private @@ -70,23 +81,19 @@ private end def next_page_path + return merge_request_path if is_referrer_type?("check_answers") + case page when "absorbing_organisation" - if create_new_organisation? - new_organisation_name_merge_request_path(@merge_request) - else - confirm_telephone_number_merge_request_path(@merge_request) - end - when "organisations" - absorbing_organisation_merge_request_path(@merge_request) - when "confirm_telephone_number" + merging_organisations_merge_request_path(@merge_request) + when "merging_organisations" merge_date_merge_request_path(@merge_request) - when "new_organisation_name" - new_organisation_address_merge_request_path(@merge_request) - when "new_organisation_address" - new_organisation_telephone_number_merge_request_path(@merge_request) - when "new_organisation_telephone_number" - new_organisation_type_merge_request_path(@merge_request) + when "merge_date" + existing_absorbing_organisation_merge_request_path(@merge_request) + when "existing_absorbing_organisation" + helpdesk_ticket_merge_request_path(@merge_request) + when "helpdesk_ticket" + merge_request_path(@merge_request) end end @@ -94,50 +101,29 @@ private page end - def create_new_organisation? - params.dig(:merge_request, :absorbing_organisation_id) == "other" - end - - def organisations_answer_options + def set_organisations_answer_options answer_options = { "" => "Select an option" } - Organisation.all.pluck(:id, :name).each do |organisation| - answer_options[organisation[0]] = organisation[1] + if current_user.support? + Organisation.all.pluck(:id, :name).each do |organisation| + answer_options[organisation[0]] = organisation[1] + end end - answer_options + + @answer_options = answer_options end def merge_request_params merge_params = params.fetch(:merge_request, {}).permit( :requesting_organisation_id, - :other_merging_organisations, + :helpdesk_ticket, :status, :absorbing_organisation_id, - :telephone_number_correct, - :new_telephone_number, - :new_organisation_name, - :new_organisation_address_line1, - :new_organisation_address_line2, - :new_organisation_postcode, - :new_organisation_telephone_number, + :merge_date, + :existing_absorbing_organisation, ) - if merge_params[:requesting_organisation_id].present? && (current_user.data_coordinator? || current_user.data_provider?) - merge_params[:requesting_organisation_id] = current_user.organisation.id - end - - if merge_params[:absorbing_organisation_id].present? - if create_new_organisation? - merge_params[:new_absorbing_organisation] = true - merge_params[:absorbing_organisation_id] = nil - else - merge_params[:new_absorbing_organisation] = false - end - end - - if merge_params[:telephone_number_correct] == "true" - merge_params[:new_telephone_number] = nil - end + merge_params[:requesting_organisation_id] = current_user.organisation.id merge_params end @@ -145,18 +131,25 @@ private def validate_response case page when "absorbing_organisation" - if merge_request_params[:absorbing_organisation_id].blank? && merge_request_params[:new_absorbing_organisation].blank? + if merge_request_params[:absorbing_organisation_id].blank? @merge_request.errors.add(:absorbing_organisation_id, :blank) end - when "confirm_telephone_number" - if merge_request_params[:telephone_number_correct].blank? - @merge_request.errors.add(:telephone_number_correct, :blank) if @merge_request.absorbing_organisation.phone.present? - @merge_request.errors.add(:new_telephone_number, :blank) if @merge_request.absorbing_organisation.phone.blank? + when "merge_date" + day = merge_request_params["merge_date(3i)"] + month = merge_request_params["merge_date(2i)"] + year = merge_request_params["merge_date(1i)"] + + return @merge_request.errors.add(:merge_date, :blank) if [day, month, year].all?(&:blank?) + + if [day, month, year].none?(&:blank?) && Date.valid_date?(year.to_i, month.to_i, day.to_i) + merge_request_params["merge_date"] = Time.zone.local(year.to_i, month.to_i, day.to_i) + else + @merge_request.errors.add(:merge_date, :invalid) + end + when "existing_absorbing_organisation" + if merge_request_params[:existing_absorbing_organisation].nil? + @merge_request.errors.add(:existing_absorbing_organisation, :blank) end - when "new_organisation_name" - @merge_request.errors.add(:new_organisation_name, :blank) if merge_request_params[:new_organisation_name].blank? - when "new_organisation_telephone_number" - @merge_request.errors.add(:new_organisation_telephone_number, :blank) if merge_request_params[:new_organisation_telephone_number].blank? end end @@ -168,12 +161,42 @@ private end def find_resource + return if params[:id].blank? + @merge_request = MergeRequest.find(params[:id]) end def authenticate_scope! - if current_user.organisation != @merge_request.requesting_organisation && !current_user.support? + unless current_user.support? render_not_found end end + + def is_referrer_type?(referrer_type) + from_referrer_query("referrer") == referrer_type + end + + def from_referrer_query(query_param) + referrer = request.headers["HTTP_REFERER"] + return unless referrer + + query_params = URI.parse(referrer).query + return unless query_params + + parsed_params = CGI.parse(query_params) + parsed_params[query_param]&.first + end + + def add_merging_organsations + new_merging_org_ids = params["merge_request"]["new_merging_org_ids"].split(" ") + new_merging_org_ids.each do |org_id| + MergeRequestOrganisation.create!(merge_request: @merge_request, merging_organisation_id: org_id) + end + end + + def remove_absorbing_org_from_merging_organisations + if @merge_request.merge_request_organisations.where(merging_organisation_id: @merge_request.absorbing_organisation_id).exists? + MergeRequestOrganisation.find_by(merge_request: @merge_request, merging_organisation_id: @merge_request.absorbing_organisation_id).destroy! + end + end end diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 9d3e63b33..044bab74d 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -16,6 +16,9 @@ class OrganisationsController < ApplicationController all_organisations = Organisation.order(:name) @pagy, @organisations = pagy(filtered_collection(all_organisations.visible, search_term)) + @merge_requests = MergeRequest.visible + .joins("LEFT JOIN organisations ON organisations.id = merge_requests.absorbing_organisation_id") + .order("organisations.name, merge_requests.merge_date DESC NULLS LAST, merge_requests.id") @searched = search_term.presence @total_count = all_organisations.visible.size end @@ -235,6 +238,7 @@ class OrganisationsController < ApplicationController def merge_request @merge_request = MergeRequest.new + render "merge_requests/merge_request" end def data_sharing_agreement diff --git a/app/frontend/controllers/index.js b/app/frontend/controllers/index.js index ef29b99ca..944e32e2d 100644 --- a/app/frontend/controllers/index.js +++ b/app/frontend/controllers/index.js @@ -16,6 +16,9 @@ import NumericQuestionController from './numeric_question_controller.js' import SearchController from './search_controller.js' import FilterLayoutController from './filter_layout_controller.js' + +import TabsController from './tabs_controller.js' + application.register('accessible-autocomplete', AccessibleAutocompleteController) application.register('conditional-filter', ConditionalFilterController) application.register('conditional-question', ConditionalQuestionController) @@ -23,3 +26,4 @@ application.register('govukfrontend', GovukfrontendController) application.register('numeric-question', NumericQuestionController) application.register('filter-layout', FilterLayoutController) application.register('search', SearchController) +application.register('tabs', TabsController) diff --git a/app/frontend/controllers/tabs_controller.js b/app/frontend/controllers/tabs_controller.js new file mode 100644 index 000000000..7cfd65bbb --- /dev/null +++ b/app/frontend/controllers/tabs_controller.js @@ -0,0 +1,35 @@ +document.addEventListener('DOMContentLoaded', function () { + const urlParams = new URLSearchParams(window.location.search) + let tab = urlParams.get('tab') + + if (!tab && window.location.hash) { + tab = window.location.hash.substring(1) + urlParams.set('tab', tab) + window.history.replaceState(null, null, `${window.location.pathname}?${urlParams.toString()}`) + } + function activateTab (tabId) { + const tabElement = document.getElementById(tabId) + if (tabElement) { + tabElement.click() + } + window.history.replaceState(null, null, `${window.location.pathname}?${urlParams.toString()}`) + } + + function handleTabClick (event) { + event.preventDefault() + const targetId = this.getAttribute('href').substring(1) + activateTab(targetId) + urlParams.set('tab', targetId) + window.history.replaceState(null, null, `${window.location.pathname}?${urlParams.toString()}`) + } + + if (tab) { + activateTab(`tab_${tab}`) + } + + window.scrollTo(0, 0) + + document.querySelectorAll('.govuk-tabs__tab').forEach(tabElement => { + tabElement.addEventListener('click', handleTabClick) + }) +}) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb new file mode 100644 index 000000000..a342ca808 --- /dev/null +++ b/app/helpers/merge_requests_helper.rb @@ -0,0 +1,279 @@ +module MergeRequestsHelper + include GovukLinkHelper + include GovukVisuallyHiddenHelper + + def display_value_or_placeholder(value, placeholder = "You didn't answer this question") + value.presence || content_tag(:span, placeholder, class: "app-!-colour-muted") + end + + def request_details(merge_request) + [ + { label: "Requester", value: display_value_or_placeholder(merge_request.requester&.name) }, + { label: "Helpdesk ticket", value: merge_request.helpdesk_ticket.present? ? link_to("#{merge_request.helpdesk_ticket} (opens in a new tab)", "https://dluhcdigital.atlassian.net/browse/#{merge_request.helpdesk_ticket}", target: "_blank", rel: "noopener noreferrer") : display_value_or_placeholder(nil), action: merge_request_action(merge_request, "helpdesk_ticket") }, + { label: "Status", value: status_tag(merge_request.status) }, + ] + end + + def merge_details(merge_request) + [ + { label: "Absorbing organisation", value: display_value_or_placeholder(merge_request.absorbing_organisation_name), action: merge_request_action(merge_request, "absorbing_organisation") }, + { label: "Merging organisations", value: merge_request.merge_request_organisations.any? ? merge_request.merge_request_organisations.map(&:merging_organisation_name).join("
").html_safe : display_value_or_placeholder(nil), action: merge_request_action(merge_request, "merging_organisations") }, + { label: "Merge date", value: display_value_or_placeholder(merge_request.merge_date), action: merge_request_action(merge_request, "merge_date") }, + { label: "Absorbing organisation already active?", value: display_value_or_placeholder(merge_request.existing_absorbing_organisation_label), action: merge_request_action(merge_request, "existing_absorbing_organisation") }, + ] + end + + def merge_outcomes(merge_request) + [ + { label: "Total users after merge", value: display_value_or_placeholder(merge_request.total_users_label), action: merge_outcome_action(merge_request, "user_outcomes") }, + { label: "Total schemes after merge", value: display_value_or_placeholder(merge_request.total_schemes_label), action: merge_outcome_action(merge_request, "scheme_outcomes") }, + { label: "Total logs after merge", value: display_value_or_placeholder(merge_request.total_logs_label), action: merge_outcome_action(merge_request, "logs_outcomes") }, + { label: "Total stock owners & managing agents after merge", value: display_value_or_placeholder(merge_request.total_stock_owners_managing_agents_label), action: merge_outcome_action(merge_request, "relationship_outcomes") }, + ] + end + + def ordered_merging_organisations(merge_request, new_merging_org_ids) + Organisation.where(id: new_merging_org_ids) + merge_request.merge_request_organisations.order(created_at: :desc).map(&:merging_organisation) + end + + def submit_merge_request_button_text(referrer) + if accessed_from_check_answers?(referrer) + "Save changes" + else + "Save and continue" + end + end + + def secondary_merge_request_link_text(referrer, skip_for_now: false) + if accessed_from_check_answers?(referrer) + "Cancel" + elsif skip_for_now + "Skip for now" + else + "" + end + end + + def accessed_from_check_answers?(referrer) + %w[check_answers].include?(referrer) + end + + def merge_request_back_link(merge_request, page, referrer) + return merge_request_path(merge_request) if accessed_from_check_answers?(referrer) + + case page + when "absorbing_organisation" + organisations_path(tab: "merge-requests") + when "merging_organisations" + absorbing_organisation_merge_request_path(merge_request) + when "merge_date" + merging_organisations_merge_request_path(merge_request) + when "existing_absorbing_organisation" + merge_date_merge_request_path(merge_request) + when "helpdesk_ticket" + existing_absorbing_organisation_merge_request_path(merge_request) + end + end + + def merge_request_action(merge_request, page) + unless merge_request.status == "request_merged" || merge_request.status == "processing" + { text: "Change", href: send("#{page}_merge_request_path", merge_request, referrer: "check_answers"), visually_hidden_text: page.humanize } + end + end + + def merge_outcome_action(merge_request, page) + unless merge_request.status == "request_merged" || merge_request.status == "processing" + { text: "View", href: send("#{page}_merge_request_path", merge_request), visually_hidden_text: page.humanize } + end + end + + def submit_merge_request_url(referrer) + referrer == "check_answers" ? merge_request_path(referrer: "check_answers") : merge_request_path + end + + def merging_organisations_without_users_text(organisations) + return "" unless organisations.count.positive? + + if organisations.count == 1 + "#{organisations.first.name} has no users." + else + "#{organisations.map(&:name).to_sentence} have no users." + end + end + + def link_to_merging_organisation_users(organisation) + count_text = organisation.users.count == 1 ? "1 #{organisation.name} user" : "all #{organisation.users.count} #{organisation.name} users" + govuk_link_to "View #{count_text} (opens in a new tab)", users_organisation_path(organisation), target: "_blank" + end + + def total_users_after_merge_text(merge_request) + count = merge_request.total_visible_users_after_merge + "#{"#{count} user".pluralize(count)} after merge" + end + + def total_stock_owners_after_merge_text(merge_request) + count = merge_request.total_stock_owners_after_merge + "#{"#{count} stock owner".pluralize(count)} after merge" + end + + def total_managing_agents_after_merge_text(merge_request) + count = merge_request.total_managing_agents_after_merge + "#{"#{count} managing agent".pluralize(count)} after merge" + end + + def related_organisations(merge_request, relationship_type) + organisations = merge_request.absorbing_organisation.send(relationship_type.pluralize).visible + merge_request.merging_organisations.flat_map { |org| org.send(relationship_type.pluralize).visible } + organisations += [merge_request.absorbing_organisation] + merge_request.merging_organisations + organisations.group_by { |relationship| relationship }.select { |_, occurrences| occurrences.size > 1 }.keys + end + + def related_organisations_text(merge_request, relationship_type) + if related_organisations(merge_request, relationship_type).any? + "Some of the organisations merging have common #{relationship_type.humanize(capitalize: false).pluralize}.

" + else + "" + end + end + + def organisations_without_relationships(merge_request, relationship_type) + ([merge_request.absorbing_organisation] + merge_request.merging_organisations).select { |org| org.send(relationship_type.pluralize).visible.empty? } + end + + def organisations_without_relationships_text(organisations_without_relationships, relationship_type) + return "" unless organisations_without_relationships.any? + + org_names = organisations_without_relationships.map(&:name).to_sentence + verb = organisations_without_relationships.count > 1 ? "have" : "has" + "#{org_names} #{verb} no #{relationship_type.humanize(capitalize: false).pluralize}.

" + end + + def generate_organisation_link_text(organisation_count, org, relationship_type) + "View #{organisation_count == 1 ? 'the' : 'all'} #{organisation_count} #{org.name} #{relationship_type.humanize(capitalize: false).pluralize(organisation_count)} (opens in a new tab)" + end + + def relationship_text(merge_request, relationship_type, organisation_path_helper) + text = "" + organisations_without_relationships = organisations_without_relationships(merge_request, relationship_type) + + text += related_organisations_text(merge_request, relationship_type) + text += organisations_without_relationships_text(organisations_without_relationships, relationship_type) + + ([merge_request.absorbing_organisation] + merge_request.merging_organisations).each do |org| + organisation_count = org.send(relationship_type.pluralize).visible.count + next if organisation_count.zero? + + link_text = generate_organisation_link_text(organisation_count, org, relationship_type) + text += "#{govuk_link_to(link_text, send(organisation_path_helper, org), target: '_blank')}

" + end + + text.html_safe + end + + def stock_owners_text(merge_request) + relationship_text(merge_request, "stock_owner", :stock_owners_organisation_path) + end + + def managing_agent_text(merge_request) + relationship_text(merge_request, "managing_agent", :managing_agents_organisation_path) + end + + def merging_organisations_without_schemes_text(organisations) + return "" unless organisations.count.positive? + + if organisations.count == 1 + "#{organisations.first.name} has no schemes." + else + "#{organisations.map(&:name).to_sentence} have no schemes." + end + end + + def link_to_merging_organisation_schemes(organisation) + count_text = organisation.owned_schemes.count == 1 ? "1 #{organisation.name} scheme" : "all #{organisation.owned_schemes.count} #{organisation.name} schemes" + govuk_link_to "View #{count_text} (opens in a new tab)", schemes_organisation_path(organisation), target: "_blank" + end + + def total_schemes_after_merge_text(merge_request) + count = merge_request.total_visible_schemes_after_merge + "#{"#{count} scheme".pluralize(count)} after merge" + end + + def total_lettings_logs_after_merge_text(merge_request) + count = merge_request.total_visible_lettings_logs_after_merge + "#{"#{count} lettings log".pluralize(count)} after merge" + end + + def total_sales_logs_after_merge_text(merge_request) + count = merge_request.total_visible_sales_logs_after_merge + "#{"#{count} sales log".pluralize(count)} after merge" + end + + def merging_organisations_lettings_logs_outcomes_text(merge_request) + merging_organisations_logs_outcomes_text(merge_request, "lettings") + end + + def merging_organisations_sales_logs_outcomes_text(merge_request) + merging_organisations_logs_outcomes_text(merge_request, "sales") + end + + def merging_organisations_logs_outcomes_text(merge_request, type) + text = "" + if any_organisations_have_logs?(merge_request.merging_organisations, type) + managed_or_reported = type == "lettings" ? "managed" : "reported" + merging_organisations = merge_request.merging_organisations.count == 1 ? "merging organisation" : "merging organisations" + text += "#{merge_request.absorbing_organisation.name} users will have access to all #{type} logs owned or #{managed_or_reported} by the #{merging_organisations} after the merge.

" + + if any_organisations_have_logs_after_merge_date?(merge_request.merging_organisations, type, merge_request.merge_date) + startdate = type == "lettings" ? "tenancy start date" : "sale completion date" + text += "#{type.capitalize} logs that are owned or #{managed_or_reported} by the #{merging_organisations} and have a #{startdate} after the merge date will have their owning or managing organisation changed to #{merge_request.absorbing_organisation.name}.

" + end + + if any_organisations_share_logs?(merge_request.merging_organisations, type) + text += "Some logs are owned and #{managed_or_reported} by different organisations in this merge. They appear in the list for both the owning and the managing organisation.

" + end + end + + organisations_without_logs, organisations_with_logs = merge_request.merging_organisations.partition { |organisation| organisation.send("#{type}_logs").count.zero? } + if merge_request.absorbing_organisation.send("#{type}_logs").count.zero? + organisations_without_logs = [merge_request.absorbing_organisation] + organisations_without_logs + else + organisations_with_logs = [merge_request.absorbing_organisation] + organisations_with_logs + end + + if organisations_without_logs.any? + text += "#{organisations_without_logs.map(&:name).to_sentence} #{organisations_without_logs.count == 1 ? 'has' : 'have'} no #{type} logs.

" + end + + organisations_with_logs.each do |organisation| + text += "#{link_to_merging_organisation_logs(organisation, type)}

" + end + + text.html_safe + end + + def link_to_merging_organisation_logs(organisation, type) + count_text = organisation.send("#{type}_logs").count == 1 ? "1 #{organisation.name} #{type} log" : "all #{organisation.send("#{type}_logs").count} #{organisation.name} #{type} logs" + govuk_link_to "View #{count_text} (opens in a new tab)", send("#{type}_logs_organisation_path", organisation), target: "_blank" + end + + def lettings_logs_outcomes_header_text(merge_request) + count = merge_request.total_visible_lettings_logs_after_merge + "#{count} #{'lettings log'.pluralize(count)} after merge" + end + + def sales_logs_outcomes_header_text(merge_request) + count = merge_request.total_visible_sales_logs_after_merge + "#{count} #{'sales log'.pluralize(count)} after merge" + end + + def any_organisations_have_logs?(organisations, type) + organisations.any? { |organisation| organisation.send("#{type}_logs").count.positive? } + end + + def any_organisations_have_logs_after_merge_date?(organisations, type, merge_date) + organisations.any? { |organisation| organisation.send("#{type}_logs").after_date(merge_date).exists? } + end + + def any_organisations_share_logs?(organisations, type) + organisations.any? { |organisation| organisation.send("#{type}_logs").filter_by_managing_organisation(organisations.where.not(id: organisation.id)).exists? } + end +end diff --git a/app/helpers/tag_helper.rb b/app/helpers/tag_helper.rb index c389942dc..3c2e332f6 100644 --- a/app/helpers/tag_helper.rb +++ b/app/helpers/tag_helper.rb @@ -15,6 +15,10 @@ module TagHelper deleted: "Deleted", merged: "Merged", unconfirmed: "Unconfirmed", + merge_issues: "Merge issues", + request_merged: "Merged", + ready_to_merge: "Ready to merge", + processing: "Processing", }.freeze COLOUR = { @@ -31,6 +35,10 @@ module TagHelper deleted: "red", merged: "orange", unconfirmed: "blue", + merge_issues: "orange", + request_merged: "green", + ready_to_merge: "blue", + processing: "yellow", }.freeze def status_tag(status, classes = []) diff --git a/app/jobs/process_merge_request_job.rb b/app/jobs/process_merge_request_job.rb new file mode 100644 index 000000000..8c3d1842c --- /dev/null +++ b/app/jobs/process_merge_request_job.rb @@ -0,0 +1,15 @@ +class ProcessMergeRequestJob < ApplicationJob + queue_as :default + + def perform(merge_request:) + absorbing_organisation_id = merge_request.absorbing_organisation_id + merging_organisation_ids = merge_request.merging_organisations.pluck(:id) + merge_date = merge_request.merge_date + absorbing_organisation_active_from_merge_date = !merge_request.existing_absorbing_organisation unless merge_request.existing_absorbing_organisation.nil? + + Merge::MergeOrganisationsService.new(absorbing_organisation_id:, merging_organisation_ids:, merge_date:, absorbing_organisation_active_from_merge_date:).call + merge_request.update!(request_merged: true, last_failed_attempt: nil) + rescue StandardError + merge_request.set_back_to_ready_to_merge! + end +end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index e62b1d39d..24fa26bde 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -3,18 +3,182 @@ class MergeRequest < ApplicationRecord has_many :merge_request_organisations belongs_to :absorbing_organisation, class_name: "Organisation", optional: true has_many :merging_organisations, through: :merge_request_organisations, source: :merging_organisation - validate :organisation_name_uniqueness, if: :new_organisation_name - validates :new_telephone_number, presence: true, if: -> { telephone_number_correct == false } + belongs_to :requester, class_name: "User", optional: true STATUS = { - "unsubmitted" => 0, - "submitted" => 1, + merge_issues: "merge_issues", + incomplete: "incomplete", + ready_to_merge: "ready_to_merge", + processing: "processing", + request_merged: "request_merged", + deleted: "deleted", }.freeze enum status: STATUS - def organisation_name_uniqueness - if Organisation.where("lower(name) = ?", new_organisation_name&.downcase).exists? - errors.add(:new_organisation_name, :invalid) + scope :not_merged, -> { where(request_merged: [false, nil]) } + scope :merged, -> { where(request_merged: true) } + scope :visible, lambda { + open_collection_period_start_date = FormHandler.instance.start_date_of_earliest_open_collection_period + merged.where("merge_requests.merge_date >= ?", open_collection_period_start_date).or(not_merged).where(discarded_at: nil) + } + + def absorbing_organisation_name + absorbing_organisation&.name || "" + end + + def dpo_user + absorbing_organisation.data_protection_officers.filter_by_active.first + end + + def discard! + update!(discarded_at: Time.zone.now) + end + + def status + return STATUS[:deleted] if discarded_at.present? + return STATUS[:request_merged] if request_merged + return STATUS[:processing] if processing + return STATUS[:incomplete] unless required_questions_answered? + return STATUS[:ready_to_merge] if absorbing_organisation_signed_dsa? + + STATUS[:merge_issues] + end + + def required_questions_answered? + absorbing_organisation_id.present? && + merge_date.present? && + !existing_absorbing_organisation.nil? && + merging_organisations.count.positive? && + errors.empty? + end + + def absorbing_organisation_signed_dsa? + absorbing_organisation&.data_protection_confirmed? + end + + def total_visible_users_after_merge + return total_users if status == STATUS[:request_merged] || status == STATUS[:processing] + + absorbing_organisation.users.visible.count + merging_organisations.sum { |org| org.users.visible.count } + end + + def total_users_label + "#{total_visible_users_after_merge} #{'user'.pluralize(total_visible_users_after_merge)}" + end + + def organisations_with_users + return [] unless absorbing_organisation.present? && merging_organisations.any? + + ([absorbing_organisation] + merging_organisations).select(&:has_visible_users?) + end + + def organisations_without_users + return [] unless absorbing_organisation.present? && merging_organisations.any? + + ([absorbing_organisation] + merging_organisations).reject(&:has_visible_users?) + end + + def total_visible_schemes_after_merge + return total_schemes if status == STATUS[:request_merged] || status == STATUS[:processing] + + absorbing_organisation.owned_schemes.visible.count + merging_organisations.sum { |org| org.owned_schemes.visible.count } + end + + def total_schemes_label + "#{total_visible_schemes_after_merge} #{'scheme'.pluralize(total_visible_schemes_after_merge)}" + end + + def organisations_with_schemes + return [] unless absorbing_organisation.present? && merging_organisations.any? + + ([absorbing_organisation] + merging_organisations).select(&:has_visible_schemes?) + end + + def organisations_without_schemes + return [] unless absorbing_organisation.present? && merging_organisations.any? + + ([absorbing_organisation] + merging_organisations).reject(&:has_visible_schemes?) + end + + def existing_absorbing_organisation_label + return if existing_absorbing_organisation.nil? + + existing_absorbing_organisation ? "Yes" : "No" + end + + def filter_relationships(absorbing_relationships, merging_relationships, absorbing_organisation, merging_organisations) + unique_relationships = (absorbing_relationships + merging_relationships).uniq + unique_relationships.reject do |relationship| + merging_organisations.include?(relationship) || relationship == absorbing_organisation end end + + def total_stock_owners_after_merge + return total_stock_owners if status == STATUS[:request_merged] || status == STATUS[:processing] + + absorbing_stock_owners = absorbing_organisation.stock_owners.visible + merging_stock_owners = merging_organisations.flat_map { |org| org.stock_owners.visible } + + total_filtered_stock_owners = filter_relationships(absorbing_stock_owners, merging_stock_owners, absorbing_organisation, merging_organisations) + total_filtered_stock_owners.count + end + + def total_managing_agents_after_merge + return total_managing_agents if status == STATUS[:request_merged] || status == STATUS[:processing] + + absorbing_managing_agents = absorbing_organisation.managing_agents.visible + merging_managing_agents = merging_organisations.flat_map { |org| org.managing_agents.visible } + + total_filtered_managing_agents = filter_relationships(absorbing_managing_agents, merging_managing_agents, absorbing_organisation, merging_organisations) + total_filtered_managing_agents.count + end + + def total_stock_owners_managing_agents_label + stock_owners_count = total_stock_owners_after_merge + managing_agents_count = total_managing_agents_after_merge + + "#{stock_owners_count} #{'stock owner'.pluralize(stock_owners_count)}\n#{managing_agents_count} #{'managing agent'.pluralize(managing_agents_count)}" + end + + def total_visible_sales_logs_after_merge + return total_sales_logs if status == STATUS[:request_merged] || status == STATUS[:processing] + + (absorbing_organisation.sales_logs.visible.pluck(:id) + merging_organisations.map { |org| org.sales_logs.visible.pluck(:id) }.flatten).uniq.count + end + + def total_visible_lettings_logs_after_merge + return total_lettings_logs if status == STATUS[:request_merged] || status == STATUS[:processing] + + (absorbing_organisation.lettings_logs.visible.pluck(:id) + merging_organisations.map { |org| org.lettings_logs.visible.pluck(:id) }.flatten).uniq.count + end + + def total_logs_label + "#{total_visible_lettings_logs_after_merge} lettings logs
#{total_visible_sales_logs_after_merge} sales logs" + end + + def start_merge! + update!( + processing: true, + last_failed_attempt: nil, + total_users: total_visible_users_after_merge, + total_schemes: total_visible_schemes_after_merge, + total_stock_owners: total_stock_owners_after_merge, + total_managing_agents: total_managing_agents_after_merge, + total_lettings_logs: total_visible_lettings_logs_after_merge, + total_sales_logs: total_visible_sales_logs_after_merge, + ) + end + + def set_back_to_ready_to_merge! + update!( + last_failed_attempt: Time.zone.now, + processing: false, + total_users: nil, + total_schemes: nil, + total_stock_owners: nil, + total_managing_agents: nil, + total_lettings_logs: nil, + total_sales_logs: nil, + ) + end end diff --git a/app/models/merge_request_organisation.rb b/app/models/merge_request_organisation.rb index 08c1d6d45..6dda8b35e 100644 --- a/app/models/merge_request_organisation.rb +++ b/app/models/merge_request_organisation.rb @@ -5,11 +5,15 @@ class MergeRequestOrganisation < ApplicationRecord validates :merging_organisation, presence: { message: I18n.t("validations.merge_request.merging_organisation_id.blank") } validate :validate_merging_organisations - scope :not_unsubmitted, -> { joins(:merge_request).where.not(merge_requests: { status: "unsubmitted" }) } + scope :merged, -> { joins(:merge_request).where(merge_requests: { request_merged: true }) } scope :with_merging_organisation, ->(merging_organisation) { where(merging_organisation:) } has_paper_trail + def merging_organisation_name + merging_organisation.name || "" + end + private def validate_merging_organisations @@ -17,12 +21,7 @@ private errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) end - if MergeRequestOrganisation.not_unsubmitted.with_merging_organisation(merging_organisation).count.positive? - errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) - merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) - end - - if MergeRequest.not_unsubmitted.where.not(id: merge_request_id).where(requesting_organisation: merging_organisation).count.positive? + if MergeRequestOrganisation.merged.with_merging_organisation(merging_organisation).count.positive? errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 65b35c24e..e769f7b3d 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -191,4 +191,12 @@ class Organisation < ApplicationRecord def label status == :deleted ? "#{name} (deleted)" : name end + + def has_visible_users? + users.visible.count.positive? + end + + def has_visible_schemes? + owned_schemes.visible.count.positive? + end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1ed5014c3..9e68fa7b4 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -126,7 +126,8 @@ <%= govuk_notification_banner( title_text: "Success", success: true, title_heading_level: 3, - title_id: "flash-notice" + title_id: "flash-notice", + role: "alert" ) do |notification_banner| notification_banner.with_heading(text: flash.notice.html_safe) if flash[:notification_banner_body] diff --git a/app/views/merge_requests/_details_list.html.erb b/app/views/merge_requests/_details_list.html.erb new file mode 100644 index 000000000..e93278b08 --- /dev/null +++ b/app/views/merge_requests/_details_list.html.erb @@ -0,0 +1,33 @@ +<%= govuk_summary_list do |summary_list| %> + <% details.each do |detail| %> + <% summary_list.with_row do |row| %> + <% row.with_key { detail[:label] } %> + + <% row.with_value do %> + <% if detail[:value].html_safe? %> +
+ <%= raw(detail[:value]) %> +
+ <% elsif detail[:value].is_a?(Date) || detail[:value].is_a?(Time) %> +
+ <%= detail[:value].strftime("%d %B %Y") %> +
+ <% else %> + <%= simple_format( + detail[:value], + wrapper_tag: "span", + class: "govuk-!-margin-left-8 govuk-!-margin-right-4", + ) %> + <% end %> + <% end %> + + <% if detail[:action].present? %> + <% row.with_action( + text: detail[:action][:text], + href: detail[:action][:href], + visually_hidden_text: detail[:action][:visually_hidden_text], + ) %> + <% end %> + <% end %> + <% end %> +<% end %> diff --git a/app/views/merge_requests/_merge_request_list.html.erb b/app/views/merge_requests/_merge_request_list.html.erb new file mode 100644 index 000000000..e73a1aa02 --- /dev/null +++ b/app/views/merge_requests/_merge_request_list.html.erb @@ -0,0 +1,46 @@ +
+ <% if @merge_requests.empty? %> +

No merge requests

+ <% else %> + <%= govuk_table do |table| %> + <%= table.with_caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do %> + <%= @merge_requests.not_merged.count %> unresolved merge <%= @merge_requests.not_merged.count == 1 ? "request" : "requests" %> + <% end %> + <%= table.with_head do |head| %> + <%= head.with_row do |row| %> + <% row.with_cell(header: true, text: "Absorbing organisation", html_attributes: { + scope: "col", + }) %> + <% row.with_cell(header: true, text: "Merge date", html_attributes: { + scope: "col", + }) %> + <% row.with_cell(header: true, text: "Status", html_attributes: { + scope: "col", + }) %> + <% row.with_cell(header: true, text: "", html_attributes: { + scope: "col", + }) %> + <% end %> + <% end %> + <% @merge_requests.each do |merge_request| %> + <%= table.with_body do |body| %> + <%= body.with_row do |row| %> + <%= row.with_cell(html_attributes: { scope: "row" }) do %> + <%= display_value_or_placeholder(merge_request.absorbing_organisation_name) %> + <% end %> + <% merge_date = merge_request.merge_date %> + <%= row.with_cell(html_attributes: { scope: "row" }) do %> + <%= display_value_or_placeholder(merge_date&.strftime("%d %B %Y")) %> + <% end %> + <% row.with_cell(text: status_tag(merge_request.status)) %> + <% row.with_cell(html_attributes: { + scope: "row", + }) do %> + <%= govuk_link_to("View details", merge_request_path(merge_request.id)) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> +
diff --git a/app/views/merge_requests/_notification_banners.html.erb b/app/views/merge_requests/_notification_banners.html.erb new file mode 100644 index 000000000..38c05dbcd --- /dev/null +++ b/app/views/merge_requests/_notification_banners.html.erb @@ -0,0 +1,21 @@ +<% unless @merge_request.absorbing_organisation_signed_dsa? || @merge_request.absorbing_organisation_id.blank? %> + <%= govuk_notification_banner(title_text: "Important") do %> +

+ The absorbing organisation must accept the Data Sharing Agreement before merging. +

+ <% if @merge_request.dpo_user %> + Contact the Data Protection Officer: <%= link_to @merge_request.dpo_user.name, user_path(@merge_request.dpo_user.id) %> + <% else %> + <%= @merge_request.absorbing_organisation_name %> does not have a Data Protection Officer. You can assign one on the <%= link_to "users page", "#{organisation_path(@merge_request.absorbing_organisation_id)}/users" %>. + <% end %> + <% end %> +<% end %> + +<% if @merge_request.last_failed_attempt.present? %> + <%= govuk_notification_banner(title_text: "Important") do %> +

+ An error occurred while processing the merge. +

+ No changes have been made. Try beginning the merge again. + <% end %> +<% end %> diff --git a/app/views/merge_requests/_summary_card.html.erb b/app/views/merge_requests/_summary_card.html.erb new file mode 100644 index 000000000..4a372f0a0 --- /dev/null +++ b/app/views/merge_requests/_summary_card.html.erb @@ -0,0 +1,8 @@ +
+
+

<%= title %>

+
+
+ <%= render partial: "merge_requests/details_list", locals: { details: } %> +
+
diff --git a/app/views/merge_requests/absorbing_organisation.html.erb b/app/views/merge_requests/absorbing_organisation.html.erb index e63154bac..804e9f03b 100644 --- a/app/views/merge_requests/absorbing_organisation.html.erb +++ b/app/views/merge_requests/absorbing_organisation.html.erb @@ -1,36 +1,34 @@ <% content_for :before_content do %> <% title = "Tell us if your organisation is merging" %> <% content_for :title, title %> - <%= govuk_back_link href: organisations_merge_request_path(id: @merge_request) %> + <%= govuk_back_link href: merge_request_back_link(@merge_request, "absorbing_organisation", request.query_parameters["referrer"]) %> <% end %> -<%= form_with model: @merge_request, url: merge_request_path, method: :patch do |f| %> +<%= form_with model: @merge_request, url: submit_merge_request_url(request.query_parameters["referrer"]), method: :patch do |f| %> <%= f.govuk_error_summary %> -

Which organisation is absorbing the others?

+

Which organisation is absorbing the others?

-

Select the organisation that the other organisations are merging into.

- - <%= f.govuk_radio_buttons_fieldset( - :absorbing_organisation_id, - hint: { text: "For example, if Skype and Yammer merged into Microsoft, you would select Microsoft." }, - legend: nil, - ) do %> - <% @merge_request.merging_organisations.order(:name).each do |org| %> - <%= f.govuk_radio_button( - :absorbing_organisation_id, - org.id, - label: { text: org.name }, - ) %> +

If organisations are merging into a new organisation, <%= govuk_link_to "create the new organisation", new_organisation_path %> first and then select it here.

+
+ <%= f.govuk_select(:absorbing_organisation_id, + label: { text: "Select organisation name", class: "govuk-label--m" }, + "data-controller": "accessible-autocomplete") do %> + <% @answer_options.map { |id, name| OpenStruct.new(id:, name:) }.each do |answer| %> + + <% end %> <% end %> - <%= f.govuk_radio_divider %> - <%= f.govuk_radio_button :absorbing_organisation_id, "other", checked: @merge_request.new_absorbing_organisation?, label: { text: "These organisations are merging into a new one" } %> - <% end %> <%= f.hidden_field :page, value: "absorbing_organisation" %> - - <%= f.govuk_submit %> +
+ <%= f.govuk_submit submit_merge_request_button_text(request.query_parameters["referrer"]) %> + <%= govuk_link_to(secondary_merge_request_link_text(request.query_parameters["referrer"]), merge_request_path(@merge_request)) %> +
<% end %>
diff --git a/app/views/merge_requests/confirm_telephone_number.html.erb b/app/views/merge_requests/confirm_telephone_number.html.erb deleted file mode 100644 index 8b3a354b4..000000000 --- a/app/views/merge_requests/confirm_telephone_number.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -<% content_for :before_content do %> - <% title = "Tell us if your organisation is merging" %> - <% content_for :title, title %> - <%= govuk_back_link href: absorbing_organisation_merge_request_path(id: @merge_request) %> -<% end %> - -<%= form_with model: @merge_request, url: merge_request_path, method: :patch do |f| %> - <%= f.govuk_error_summary %> - -

What is <%= @merge_request.absorbing_organisation.name %>'s telephone number?

- -
-
- <% if @merge_request.absorbing_organisation.phone.present? %> -

Confirm the telephone number on file, or enter a new one.

-
- <%= @merge_request.absorbing_organisation.phone %> -
- <% end %> - - <% if @merge_request.absorbing_organisation.phone.present? %> - <%= f.govuk_radio_buttons_fieldset(:telephone_number_correct, legend: nil) do %> - <%= f.govuk_radio_button :telephone_number_correct, true, checked: @merge_request.telephone_number_correct?, label: { text: "This telephone number is correct" }, link_errors: true %> - <%= f.govuk_radio_button( - :telephone_number_correct, - false, - checked: (@merge_request.new_telephone_number.present? || @merge_request.errors.key?(:new_telephone_number)), - label: { text: "Enter a new phone number" }, - ) do %> - <%= f.govuk_text_field :new_telephone_number, label: { text: "Telephone number" }, width: "two-thirds" %> - <% end %> - <%= f.hidden_field :page, value: "confirm_telephone_number" %> - <% end %> - <% else %> - <%= f.govuk_text_field :new_telephone_number, label: nil, width: "two-thirds" %> - <%= f.hidden_field :page, value: "confirm_telephone_number" %> - <% end %> - <%= f.govuk_submit %> - <% end %> -
-
diff --git a/app/views/merge_requests/delete_confirmation.html.erb b/app/views/merge_requests/delete_confirmation.html.erb new file mode 100644 index 000000000..d95ebba63 --- /dev/null +++ b/app/views/merge_requests/delete_confirmation.html.erb @@ -0,0 +1,23 @@ +<% content_for :before_content do %> + <% content_for :title, "Are you sure you want to delete this merge request?" %> + <%= govuk_back_link(href: :back) %> +<% end %> + +
+
+

+ <%= content_for(:title) %> +

+ + <%= govuk_warning_text(text: "You will not be able to undo this action.") %> + +
+ <%= govuk_button_to( + "Delete merge request", + delete_merge_request_path(@merge_request), + method: :delete, + ) %> + <%= govuk_button_link_to "Cancel", merge_request_path(@merge_request), secondary: true %> +
+
+
diff --git a/app/views/merge_requests/existing_absorbing_organisation.html.erb b/app/views/merge_requests/existing_absorbing_organisation.html.erb new file mode 100644 index 000000000..ce6628a35 --- /dev/null +++ b/app/views/merge_requests/existing_absorbing_organisation.html.erb @@ -0,0 +1,27 @@ +<% content_for :before_content do %> + <% title = "Tell us if your organisation is merging" %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_back_link(@merge_request, "existing_absorbing_organisation", request.query_parameters["referrer"]) %> +<% end %> +
+
+ <%= form_with model: @merge_request, url: submit_merge_request_url(request.query_parameters["referrer"]), method: :patch do |f| %> + <%= f.govuk_error_summary %> + <%= f.govuk_radio_buttons_fieldset :existing_absorbing_organisation, + legend: { text: "Was #{@merge_request.absorbing_organisation&.name} already active before the merge date?", size: "l" } do %> + <%= f.govuk_radio_button :existing_absorbing_organisation, + "true", + label: { text: "Yes, this organisation existed before the merge" } %> + <%= f.govuk_radio_button :existing_absorbing_organisation, + "false", + label: { text: "No, it is a new organisation created by this merge" } %> + <% end %> + + <%= f.hidden_field :page, value: "existing_absorbing_organisation" %> +
+ <%= f.govuk_submit submit_merge_request_button_text(request.query_parameters["referrer"]) %> + <%= govuk_link_to(secondary_merge_request_link_text(request.query_parameters["referrer"]), merge_request_path(@merge_request)) %> +
+ <% end %> +
+
diff --git a/app/views/merge_requests/helpdesk_ticket.html.erb b/app/views/merge_requests/helpdesk_ticket.html.erb new file mode 100644 index 000000000..4ebd11395 --- /dev/null +++ b/app/views/merge_requests/helpdesk_ticket.html.erb @@ -0,0 +1,27 @@ +<% content_for :before_content do %> + <% title = "Which helpdesk ticket reported this merge?" %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_back_link(@merge_request, "helpdesk_ticket", request.query_parameters["referrer"]) %> +<% end %> + +<%= form_with model: @merge_request, url: submit_merge_request_url(request.query_parameters["referrer"]), method: :patch do |f| %> + <%= f.govuk_error_summary %> + +

Which helpdesk ticket reported this merge?

+
+
+

If this merge was reported via a helpdesk ticket, provide the ticket number.
The ticket will be linked to the merge request for reference.

+
+
+
+ <%= f.govuk_text_field :helpdesk_ticket, caption: { text: "Ticket number", class: "govuk-label govuk-label--s" }, label: { text: "For example, MSD-12345", class: "app-!-colour-muted" } %> + <%= f.hidden_field :page, value: "helpdesk_ticket" %> +
+ <%= f.govuk_submit submit_merge_request_button_text(request.query_parameters["referrer"]) %> + <%= govuk_link_to(secondary_merge_request_link_text(request.query_parameters["referrer"], skip_for_now: true), merge_request_path(@merge_request)) %> +
+
+
+ <% end %> +
+
diff --git a/app/views/merge_requests/logs_outcomes.html.erb b/app/views/merge_requests/logs_outcomes.html.erb new file mode 100644 index 000000000..5b9d32359 --- /dev/null +++ b/app/views/merge_requests/logs_outcomes.html.erb @@ -0,0 +1,27 @@ +<% content_for :before_content do %> + <% title = "Users" %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_path(@merge_request) %> +<% end %> + +

+ <%= @merge_request.absorbing_organisation_name %> + Logs +

+
+
+ <% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %> +

<%= total_lettings_logs_after_merge_text(@merge_request) %>

+

+ <%= merging_organisations_lettings_logs_outcomes_text(@merge_request) %> +

+ +
+ +

<%= total_sales_logs_after_merge_text(@merge_request) %>

+

+ <%= merging_organisations_sales_logs_outcomes_text(@merge_request) %> +

+ <% end %> +
+
diff --git a/app/views/merge_requests/merge_date.html.erb b/app/views/merge_requests/merge_date.html.erb index 35d9ef36d..637426446 100644 --- a/app/views/merge_requests/merge_date.html.erb +++ b/app/views/merge_requests/merge_date.html.erb @@ -1,8 +1,26 @@ <% content_for :before_content do %> <% title = "Tell us if your organisation is merging" %> <% content_for :title, title %> - <%# TODO: Update this backlink to also work with the create org flow %> - <%= govuk_back_link href: confirm_telephone_number_merge_request_path(@merge_request) %> + <%= govuk_back_link href: merge_request_back_link(@merge_request, "merge_date", request.query_parameters["referrer"]) %> <% end %> +
+
+ <%= form_with model: @merge_request, url: submit_merge_request_url(request.query_parameters["referrer"]), method: :patch do |f| %> + <%= f.govuk_error_summary %> -

What is the merge date?

+

What is the merge date?

+

+ Enter the official merge date. Log and organisation page data will show the new organisation name from this date.

+ For example, <%= date_mid_collection_year_formatted(Time.zone.now) %>

+ <%= f.govuk_date_field :merge_date, + legend: { hidden: true }, + width: 20 do %> + <% end %> + <%= f.hidden_field :page, value: "merge_date" %> +
+ <%= f.govuk_submit submit_merge_request_button_text(request.query_parameters["referrer"]) %> + <%= govuk_link_to(secondary_merge_request_link_text(request.query_parameters["referrer"]), merge_request_path(@merge_request)) %> +
+ <% end %> +
+
diff --git a/app/views/organisations/merge_request.html.erb b/app/views/merge_requests/merge_request.html.erb similarity index 100% rename from app/views/organisations/merge_request.html.erb rename to app/views/merge_requests/merge_request.html.erb diff --git a/app/views/merge_requests/merge_start_confirmation.html.erb b/app/views/merge_requests/merge_start_confirmation.html.erb new file mode 100644 index 000000000..c85223f3a --- /dev/null +++ b/app/views/merge_requests/merge_start_confirmation.html.erb @@ -0,0 +1,27 @@ +<% content_for :before_content do %> + <% content_for :title, "Are you sure you want to begin this merge?" %> + <%= govuk_back_link href: merge_request_path(@merge_request) %> +<% end %> + +
+
+

+ <%= content_for(:title) %> +

+ + <%= govuk_warning_text(text: "You will not be able to undo this action.") %> + +
+ <%= govuk_button_to( + "Begin merge", + start_merge_merge_request_path(@merge_request), + method: :patch, + ) %> + <%= govuk_button_link_to( + "Cancel", + merge_request_path(@merge_request), + secondary: true, + ) %> +
+
+
diff --git a/app/views/merge_requests/merging_organisations.html.erb b/app/views/merge_requests/merging_organisations.html.erb new file mode 100644 index 000000000..02df05032 --- /dev/null +++ b/app/views/merge_requests/merging_organisations.html.erb @@ -0,0 +1,50 @@ +<% content_for :before_content do %> + <% title = "Tell us if your organisation is merging" %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_back_link(@merge_request, "merging_organisations", request.query_parameters["referrer"]) %> +<% end %> + +<%= form_with model: @merge_request, url: merging_organisations_merge_request_path(referrer: request.query_parameters["referrer"]), method: :patch do |f| %> + <%= f.govuk_error_summary %> +

Which organisations are merging into <%= @merge_request.absorbing_organisation&.name %>?

+ +
+
+

Add all organisations that are merging.

+
+ <%= render partial: "organisation_relationships/related_organisation_select_question", locals: { + label: { text: "Select an organisation", class: "govuk-label--m" }, + field: :merging_organisation, + question: Form::Question.new("", { "answer_options" => @answer_options.reject { |id, _org_name| id != "" && id == @merge_request.absorbing_organisation_id } }, nil), + f:, + } %> + <%= f.hidden_field :new_merging_org_ids, value: @new_merging_org_ids %> + <%= f.govuk_submit "Add organisation", secondary: true, classes: "govuk-button--secondary" %> + <%= govuk_table do |table| %> + <% ordered_merging_organisations(@merge_request, @new_merging_org_ids).each do |merging_organisation| %> + <%= table.with_body do |body| %> + <%= body.with_row do |row| %> + <% row.with_cell(text: merging_organisation.name) %> + <% row.with_cell(html_attributes: { + scope: "row", + class: "govuk-!-text-align-right", + }) do %> + <%= govuk_link_to("Remove", merging_organisations_remove_merge_request_path(merge_request: { merging_organisation: merging_organisation.id, new_merging_org_ids: @new_merging_org_ids })) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <%= form_with model: @merge_request, url: merge_request_path(id: @merge_request.id), method: :patch do |f| %> + <%= f.hidden_field :page, value: "merging_organisations" %> + <%= f.hidden_field :new_merging_org_ids, value: @new_merging_org_ids %> +
+ <% if @merge_request.merging_organisations.count.positive? || @new_merging_org_ids.count.positive? %> + <%= f.govuk_submit submit_merge_request_button_text(request.query_parameters["referrer"]) %> + <%= govuk_link_to secondary_merge_request_link_text(request.query_parameters["referrer"]), merge_request_path(@merge_request) %> + <% end %> +
+ <% end %> +
+
diff --git a/app/views/merge_requests/new_organisation_address.html.erb b/app/views/merge_requests/new_organisation_address.html.erb deleted file mode 100644 index 7dd331507..000000000 --- a/app/views/merge_requests/new_organisation_address.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -<% content_for :before_content do %> - <% title = "New organisation address" %> - <% content_for :title, title %> - <%= govuk_back_link href: new_organisation_name_merge_request_path(@merge_request) %> -<% end %> - -<%= form_with model: @merge_request, url: merge_request_path, method: :patch do |f| %> - <%= f.govuk_error_summary %> - -

What is <%= @merge_request.new_organisation_name.possessive %> address?

-
-
- <%= f.govuk_text_field :new_organisation_address_line1, - label: { text: "Address line 1", size: "m" }, - autocomplete: "address-line1" %> - - <%= f.govuk_text_field :new_organisation_address_line2, - label: { text: "Address line 2", size: "m" }, - autocomplete: "address-line2" %> - - <%= f.govuk_text_field :new_organisation_postcode, - label: { text: "Postcode", size: "m" }, - autocomplete: "postal-code", - width: 10 %> - - <%= f.hidden_field :page, value: "new_organisation_address" %> -
- <%= f.govuk_submit %> - <%= govuk_link_to("Skip for now", new_organisation_telephone_number_merge_request_path(@merge_request)) %> -
-
-
-<% end %> diff --git a/app/views/merge_requests/new_organisation_name.html.erb b/app/views/merge_requests/new_organisation_name.html.erb deleted file mode 100644 index 8b391e735..000000000 --- a/app/views/merge_requests/new_organisation_name.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -<% content_for :before_content do %> - <% title = "New organisation name" %> - <% content_for :title, title %> - <%= govuk_back_link href: absorbing_organisation_merge_request_path(id: @merge_request) %> -<% end %> - -<%= form_with model: @merge_request, url: merge_request_path, method: :patch do |f| %> - <%= f.govuk_error_summary %> - -

What is the new organisation called?

- -
-
- <%= f.govuk_text_field :new_organisation_name, label: nil %> - <%= f.hidden_field :page, value: "new_organisation_name" %> - <%= f.govuk_submit %> - <% end %> -
-
diff --git a/app/views/merge_requests/new_organisation_telephone_number.html.erb b/app/views/merge_requests/new_organisation_telephone_number.html.erb deleted file mode 100644 index 6c0c1d81e..000000000 --- a/app/views/merge_requests/new_organisation_telephone_number.html.erb +++ /dev/null @@ -1,20 +0,0 @@ -<% content_for :before_content do %> - <% title = "New organisation telephone number" %> - <% content_for :title, title %> - <%= govuk_back_link href: new_organisation_address_merge_request_path(@merge_request) %> -<% end %> - -<%= form_with model: @merge_request, url: merge_request_path, method: :patch do |f| %> - <%= f.govuk_error_summary %> - -

What is <%= @merge_request.new_organisation_name.possessive %> telephone number?

-
-
- <%= f.govuk_text_field :new_organisation_telephone_number, label: nil, width: "two-thirds" %> - <%= f.hidden_field :page, value: "new_organisation_telephone_number" %> -
- <%= f.govuk_submit %> -
-
-
-<% end %> diff --git a/app/views/merge_requests/new_organisation_type.html.erb b/app/views/merge_requests/new_organisation_type.html.erb deleted file mode 100644 index 2e22071e7..000000000 --- a/app/views/merge_requests/new_organisation_type.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% content_for :before_content do %> - <% title = "New organisation type" %> - <% content_for :title, title %> - <%= govuk_back_link href: new_organisation_telephone_number_merge_request_path(@merge_request) %> -<% end %> diff --git a/app/views/merge_requests/organisations.html.erb b/app/views/merge_requests/organisations.html.erb deleted file mode 100644 index 8a47a8641..000000000 --- a/app/views/merge_requests/organisations.html.erb +++ /dev/null @@ -1,51 +0,0 @@ -<% content_for :before_content do %> - <% title = "Tell us if your organisation is merging" %> - <% content_for :title, title %> - <%= govuk_back_link href: merge_request_organisation_path(id: @merge_request.requesting_organisation_id) %> -<% end %> - -<%= form_with model: @merge_request, url: organisations_merge_request_path, method: :patch do |f| %> - <%= f.govuk_error_summary %> -

Which organisations are merging?

- -
-
-

- Add all organisations to be merged - we have already added your own. -

- -

Start typing to search

- <%= render partial: "organisation_relationships/related_organisation_select_question", locals: { - field: :merging_organisation, - question: Form::Question.new("", { "answer_options" => @answer_options }, nil), - f:, - } %> - <%= f.govuk_submit "Add organisation", classes: "govuk-button--secondary" %> - <%= govuk_table do |table| %> - <% @merge_request.merging_organisations.order(:name).each do |merging_organisation| %> - <%= table.with_body do |body| %> - <%= body.with_row do |row| %> - <% row.with_cell(text: merging_organisation.name) %> - <% row.with_cell(html_attributes: { - scope: "row", - class: "govuk-!-text-align-right", - }) do %> - <% if @merge_request.requesting_organisation != merging_organisation %> - <%= govuk_link_to("Remove", organisations_remove_merge_request_path(merge_request: { merging_organisation: merging_organisation.id })) %> - <% end %> - <% end %> - <% end %> - <% end %> - <% end %> - <% end %> - <% end %> - <%= form_with model: @merge_request, url: merge_request_path(id: @merge_request.id), method: :patch do |f| %> - <%= govuk_details(summary_text: "I cannot find an organisation on the list") do %> - <%= f.govuk_text_area :other_merging_organisations, label: { text: "Other organisations" }, hint: { text: "List other organisations that are part of the merge but not registered on CORE." }, rows: 9 %> - <% end %> - <% if @merge_request.merging_organisations.count > 1 %> - <%= f.hidden_field :page, value: "organisations" %> - <%= f.govuk_submit "Continue" %> - <% end %> - <% end %> -
diff --git a/app/views/merge_requests/relationship_outcomes.html.erb b/app/views/merge_requests/relationship_outcomes.html.erb new file mode 100644 index 000000000..6d1eba0d7 --- /dev/null +++ b/app/views/merge_requests/relationship_outcomes.html.erb @@ -0,0 +1,25 @@ +<% content_for :before_content do %> + <% title = "Stock owners & managing agents".html_safe %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_path(@merge_request) %> +<% end %> + +

+ <%= @merge_request.absorbing_organisation_name %> + Stock owners & managing agents +

+
+
+ <% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %> +

<%= total_stock_owners_after_merge_text(@merge_request) %>

+

+ <%= stock_owners_text(@merge_request) %> +

+
+

<%= total_managing_agents_after_merge_text(@merge_request) %>

+

+ <%= managing_agent_text(@merge_request) %> +

+ <% end %> +
+
diff --git a/app/views/merge_requests/scheme_outcomes.html.erb b/app/views/merge_requests/scheme_outcomes.html.erb new file mode 100644 index 000000000..ff8a8ffd3 --- /dev/null +++ b/app/views/merge_requests/scheme_outcomes.html.erb @@ -0,0 +1,23 @@ +<% content_for :before_content do %> + <% title = "Schemes" %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_path(@merge_request) %> +<% end %> + +

+ <%= @merge_request.absorbing_organisation_name %> + Schemes +

+ +<% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %> +

<%= total_schemes_after_merge_text(@merge_request) %>

+

+ <%= merging_organisations_without_schemes_text(@merge_request.organisations_without_schemes) %> +

+ + <% @merge_request.organisations_with_schemes.map do |org| %> +

+ <%= link_to_merging_organisation_schemes(org) %> +

+ <% end %> +<% end %> diff --git a/app/views/merge_requests/show.html.erb b/app/views/merge_requests/show.html.erb new file mode 100644 index 000000000..0fbde7621 --- /dev/null +++ b/app/views/merge_requests/show.html.erb @@ -0,0 +1,26 @@ +<% content_for :before_content do %> + <% title = "Merge request: #{@merge_request.absorbing_organisation_name}" %> + <% content_for :title, title %> + <%= govuk_back_link href: organisations_path(tab: "merge-requests") %> +<% end %> + +<%= render partial: "notification_banners" %> + +

+ Merge request + <%= display_value_or_placeholder(@merge_request.absorbing_organisation_name) %> +

+<% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %> +
+ <%= govuk_button_link_to "Begin merge", merge_start_confirmation_merge_request_path(@merge_request), disabled: @merge_request.status != "ready_to_merge" %> + <%= govuk_button_link_to "Delete merge request", delete_confirmation_merge_request_path(@merge_request), warning: true %> +
+<% end %> + +<%= render partial: "merge_requests/summary_card", locals: { title: "Request details", details: request_details(@merge_request) } %> + +<%= render partial: "merge_requests/summary_card", locals: { title: "Merge details", details: merge_details(@merge_request) } %> + +<% unless @merge_request.status == "incomplete" %> + <%= render partial: "merge_requests/summary_card", locals: { title: "Merge outcomes", details: merge_outcomes(@merge_request) } %> +<% end %> diff --git a/app/views/merge_requests/user_outcomes.html.erb b/app/views/merge_requests/user_outcomes.html.erb new file mode 100644 index 000000000..411b78216 --- /dev/null +++ b/app/views/merge_requests/user_outcomes.html.erb @@ -0,0 +1,23 @@ +<% content_for :before_content do %> + <% title = "Users" %> + <% content_for :title, title %> + <%= govuk_back_link href: merge_request_path(@merge_request) %> +<% end %> + +

+ <%= @merge_request.absorbing_organisation_name %> + Users +

+ +<% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %> +

<%= total_users_after_merge_text(@merge_request) %>

+

+ <%= merging_organisations_without_users_text(@merge_request.organisations_without_users) %> +

+ + <% @merge_request.organisations_with_users.map do |org| %> +

+ <%= link_to_merging_organisation_users(org) %> +

+ <% end %> +<% end %> diff --git a/app/views/organisation_relationships/_related_organisation_select_question.html.erb b/app/views/organisation_relationships/_related_organisation_select_question.html.erb index 551bce5ed..f0d1aa2eb 100644 --- a/app/views/organisation_relationships/_related_organisation_select_question.html.erb +++ b/app/views/organisation_relationships/_related_organisation_select_question.html.erb @@ -1,3 +1,3 @@ <% answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } %> -<%= f.govuk_collection_select field, answers, :id, :name, label: { hidden: true }, "data-controller": "accessible-autocomplete" do %> +<%= f.govuk_collection_select field, answers, :id, :name, label:, "data-controller": "accessible-autocomplete" do %> <% end %> diff --git a/app/views/organisation_relationships/add_managing_agent.html.erb b/app/views/organisation_relationships/add_managing_agent.html.erb index b3e3a4a42..25c7c53a2 100644 --- a/app/views/organisation_relationships/add_managing_agent.html.erb +++ b/app/views/organisation_relationships/add_managing_agent.html.erb @@ -19,6 +19,7 @@ <% end %> <%= render partial: "organisation_relationships/related_organisation_select_question", locals: { field: :child_organisation_id, + label: { hidden: true }, question: Form::Question.new("", { "answer_options" => answer_options }, nil), f:, } %> diff --git a/app/views/organisation_relationships/add_stock_owner.html.erb b/app/views/organisation_relationships/add_stock_owner.html.erb index b74d812ec..042442125 100644 --- a/app/views/organisation_relationships/add_stock_owner.html.erb +++ b/app/views/organisation_relationships/add_stock_owner.html.erb @@ -19,6 +19,7 @@ <% end %> <%= render partial: "organisation_relationships/related_organisation_select_question", locals: { field: :parent_organisation_id, + label: { hidden: true }, question: Form::Question.new("", { "answer_options" => answer_options }, nil), f:, } %> diff --git a/app/views/organisations/index.html.erb b/app/views/organisations/index.html.erb index 72d1d2f43..3b96288f1 100644 --- a/app/views/organisations/index.html.erb +++ b/app/views/organisations/index.html.erb @@ -5,13 +5,18 @@ <%= render partial: "organisations/headings", locals: request.path == organisations_path ? { main: "Organisations", sub: nil } : { main: @organisation.name, sub: "Organisations" } %> -<% if current_user.support? %> - <%= govuk_button_link_to "Create a new organisation", new_organisation_path, html: { method: :get } %> -<% end %> - -<%= render SearchComponent.new(current_user:, search_label: "Search by organisation name", value: @searched) %> - -<%= govuk_section_break(visible: true, size: "m") %> - -<%= render partial: "organisation_list", locals: { organisations: @organisations, title: "Organisations", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %> -<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "organisations" } %> +
+ <%= govuk_tabs(title: "Collection resources", classes: %w[app-tab__large-headers]) do |c| %> + <% c.with_tab(label: "All organisations") do %> + <%= govuk_button_link_to "Create a new organisation", new_organisation_path, html: { method: :get } %> + <%= render SearchComponent.new(current_user:, search_label: "Search by organisation name", value: @searched) %> + <%= govuk_section_break(visible: true, size: "m") %> + <%= render partial: "organisation_list", locals: { organisations: @organisations, title: "Organisations", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %> + <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "organisations" } %> + <% end %> + <% c.with_tab(label: "Merge requests") do %> + <%= govuk_button_to "Create new merge request", merge_requests_path, html: { method: :post } %> + <%= render partial: "merge_requests/merge_request_list", locals: { merge_requests: @merge_requests, title: "Merge requests", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %> + <% end %> + <% end %> +
diff --git a/config/locales/en.yml b/config/locales/en.yml index bae5e568d..7d52a92e0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -175,17 +175,12 @@ en: merge_request: attributes: absorbing_organisation_id: - blank: "Select the organisation absorbing the others" - telephone_number_correct: - blank: "Select to confirm or enter a new telephone number" - invalid: "Enter a valid telephone number" - new_telephone_number: - blank: "Enter a valid telephone number" - new_organisation_name: - blank: "Enter an organisation name" - invalid: "An organisation with this name already exists" - new_organisation_telephone_number: - blank: "Enter a valid telephone number" + blank: "Select the absorbing organisation" + merge_date: + blank: "Enter a merge date" + invalid: "Enter a valid merge date" + existing_absorbing_organisation: + blank: "You must answer absorbing organisation already active?" notification: logs_deleted: diff --git a/config/routes.rb b/config/routes.rb index faea457fe..4817bebb9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -203,16 +203,21 @@ Rails.application.routes.draw do resources :merge_requests, path: "/merge-request" do member do - get "organisations" - patch "organisations", to: "merge_requests#update_organisations" - get "organisations/remove", to: "merge_requests#remove_merging_organisation" + get "merging-organisations" + patch "merging-organisations", to: "merge_requests#update_merging_organisations" + get "merging-organisations/remove", to: "merge_requests#remove_merging_organisation" get "absorbing-organisation" - get "confirm-telephone-number" - get "new-organisation-name" - get "new-organisation-address" - get "new-organisation-telephone-number" - get "new-organisation-type" get "merge-date" + get "existing-absorbing-organisation" + get "helpdesk-ticket" + get "merge-start-confirmation" + get "user-outcomes" + get "relationship-outcomes" + get "scheme-outcomes" + get "logs-outcomes" + get "delete-confirmation", to: "merge_requests#delete_confirmation" + delete "delete", to: "merge_requests#delete" + patch "start-merge", to: "merge_requests#start_merge" end end diff --git a/db/migrate/20240808134014_add_merge_date_to_merge_requests.rb b/db/migrate/20240808134014_add_merge_date_to_merge_requests.rb new file mode 100644 index 000000000..80a2c5650 --- /dev/null +++ b/db/migrate/20240808134014_add_merge_date_to_merge_requests.rb @@ -0,0 +1,5 @@ +class AddMergeDateToMergeRequests < ActiveRecord::Migration[7.0] + def change + add_column :merge_requests, :merge_date, :datetime + end +end diff --git a/db/migrate/20240809154241_add_additional_fields_to_merge_requests.rb b/db/migrate/20240809154241_add_additional_fields_to_merge_requests.rb new file mode 100644 index 000000000..6f2a37fd0 --- /dev/null +++ b/db/migrate/20240809154241_add_additional_fields_to_merge_requests.rb @@ -0,0 +1,16 @@ +class AddAdditionalFieldsToMergeRequests < ActiveRecord::Migration[7.0] + def change + change_table :merge_requests, bulk: true do |t| + t.integer :requester_id + t.string :helpdesk_ticket + t.integer :total_users + t.integer :total_schemes + t.integer :total_lettings_logs + t.integer :total_sales_logs + t.integer :total_stock_owners + t.integer :total_managing_agents + t.boolean :signed_dsa, default: false + t.datetime :discarded_at + end + end +end diff --git a/db/migrate/20240813072041_remove_other_merging_org_field.rb b/db/migrate/20240813072041_remove_other_merging_org_field.rb new file mode 100644 index 000000000..eb1ddc2e4 --- /dev/null +++ b/db/migrate/20240813072041_remove_other_merging_org_field.rb @@ -0,0 +1,5 @@ +class RemoveOtherMergingOrgField < ActiveRecord::Migration[7.0] + def change + remove_column :merge_requests, :other_merging_organisations, :string + end +end diff --git a/db/migrate/20240813112119_remove_new_org_merge_request_fields.rb b/db/migrate/20240813112119_remove_new_org_merge_request_fields.rb new file mode 100644 index 000000000..c7050b54e --- /dev/null +++ b/db/migrate/20240813112119_remove_new_org_merge_request_fields.rb @@ -0,0 +1,27 @@ +class RemoveNewOrgMergeRequestFields < ActiveRecord::Migration[7.0] + def up + change_table :merge_requests, bulk: true do |t| + t.remove :new_absorbing_organisation + t.remove :telephone_number_correct + t.remove :new_telephone_number + t.remove :new_organisation_name + t.remove :new_organisation_address_line1 + t.remove :new_organisation_address_line2 + t.remove :new_organisation_postcode + t.remove :new_organisation_telephone_number + end + end + + def down + change_table :merge_requests, bulk: true do |t| + t.column :new_absorbing_organisation, :boolean + t.column :telephone_number_correct, :boolean + t.column :new_telephone_number, :string + t.column :new_organisation_name, :string + t.column :new_organisation_address_line1, :string + t.column :new_organisation_address_line2, :string + t.column :new_organisation_postcode, :string + t.column :new_organisation_telephone_number, :string + end + end +end diff --git a/db/migrate/20240814083017_add_last_failed_attempt.rb b/db/migrate/20240814083017_add_last_failed_attempt.rb new file mode 100644 index 000000000..34e0e4046 --- /dev/null +++ b/db/migrate/20240814083017_add_last_failed_attempt.rb @@ -0,0 +1,5 @@ +class AddLastFailedAttempt < ActiveRecord::Migration[7.0] + def change + add_column :merge_requests, :last_failed_attempt, :datetime + end +end diff --git a/db/migrate/20240819100411_update_merge_request_fields_for_status.rb b/db/migrate/20240819100411_update_merge_request_fields_for_status.rb new file mode 100644 index 000000000..6613d27a4 --- /dev/null +++ b/db/migrate/20240819100411_update_merge_request_fields_for_status.rb @@ -0,0 +1,17 @@ +class UpdateMergeRequestFieldsForStatus < ActiveRecord::Migration[7.0] + def up + change_table :merge_requests, bulk: true do |t| + t.column :request_merged, :boolean + t.column :processing, :boolean + t.remove :status + end + end + + def down + change_table :merge_requests, bulk: true do |t| + t.remove :request_merged + t.remove :processing + t.column :status, :string + end + end +end diff --git a/db/migrate/20240822080228_add_existing_absorbing_organisation_field.rb b/db/migrate/20240822080228_add_existing_absorbing_organisation_field.rb new file mode 100644 index 000000000..f1a0d8b1e --- /dev/null +++ b/db/migrate/20240822080228_add_existing_absorbing_organisation_field.rb @@ -0,0 +1,5 @@ +class AddExistingAbsorbingOrganisationField < ActiveRecord::Migration[7.0] + def change + add_column :merge_requests, :existing_absorbing_organisation, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 00159931b..05f9bfde2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_08_19_143150) do +ActiveRecord::Schema[7.0].define(version: 2024_08_22_080228) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -429,19 +429,24 @@ ActiveRecord::Schema[7.0].define(version: 2024_08_19_143150) do create_table "merge_requests", force: :cascade do |t| t.integer "requesting_organisation_id" - t.text "other_merging_organisations" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "status" t.integer "absorbing_organisation_id" - t.boolean "new_absorbing_organisation" - t.boolean "telephone_number_correct" - t.string "new_telephone_number" - t.string "new_organisation_name" - t.string "new_organisation_address_line1" - t.string "new_organisation_address_line2" - t.string "new_organisation_postcode" - t.string "new_organisation_telephone_number" + t.datetime "merge_date" + t.integer "requester_id" + t.string "helpdesk_ticket" + t.integer "total_users" + t.integer "total_schemes" + t.integer "total_lettings_logs" + t.integer "total_sales_logs" + t.integer "total_stock_owners" + t.integer "total_managing_agents" + t.boolean "signed_dsa", default: false + t.datetime "discarded_at" + t.datetime "last_failed_attempt" + t.boolean "request_merged" + t.boolean "processing" + t.boolean "existing_absorbing_organisation" end create_table "notifications", force: :cascade do |t| diff --git a/spec/factories/merge_request.rb b/spec/factories/merge_request.rb new file mode 100644 index 000000000..19020fce1 --- /dev/null +++ b/spec/factories/merge_request.rb @@ -0,0 +1,8 @@ +FactoryBot.define do + factory :merge_request do + status { "incomplete" } + merge_date { nil } + helpdesk_ticket { "MSD-99999" } + association :requesting_organisation, factory: :organisation + end +end diff --git a/spec/factories/merge_request_organisation.rb b/spec/factories/merge_request_organisation.rb new file mode 100644 index 000000000..178401fb0 --- /dev/null +++ b/spec/factories/merge_request_organisation.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :merge_request_organisation do + association :merging_organisation, factory: :organisation + association :merge_request, factory: :merge_request + end +end diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index 2d43e9b02..0706a0d7e 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -766,8 +766,10 @@ RSpec.describe "Schemes scheme Features" do before do Timecop.freeze(Time.zone.local(2023, 10, 10)) + Singleton.__init__(FormHandler) FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location: deactivated_location) Timecop.unfreeze + Singleton.__init__(FormHandler) click_link(scheme.service_name) end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb new file mode 100644 index 000000000..0db07e02b --- /dev/null +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -0,0 +1,272 @@ +require "rails_helper" + +RSpec.describe MergeRequestsHelper do + describe "#merging_organisations_without_users_text" do + context "with 1 organisation" do + let(:organisation) { build(:organisation, name: "Org 1") } + + it "returns the correct text" do + expect(merging_organisations_without_users_text([organisation])).to eq("Org 1 has no users.") + end + end + + context "with 2 organisations" do + let(:organisation) { build(:organisation, name: "Org 1") } + let(:organisation_2) { build(:organisation, name: "Org 2") } + + it "returns the correct text" do + expect(merging_organisations_without_users_text([organisation, organisation_2])).to eq("Org 1 and Org 2 have no users.") + end + end + + context "with 3 organisations" do + let(:organisation) { build(:organisation, name: "Org 1") } + let(:organisation_2) { build(:organisation, name: "Org 2") } + let(:organisation_3) { build(:organisation, name: "Org 3") } + + it "returns the correct text" do + expect(merging_organisations_without_users_text([organisation, organisation_2, organisation_3])).to eq("Org 1, Org 2, and Org 3 have no users.") + end + end + end + + describe "#link_to_merging_organisation_users" do + context "with 1 organisation user" do + let(:organisation) { create(:organisation, name: "Org 1") } + + it "returns the correct link" do + expect(link_to_merging_organisation_users(organisation)).to include("View 1 Org 1 user (opens in a new tab)") + expect(link_to_merging_organisation_users(organisation)).to include(users_organisation_path(organisation)) + end + end + + context "with multiple organisation users" do + let(:organisation) { create(:organisation, name: "Org 1") } + + before do + create(:user, organisation:) + end + + it "returns the correct link" do + expect(link_to_merging_organisation_users(organisation)).to include("View all 2 Org 1 users (opens in a new tab)") + expect(link_to_merging_organisation_users(organisation)).to include(users_organisation_path(organisation)) + end + end + end + + describe "#merging_organisations_without_schemes_text" do + context "with 1 organisation" do + let(:organisation) { build(:organisation, name: "Org 1") } + + it "returns the correct text" do + expect(merging_organisations_without_schemes_text([organisation])).to eq("Org 1 has no schemes.") + end + end + + context "with 2 organisations" do + let(:organisation) { build(:organisation, name: "Org 1") } + let(:organisation_2) { build(:organisation, name: "Org 2") } + + it "returns the correct text" do + expect(merging_organisations_without_schemes_text([organisation, organisation_2])).to eq("Org 1 and Org 2 have no schemes.") + end + end + + context "with 3 organisations" do + let(:organisation) { build(:organisation, name: "Org 1") } + let(:organisation_2) { build(:organisation, name: "Org 2") } + let(:organisation_3) { build(:organisation, name: "Org 3") } + + it "returns the correct text" do + expect(merging_organisations_without_schemes_text([organisation, organisation_2, organisation_3])).to eq("Org 1, Org 2, and Org 3 have no schemes.") + end + end + end + + describe "#link_to_merging_organisation_schemes" do + context "with 1 organisation scheme" do + let(:organisation) { create(:organisation, name: "Org 1") } + + before do + create(:scheme, owning_organisation: organisation) + end + + it "returns the correct link" do + expect(link_to_merging_organisation_schemes(organisation)).to include("View 1 Org 1 scheme (opens in a new tab)") + expect(link_to_merging_organisation_schemes(organisation)).to include(schemes_organisation_path(organisation)) + end + end + + context "with multiple organisation schemes" do + let(:organisation) { create(:organisation, name: "Org 1") } + + before do + create_list(:scheme, 2, owning_organisation: organisation) + end + + it "returns the correct link" do + expect(link_to_merging_organisation_schemes(organisation)).to include("View all 2 Org 1 schemes (opens in a new tab)") + expect(link_to_merging_organisation_schemes(organisation)).to include(schemes_organisation_path(organisation)) + end + end + end + + describe "when creating relationship outcomes content" do + let(:stock_owner1) { create(:organisation, name: "Stock owner 1") } + let(:stock_owner2) { create(:organisation, name: "Stock owner 2") } + let(:managing_agent1) { create(:organisation, name: "Managing agent 1") } + let(:managing_agent2) { create(:organisation, name: "Managing agent 2") } + let(:absorbing_organisation) { create(:organisation, name: "Absorbing Org") } + let(:merging_organisations) { create_list(:organisation, 2) { |org, i| org.name = "Dummy Org #{i + 1}" } } + let(:merge_request) { create(:merge_request, absorbing_organisation:, merging_organisations:) } + + context "when there are no relationships" do + it "returns text stating there are no stock owners" do + expect(stock_owners_text(merge_request)).to eq("Absorbing Org, Dummy Org 1, and Dummy Org 2 have no stock owners.

") + end + + it "returns text stating there are no managing agents" do + expect(managing_agent_text(merge_request)).to eq("Absorbing Org, Dummy Org 1, and Dummy Org 2 have no managing agents.

") + end + end + + context "when there are stock owners" do + before do + create(:organisation_relationship, child_organisation: absorbing_organisation, parent_organisation: stock_owner1) + create(:organisation_relationship, child_organisation: merging_organisations.first, parent_organisation: stock_owner2) + create(:organisation_relationship, child_organisation: merging_organisations.first, parent_organisation: stock_owner1) + end + + it "returns text stating the relationships" do + expect(stock_owners_text(merge_request)).to include("Some of the organisations merging have common stock owners.") + expect(stock_owners_text(merge_request)).to include("Dummy Org 2 has no stock owners.") + expect(stock_owners_text(merge_request)).to include("View all 2 Dummy Org 1 stock owners (opens in a new tab)") + end + end + + context "when there are managing agents" do + before do + create(:organisation_relationship, parent_organisation: absorbing_organisation, child_organisation: managing_agent1) + create(:organisation_relationship, parent_organisation: absorbing_organisation, child_organisation: managing_agent2) + create(:organisation_relationship, parent_organisation: merging_organisations.first, child_organisation: managing_agent2) + end + + it "returns text stating the relationships" do + expect(managing_agent_text(merge_request)).to include("Some of the organisations merging have common managing agents.") + expect(managing_agent_text(merge_request)).to include("Dummy Org 2 has no managing agents.") + expect(managing_agent_text(merge_request)).to include("View the 1 Dummy Org 1 managing agent (opens in a new tab)") + end + end + end + + describe "logs outcomes summary" do + let(:organisation) { create(:organisation, name: "Org 1") } + let(:merging_organisation) { create(:organisation, name: "Org 2") } + let(:merging_organisation_2) { create(:organisation, name: "Org 3") } + let(:merge_request) { create(:merge_request, absorbing_organisation: organisation, merge_date: Time.zone.today) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation:) + end + + context "when merging organisations don't have logs" do + it "returns the correct merging_organisations_lettings_logs_outcomes_text text" do + outcome_text = merging_organisations_lettings_logs_outcomes_text(merge_request) + expect(outcome_text).not_to include("Org 1 users will have access to all lettings logs owned or managed by the merging organisation after the merge.") + expect(outcome_text).not_to include("Lettings logs that are owned or managed by the merging organisation and have a tenancy start date after the merge date will have their owning or managing organisation changed to Org 1.") + expect(outcome_text).not_to include("Some logs are owned and managed by different organisations in this merge. They appear in the list for both the owning and the managing organisation.") + expect(outcome_text).to include("Org 1 and Org 2 have no lettings logs.") + end + + it "returns correct lettings_logs_outcomes_header_text" do + expect(lettings_logs_outcomes_header_text(merge_request)).to eq("0 lettings logs after merge") + end + + it "returns the correct merging_organisations_sales_logs_outcomes_text text" do + outcome_text = merging_organisations_sales_logs_outcomes_text(merge_request) + expect(outcome_text).not_to include("Org 1 users will have access to all sales logs owned or reported by the merging organisation after the merge.") + expect(outcome_text).not_to include("Sales logs that are owned or reported by the merging organisation and have a sale completion date after the merge date will have their owning or managing organisation changed to Org 1.") + expect(outcome_text).not_to include("Some logs are owned and reported by different organisation in this merge. They appear in the list for both the owning and the managing organisation.") + expect(outcome_text).to include("Org 1 and Org 2 have no sales logs.") + end + + it "returns correct sales_logs_outcomes_header_text" do + expect(sales_logs_outcomes_header_text(merge_request)).to eq("0 sales logs after merge") + end + end + + context "when merging organisations have logs" do + before do + create(:lettings_log, owning_organisation: organisation) + create(:lettings_log, owning_organisation: merging_organisation, startdate: Time.zone.tomorrow) + create(:lettings_log, owning_organisation: merging_organisation, startdate: Time.zone.yesterday) + create(:sales_log, owning_organisation: organisation) + create(:sales_log, owning_organisation: merging_organisation, saledate: Time.zone.tomorrow) + create(:sales_log, owning_organisation: merging_organisation, saledate: Time.zone.yesterday) + end + + it "returns the correct merging_organisations_lettings_logs_outcomes_text text" do + outcome_text = merging_organisations_lettings_logs_outcomes_text(merge_request) + expect(outcome_text).to include("Org 1 users will have access to all lettings logs owned or managed by the merging organisation after the merge.") + expect(outcome_text).to include("Lettings logs that are owned or managed by the merging organisation and have a tenancy start date after the merge date will have their owning or managing organisation changed to Org 1.") + expect(outcome_text).not_to include("Some logs are owned and managed by different organisations in this merge. They appear in the list for both the owning and the managing organisation.") + expect(outcome_text).not_to include("Org 2 has no lettings logs.") + expect(outcome_text).to include("View all 2 Org 2 lettings logs (opens in a new tab)") + end + + it "returns correct lettings_logs_outcomes_header_text" do + expect(lettings_logs_outcomes_header_text(merge_request)).to eq("3 lettings logs after merge") + end + + it "returns the correct merging_organisations_sales_logs_outcomes_text text" do + outcome_text = merging_organisations_sales_logs_outcomes_text(merge_request) + expect(outcome_text).to include("Org 1 users will have access to all sales logs owned or reported by the merging organisation after the merge.") + expect(outcome_text).to include("Sales logs that are owned or reported by the merging organisation and have a sale completion date after the merge date will have their owning or managing organisation changed to Org 1.") + expect(outcome_text).not_to include("Some logs are owned and reported by different organisations in this merge. They appear in the list for both the owning and the managing organisation.") + expect(outcome_text).not_to include("Org 2 has no sales logs.") + expect(outcome_text).to include("View all 2 Org 2 sales logs (opens in a new tab)") + end + + it "returns correct sales_logs_outcomes_header_text" do + expect(sales_logs_outcomes_header_text(merge_request)).to eq("3 sales logs after merge") + end + + context "when logs are owned and managed by organisations in the same merge" do + before do + create(:organisation_relationship, parent_organisation: merging_organisation_2, child_organisation: merging_organisation) + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_2) + create(:lettings_log, assigned_to: merging_organisation_2.users.first, owning_organisation: merging_organisation_2, managing_organisation: merging_organisation, startdate: Time.zone.yesterday) + create(:sales_log, assigned_to: merging_organisation_2.users.first, owning_organisation: merging_organisation_2, managing_organisation: merging_organisation, saledate: Time.zone.yesterday) + end + + it "returns the correct merging_organisations_lettings_logs_outcomes_text text" do + outcome_text = merging_organisations_lettings_logs_outcomes_text(merge_request) + expect(outcome_text).to include("Org 1 users will have access to all lettings logs owned or managed by the merging organisations after the merge.") + expect(outcome_text).to include("Lettings logs that are owned or managed by the merging organisations and have a tenancy start date after the merge date will have their owning or managing organisation changed to Org 1.") + expect(outcome_text).to include("Some logs are owned and managed by different organisations in this merge. They appear in the list for both the owning and the managing organisation.") + expect(outcome_text).not_to include("Org 2 has no lettings logs.") + expect(outcome_text).to include("View all 3 Org 2 lettings logs (opens in a new tab)") + expect(outcome_text).to include("View 1 Org 3 lettings log (opens in a new tab)") + end + + it "returns correct lettings_logs_outcomes_header_text" do + expect(lettings_logs_outcomes_header_text(merge_request)).to eq("4 lettings logs after merge") + end + + it "returns the correct merging_organisations_sales_logs_outcomes_text text" do + outcome_text = merging_organisations_sales_logs_outcomes_text(merge_request) + expect(outcome_text).to include("Org 1 users will have access to all sales logs owned or reported by the merging organisations after the merge.") + expect(outcome_text).to include("Sales logs that are owned or reported by the merging organisations and have a sale completion date after the merge date will have their owning or managing organisation changed to Org 1.") + expect(outcome_text).to include("Some logs are owned and reported by different organisations in this merge. They appear in the list for both the owning and the managing organisation.") + expect(outcome_text).not_to include("Org 2 has no sales logs.") + expect(outcome_text).to include("View all 3 Org 2 sales logs (opens in a new tab)") + expect(outcome_text).to include("View 1 Org 3 sales log (opens in a new tab)") + end + + it "returns correct sales_logs_outcomes_header_text" do + expect(sales_logs_outcomes_header_text(merge_request)).to eq("4 sales logs after merge") + end + end + end + end +end diff --git a/spec/jobs/process_merge_request_job_spec.rb b/spec/jobs/process_merge_request_job_spec.rb new file mode 100644 index 000000000..72ccbdf26 --- /dev/null +++ b/spec/jobs/process_merge_request_job_spec.rb @@ -0,0 +1,65 @@ +require "rails_helper" + +describe ProcessMergeRequestJob do + let(:job) { described_class.new } + let(:merge_organisations_service) { instance_double(Merge::MergeOrganisationsService) } + + before do + allow(Merge::MergeOrganisationsService).to receive(:new).and_return(merge_organisations_service) + allow(merge_organisations_service).to receive(:call).and_return(nil) + end + + context "when processing a merge request" do + let(:organisation) { create(:organisation) } + let(:merging_organisation) { create(:organisation) } + let(:other_merging_organisation) { create(:organisation) } + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: organisation, merge_date: Time.zone.local(2022, 3, 3), total_users: 5, total_schemes: 5, total_lettings_logs: 2, total_sales_logs: 8, total_managing_agents: 2, total_stock_owners: 1, existing_absorbing_organisation: true) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation:) + create(:merge_request_organisation, merge_request:, merging_organisation: other_merging_organisation) + end + + it "calls the merge organisations service with correct arguments" do + expect(Merge::MergeOrganisationsService).to receive(:new).with(absorbing_organisation_id: organisation.id, merging_organisation_ids: [merging_organisation.id, other_merging_organisation.id], merge_date: Time.zone.local(2022, 3, 3), absorbing_organisation_active_from_merge_date: false) + + job.perform(merge_request:) + expect(merge_request.reload.status).to eq("request_merged") + end + + context "with new absorbing organisation" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: organisation, merge_date: Time.zone.local(2022, 3, 3), existing_absorbing_organisation: false) } + + it "calls the merge organisations service with correct arguments" do + expect(Merge::MergeOrganisationsService).to receive(:new).with(absorbing_organisation_id: organisation.id, merging_organisation_ids: [merging_organisation.id, other_merging_organisation.id], merge_date: Time.zone.local(2022, 3, 3), absorbing_organisation_active_from_merge_date: true) + + job.perform(merge_request:) + expect(merge_request.reload.status).to eq("request_merged") + end + end + + it "clears last_failed_attempt value" do + merge_request.update!(last_failed_attempt: Time.zone.now) + job.perform(merge_request:) + + expect(merge_request.reload.last_failed_attempt).to be_nil + end + + it "sets last_failed_attempt value, sets processing to false and clears all outcomes if there's an error" do + allow(merge_organisations_service).to receive(:call).and_raise(ActiveRecord::Rollback) + + expect(merge_request.last_failed_attempt).to be_nil + job.perform(merge_request:) + + merge_request.reload + expect(merge_request.last_failed_attempt).to be_within(10.seconds).of(Time.zone.now) + expect(merge_request.processing).to eq(false) + expect(merge_request.total_users).to be_nil + expect(merge_request.total_schemes).to be_nil + expect(merge_request.total_managing_agents).to be_nil + expect(merge_request.total_stock_owners).to be_nil + expect(merge_request.total_lettings_logs).to be_nil + expect(merge_request.total_sales_logs).to be_nil + end + end +end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb new file mode 100644 index 000000000..0228f71a7 --- /dev/null +++ b/spec/models/merge_request_spec.rb @@ -0,0 +1,451 @@ +require "rails_helper" + +RSpec.describe MergeRequest, type: :model do + describe ".visible" do + let(:open_collection_period_start_date) { 1.year.ago } + let!(:merged_recent) { create(:merge_request, request_merged: true, merge_date: 3.months.ago) } + let!(:merged_old) { create(:merge_request, request_merged: true, merge_date: 18.months.ago) } + let!(:not_merged) { create(:merge_request, request_merged: false) } + + before do + allow(FormHandler.instance).to receive(:start_date_of_earliest_open_collection_period).and_return(open_collection_period_start_date) + end + + it "includes merged requests with merge dates after the open collection period start date" do + expect(described_class.visible).to include(merged_recent) + end + + it "excludes merged requests with merge dates before the open collection period start date" do + expect(described_class.visible).not_to include(merged_old) + end + + it "includes not_merged requests" do + expect(described_class.visible).to include(not_merged) + end + end + + describe "#discard!" do + let(:merge_request) { create(:merge_request) } + + it "sets the discarded_at field" do + merge_request.discard! + expect(merge_request.discarded_at).not_to be_nil + end + + it "does not delete the record" do + merge_request.discard! + expect(merge_request).to be_persisted + end + + it "is not visible in the visible scope" do + merge_request.discard! + expect(described_class.visible).not_to include(merge_request) + end + end + + describe "#status" do + it "returns the correct status for deleted merge request" do + merge_request = build(:merge_request, id: 1, discarded_at: Time.zone.today) + expect(merge_request.status).to eq MergeRequest::STATUS[:deleted] + end + + it "returns the correct status for a merged request" do + merge_request = build(:merge_request, id: 1, request_merged: true) + expect(merge_request.status).to eq MergeRequest::STATUS[:request_merged] + end + + it "returns the correct status for a ready to merge request" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: create(:organisation), merge_date: Time.zone.today, existing_absorbing_organisation: true) + create(:merge_request_organisation, merge_request:) + expect(merge_request.status).to eq MergeRequest::STATUS[:ready_to_merge] + end + + it "returns the correct status for a ready to merge request when existing_absorbing_organisation is false" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: create(:organisation), merge_date: Time.zone.today, existing_absorbing_organisation: false) + create(:merge_request_organisation, merge_request:) + expect(merge_request.status).to eq MergeRequest::STATUS[:ready_to_merge] + end + + it "returns the merge issues if dsa is not signed for absorbing organisation" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: create(:organisation, with_dsa: false), merge_date: Time.zone.today, existing_absorbing_organisation: true) + create(:merge_request_organisation, merge_request:) + expect(merge_request.status).to eq MergeRequest::STATUS[:merge_issues] + end + + it "returns the incomplete if absorbing organisation is missing" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: nil, merge_date: Time.zone.today) + create(:merge_request_organisation, merge_request:) + expect(merge_request.status).to eq MergeRequest::STATUS[:incomplete] + end + + it "returns the incomplete if merge requests organisation is missing" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: create(:organisation), merge_date: Time.zone.today) + expect(merge_request.status).to eq MergeRequest::STATUS[:incomplete] + end + + it "returns the incomplete if merge date is missing" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: create(:organisation)) + create(:merge_request_organisation, merge_request:) + expect(merge_request.status).to eq MergeRequest::STATUS[:incomplete] + end + + it "returns the incomplete if existing absorbing organisation is missing" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: create(:organisation, with_dsa: false), merge_date: Time.zone.today) + create(:merge_request_organisation, merge_request:) + expect(merge_request.status).to eq MergeRequest::STATUS[:incomplete] + end + + it "returns processing if merge is processing" do + merge_request = build(:merge_request, id: 1, absorbing_organisation: create(:organisation), processing: true) + create(:merge_request_organisation, merge_request:) + expect(merge_request.status).to eq MergeRequest::STATUS[:processing] + end + end + + describe "#organisations_with_users" do + context "when absorbing organisation has users" do + let(:merge_request) { create(:merge_request, absorbing_organisation:) } + let(:absorbing_organisation) { create(:organisation) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_1) + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_2) + end + + context "and some merging organisations have users" do + let(:merging_organisation_1) { create(:organisation) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(absorbing_organisation.users.count).to eq(1) + expect(merging_organisation_1.users.count).to eq(1) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_with_users.count).to eq(2) + expect(merge_request.organisations_with_users).to include(merging_organisation_1) + expect(merge_request.organisations_with_users).to include(absorbing_organisation) + end + end + + context "and no merging organisations have users" do + let(:merging_organisation_1) { create(:organisation, with_dsa: false) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(absorbing_organisation.users.count).to eq(1) + expect(merging_organisation_1.users.count).to eq(0) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_with_users.count).to eq(1) + expect(merge_request.organisations_with_users).to include(absorbing_organisation) + end + end + end + + context "when absorbing organisation has no users" do + let(:merge_request) { create(:merge_request, absorbing_organisation:) } + let(:absorbing_organisation) { create(:organisation, with_dsa: false) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_1) + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_2) + end + + context "and some merging organisations have users" do + let(:merging_organisation_1) { create(:organisation) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(merging_organisation_1.users.count).to eq(1) + expect(absorbing_organisation.users.count).to eq(0) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_with_users.count).to eq(1) + expect(merge_request.organisations_with_users).to include(merging_organisation_1) + end + end + + context "and no merging organisations have users" do + let(:merging_organisation_1) { create(:organisation, with_dsa: false) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(absorbing_organisation.users.count).to eq(0) + expect(merging_organisation_1.users.count).to eq(0) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_with_users.count).to eq(0) + end + end + end + end + + describe "#organisations_with_schemes" do + let(:merge_request) { create(:merge_request, absorbing_organisation:) } + let(:absorbing_organisation) { create(:organisation) } + let(:merging_organisation_1) { create(:organisation) } + let(:merging_organisation_2) { create(:organisation) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_1) + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_2) + end + + context "when absorbing organisation has schemes" do + before do + create(:scheme, owning_organisation: absorbing_organisation) + end + + context "and some merging organisations have schemes" do + before do + create(:scheme, owning_organisation: merging_organisation_1) + end + + it "returns correct organisations with schemes" do + expect(absorbing_organisation.owned_schemes.count).to eq(1) + expect(merging_organisation_1.owned_schemes.count).to eq(1) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_with_schemes.count).to eq(2) + expect(merge_request.organisations_with_schemes).to include(merging_organisation_1) + expect(merge_request.organisations_with_schemes).to include(absorbing_organisation) + end + end + + context "and no merging organisations have schemes" do + it "returns correct organisations with schemes" do + expect(absorbing_organisation.owned_schemes.count).to eq(1) + expect(merging_organisation_1.owned_schemes.count).to eq(0) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_with_schemes.count).to eq(1) + expect(merge_request.organisations_with_schemes).to include(absorbing_organisation) + end + end + end + + context "when absorbing organisation has no schemes" do + context "and some merging organisations have schemes" do + before do + create(:scheme, owning_organisation: merging_organisation_1) + end + + it "returns correct organisations with schemes" do + expect(merging_organisation_1.owned_schemes.count).to eq(1) + expect(absorbing_organisation.owned_schemes.count).to eq(0) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_with_schemes.count).to eq(1) + expect(merge_request.organisations_with_schemes).to include(merging_organisation_1) + end + end + + context "and no merging organisations have schemes" do + it "returns correct organisations with schemes" do + expect(absorbing_organisation.owned_schemes.count).to eq(0) + expect(merging_organisation_1.owned_schemes.count).to eq(0) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_with_schemes.count).to eq(0) + end + end + end + end + + describe "#organisations_without_users" do + context "when absorbing organisation has users" do + let(:merge_request) { create(:merge_request, absorbing_organisation:) } + let(:absorbing_organisation) { create(:organisation) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_1) + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_2) + end + + context "and some merging organisations have users" do + let(:merging_organisation_1) { create(:organisation) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(absorbing_organisation.users.count).to eq(1) + expect(merging_organisation_1.users.count).to eq(1) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_without_users.count).to eq(1) + expect(merge_request.organisations_without_users).to include(merging_organisation_2) + end + end + + context "and no merging organisations have users" do + let(:merging_organisation_1) { create(:organisation, with_dsa: false) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(absorbing_organisation.users.count).to eq(1) + expect(merging_organisation_1.users.count).to eq(0) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_without_users.count).to eq(2) + expect(merge_request.organisations_without_users).to include(merging_organisation_1) + expect(merge_request.organisations_without_users).to include(merging_organisation_2) + end + end + end + + context "when absorbing organisation has no users" do + let(:merge_request) { create(:merge_request, absorbing_organisation:) } + let(:absorbing_organisation) { create(:organisation, with_dsa: false) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_1) + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_2) + end + + context "and some merging organisations have users" do + let(:merging_organisation_1) { create(:organisation) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(merging_organisation_1.users.count).to eq(1) + expect(absorbing_organisation.users.count).to eq(0) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_without_users.count).to eq(2) + expect(merge_request.organisations_without_users).to include(absorbing_organisation) + expect(merge_request.organisations_without_users).to include(merging_organisation_2) + end + end + + context "and no merging organisations have users" do + let(:merging_organisation_1) { create(:organisation, with_dsa: false) } + let(:merging_organisation_2) { create(:organisation, with_dsa: false) } + + it "returns correct organisations with users" do + expect(absorbing_organisation.users.count).to eq(0) + expect(merging_organisation_1.users.count).to eq(0) + expect(merging_organisation_2.users.count).to eq(0) + + expect(merge_request.organisations_without_users.count).to eq(3) + expect(merge_request.organisations_without_users).to include(absorbing_organisation) + expect(merge_request.organisations_without_users).to include(merging_organisation_1) + expect(merge_request.organisations_without_users).to include(merging_organisation_2) + end + end + end + end + + describe "#organisations_without_schemes" do + let(:merge_request) { create(:merge_request, absorbing_organisation:) } + let(:absorbing_organisation) { create(:organisation) } + let(:merging_organisation_1) { create(:organisation) } + let(:merging_organisation_2) { create(:organisation) } + + before do + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_1) + create(:merge_request_organisation, merge_request:, merging_organisation: merging_organisation_2) + end + + context "when absorbing organisation has schemes" do + before do + create(:scheme, owning_organisation: absorbing_organisation) + end + + context "and some merging organisations have schemes" do + before do + create(:scheme, owning_organisation: merging_organisation_1) + end + + it "returns correct organisations with schemes" do + expect(absorbing_organisation.owned_schemes.count).to eq(1) + expect(merging_organisation_1.owned_schemes.count).to eq(1) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_without_schemes.count).to eq(1) + expect(merge_request.organisations_without_schemes).to include(merging_organisation_2) + end + end + + context "and no merging organisations have schemes" do + it "returns correct organisations with schemes" do + expect(absorbing_organisation.owned_schemes.count).to eq(1) + expect(merging_organisation_1.owned_schemes.count).to eq(0) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_without_schemes.count).to eq(2) + expect(merge_request.organisations_without_schemes).to include(merging_organisation_1) + expect(merge_request.organisations_without_schemes).to include(merging_organisation_2) + end + end + end + + context "when absorbing organisation has no schemes" do + context "and some merging organisations have schemes" do + before do + create(:scheme, owning_organisation: merging_organisation_1) + end + + it "returns correct organisations with schemes" do + expect(merging_organisation_1.owned_schemes.count).to eq(1) + expect(absorbing_organisation.owned_schemes.count).to eq(0) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_without_schemes.count).to eq(2) + expect(merge_request.organisations_without_schemes).to include(absorbing_organisation) + expect(merge_request.organisations_without_schemes).to include(merging_organisation_2) + end + end + + context "and no merging organisations have schemes" do + it "returns correct organisations with schemes" do + expect(absorbing_organisation.owned_schemes.count).to eq(0) + expect(merging_organisation_1.owned_schemes.count).to eq(0) + expect(merging_organisation_2.owned_schemes.count).to eq(0) + + expect(merge_request.organisations_without_schemes.count).to eq(3) + expect(merge_request.organisations_without_schemes).to include(absorbing_organisation) + expect(merge_request.organisations_without_schemes).to include(merging_organisation_1) + expect(merge_request.organisations_without_schemes).to include(merging_organisation_2) + end + end + end + end + + describe "relationship outcomes" do + let(:stock_owner1) { create(:organisation, name: "Stock owner 1") } + let(:stock_owner2) { create(:organisation, name: "Stock owner 2") } + let(:stock_owner3) { create(:organisation, name: "Stock owner 3") } + let(:managing_agent1) { create(:organisation, name: "Managing agent 1") } + let(:managing_agent2) { create(:organisation, name: "Managing agent 2") } + let(:absorbing_organisation) { create(:organisation, name: "Absorbing Org") } + let(:merging_organisations) { create_list(:organisation, 2) { |org, i| org.name = "Dummy Org #{i + 1}" } } + let(:merge_request) { create(:merge_request, absorbing_organisation:, merging_organisations:) } + + before do + create(:organisation_relationship, child_organisation: absorbing_organisation, parent_organisation: stock_owner1) + create(:organisation_relationship, child_organisation: merging_organisations.first, parent_organisation: stock_owner2) + create(:organisation_relationship, child_organisation: merging_organisations.first, parent_organisation: stock_owner1) + create(:organisation_relationship, child_organisation: merging_organisations.first, parent_organisation: stock_owner3) + create(:organisation_relationship, parent_organisation: absorbing_organisation, child_organisation: managing_agent1) + create(:organisation_relationship, parent_organisation: absorbing_organisation, child_organisation: managing_agent2) + create(:organisation_relationship, parent_organisation: merging_organisations.first, child_organisation: managing_agent2) + end + + describe "#total_stock_owners_after_merge" do + it "returns the correct count of stock owners after merge" do + expect(merge_request.total_stock_owners_after_merge).to eq(3) + end + end + + describe "#total_managing_agents_after_merge" do + it "returns the correct count of managing agents after merge" do + expect(merge_request.total_managing_agents_after_merge).to eq(2) + end + end + + describe "#total_stock_owners_managing_agents_label" do + it "returns the correct label" do + expect(merge_request.total_stock_owners_managing_agents_label).to eq("3 stock owners\n2 managing agents") + end + end + end +end diff --git a/spec/requests/merge_request_spec.rb b/spec/requests/merge_request_spec.rb new file mode 100644 index 000000000..c7302b632 --- /dev/null +++ b/spec/requests/merge_request_spec.rb @@ -0,0 +1,28 @@ +require "rails_helper" + +RSpec.describe MergeRequest, type: :request do + let(:user) { create(:user, :data_coordinator) } + let(:organisation) { user.organisation } + let(:merge_request) { create(:merge_request) } + let(:support_user) { create(:user, :support, organisation:) } + let(:page) { Capybara::Node::Simple.new(response.body) } + + before do + allow(support_user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in support_user + end + + context "when deleting a merge request" do + it "discards the merge request" do + delete delete_merge_request_path(merge_request) + expect(merge_request.reload.discarded_at).not_to be_nil + end + + it "redirects to the merge request list" do + delete delete_merge_request_path(merge_request) + expect(response).to redirect_to(organisations_path(tab: "merge-requests")) + follow_redirect! + expect(page).to have_content("Merge requests") + end + end +end diff --git a/spec/requests/merge_requests_controller_spec.rb b/spec/requests/merge_requests_controller_spec.rb index 6f7b64f16..dc1dd817d 100644 --- a/spec/requests/merge_requests_controller_spec.rb +++ b/spec/requests/merge_requests_controller_spec.rb @@ -10,115 +10,88 @@ RSpec.describe MergeRequestsController, type: :request do let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } let(:other_merge_request) { MergeRequest.create!(requesting_organisation: other_organisation) } - context "when user is signed in with a data coordinator user" do - before { sign_in user } - - describe "#organisations" do - let(:params) { { merge_request: { requesting_organisation_id: "9", status: "unsubmitted" } } } - - context "when creating a new merge request" do - before do - post "/merge-request", headers:, params: - end + context "when user is signed in with a support user" do + before do + allow(support_user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in support_user + end - it "creates merge request with requesting organisation" do - follow_redirect! - expect(page).to have_content("Which organisations are merging?") - expect(page).to have_content(organisation.name) - expect(page).not_to have_link("Remove") - end + context "when creating a new merge request" do + let(:params) { { merge_request: { requesting_organisation_id: support_user.organisation_id } } } - context "when passing a different requesting organisation id" do - let(:params) { { merge_request: { requesting_organisation_id: other_organisation.id, status: "unsubmitted" } } } + before do + post "/merge-request", headers:, params: + end - it "creates merge request with current user organisation" do - follow_redirect! - expect(MergeRequest.count).to eq(1) - expect(MergeRequest.first.requesting_organisation_id).to eq(organisation.id) - expect(MergeRequest.first.merging_organisations.count).to eq(1) - expect(MergeRequest.first.merging_organisations.first.id).to eq(organisation.id) - end - end + it "creates merge request with requesting organisation" do + follow_redirect! + expect(page).to have_content("Which organisation is absorbing the others?") + expect(MergeRequest.first.requesting_organisation_id).to eq(support_user.organisation_id) end - context "when viewing existing merge request" do - before do - get "/merge-request/#{merge_request.id}/organisations", headers:, params: - end + context "when passing a different requesting organisation id" do + let(:params) { { merge_request: { requesting_organisation_id: other_organisation.id } } } - it "shows merge request with requesting organisation" do - expect(page).to have_content("Which organisations are merging?") - expect(page).to have_content(organisation.name) + it "creates merge request with current user organisation" do + follow_redirect! + expect(MergeRequest.count).to eq(1) + expect(MergeRequest.first.requesting_organisation_id).to eq(support_user.organisation_id) + expect(MergeRequest.first.merging_organisations.count).to eq(0) end end + end - context "when viewing existing merge request of a different (unauthorised) organisation" do + describe "#merging-organisations" do + context "when viewing merging organisations page" do before do - get "/merge-request/#{other_merge_request.id}/organisations", headers:, params: + merge_request.update!(absorbing_organisation_id: organisation.id) + get "/merge-request/#{merge_request.id}/merging-organisations", headers: end - it "shows merge request with requesting organisation" do - expect(response).to have_http_status(:not_found) + it "shows the correct content" do + expect(page).to have_content("Which organisations are merging into MHCLG?") end end end - describe "#update_organisations" do - let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + describe "#update_merging_organisations" do + let(:params) { { merge_request: { merging_organisation: other_organisation.id, new_merging_org_ids: [] } } } - context "when updating a merge request with a new organisation" do + context "when updating a merge request with a new merging organisation" do before do - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: end - it "updates the merge request" do + it "adds merging organisation to the page" do merge_request.reload - expect(merge_request.merging_organisations.count).to eq(1) - expect(page).to have_content("Test Org") + expect(page).to have_content("MHCLG") expect(page).to have_content("Other Test Org") expect(page).to have_link("Remove") end end context "when the user selects an organisation that requested another merge" do - let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } - - before do - MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "submitted") - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: - end - - it "does not update the merge request" do - merge_request.reload - expect(merge_request.merging_organisations.count).to eq(0) - expect(response).to have_http_status(:unprocessable_entity) - expect(page).to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) - end - end - - context "when the user selects an organisation that has another non submitted merge" do - let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + let(:params) { { merge_request: { merging_organisation: other_organisation.id, new_merging_org_ids: [] } } } before do - MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "unsubmitted") - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + MergeRequest.create!(requesting_organisation_id: other_organisation.id, request_merged: true) + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: end - it "updates the merge request" do + it "does not error" do merge_request.reload - expect(merge_request.merging_organisations.count).to eq(1) expect(page).not_to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) end end context "when the user selects an organisation that is a part of another merge" do let(:another_organisation) { create(:organisation) } - let(:params) { { merge_request: { merging_organisation: another_organisation.id } } } + let(:params) { { merge_request: { merging_organisation: another_organisation.id, new_merging_org_ids: [] } } } before do - existing_merge_request = MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "submitted") + existing_merge_request = MergeRequest.create!(requesting_organisation_id: other_organisation.id, request_merged: true) MergeRequestOrganisation.create!(merge_request_id: existing_merge_request.id, merging_organisation_id: another_organisation.id) - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: end it "does not update the merge request" do @@ -129,57 +102,42 @@ RSpec.describe MergeRequestsController, type: :request do end end - context "when the user selects an organisation that is a part of another unsubmitted merge" do + context "when the user selects an organisation that is a part of another incomplete merge" do let(:another_organisation) { create(:organisation) } - let(:params) { { merge_request: { merging_organisation: another_organisation.id } } } + let(:params) { { merge_request: { merging_organisation: another_organisation.id, new_merging_org_ids: [] } } } before do - existing_merge_request = MergeRequest.create!(requesting_organisation_id: other_organisation.id, status: "unsubmitted") + existing_merge_request = MergeRequest.create!(requesting_organisation_id: other_organisation.id) MergeRequestOrganisation.create!(merge_request_id: existing_merge_request.id, merging_organisation_id: another_organisation.id) - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: end - it "does not update the merge request" do + it "does not error" do merge_request.reload - expect(merge_request.merging_organisations.count).to eq(1) expect(page).not_to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) end end context "when the user selects an organisation that is a part of current merge" do let(:another_organisation) { create(:organisation) } - let(:params) { { merge_request: { merging_organisation: another_organisation.id } } } + let(:params) { { merge_request: { merging_organisation: another_organisation.id, new_merging_org_ids: [] } } } before do merge_request.merging_organisations << another_organisation - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: - end - - it "does not update the merge request" do - merge_request.reload - expect(merge_request.merging_organisations.count).to eq(1) - end - end - - context "when the user selects an organisation that is requesting this merge" do - let(:params) { { merge_request: { merging_organisation: merge_request.requesting_organisation_id } } } - - before do - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: end it "does not update the merge request" do merge_request.reload - expect(page).not_to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge")) expect(merge_request.merging_organisations.count).to eq(1) end end context "when the user does not select an organisation" do - let(:params) { { merge_request: { merging_organisation: nil } } } + let(:params) { { merge_request: { merging_organisation: nil, new_merging_org_ids: [] } } } before do - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: end it "does not update the merge request" do @@ -191,10 +149,10 @@ RSpec.describe MergeRequestsController, type: :request do end context "when the user selects non existent id" do - let(:params) { { merge_request: { merging_organisation: "clearly_not_an_id" } } } + let(:params) { { merge_request: { merging_organisation: "clearly_not_an_id", new_merging_org_ids: [] } } } before do - patch "/merge-request/#{merge_request.id}/organisations", headers:, params: + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: end it "does not update the merge request" do @@ -212,7 +170,7 @@ RSpec.describe MergeRequestsController, type: :request do context "when removing an organisation from merge request" do before do MergeRequestOrganisation.create!(merge_request_id: merge_request.id, merging_organisation_id: other_organisation.id) - get "/merge-request/#{merge_request.id}/organisations/remove", headers:, params: + get "/merge-request/#{merge_request.id}/merging-organisations/remove", headers:, params: end it "updates the merge request" do @@ -223,7 +181,7 @@ RSpec.describe MergeRequestsController, type: :request do context "when removing an organisation that is not part of a merge from merge request" do before do - get "/merge-request/#{merge_request.id}/organisations/remove", headers:, params: + get "/merge-request/#{merge_request.id}/merging-organisations/remove", headers:, params: end it "does not throw an error" do @@ -233,65 +191,67 @@ RSpec.describe MergeRequestsController, type: :request do end end - describe "#confirm_telephone_number" do - let(:merge_request) do - MergeRequest.create!( - absorbing_organisation: create(:organisation, phone: phone_number), - requesting_organisation: organisation, - ) - end + describe "#absorbing_organisation" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } - before { get "/merge-request/#{merge_request.id}/confirm-telephone-number", headers: } + before { get "/merge-request/#{merge_request.id}/absorbing-organisation", headers: } - context "when org has phone number" do - let(:phone_number) { 123 } + it "asks for the absorbing organisation" do + expect(page).to have_content("Which organisation is absorbing the others?") + expect(page).to have_content("Select organisation name") + end - it "asks to confirm or provide new number" do - expect(page).to have_content("This telephone number is correct") - expect(page).to have_content("Confirm the telephone number on file, or enter a new one.") - expect(page).to have_content(phone_number) - expect(page).to have_content("What is #{merge_request.absorbing_organisation.name}'s telephone number?") - end + it "has the correct back button" do + expect(page).to have_link("Back", href: organisations_path(tab: "merge-requests")) end + end - context "when org does not have a phone number set" do - let(:phone_number) { nil } + describe "#merge_date" do + context "when viewing merge date page" do + before do + merge_request.update!(absorbing_organisation_id: organisation.id) + get "/merge-request/#{merge_request.id}/merge-date", headers: + end - it "asks provide new number" do - expect(page).not_to have_content("This telephone number is correct") - expect(page).not_to have_content("Confirm the telephone number on file, or enter a new one.") - expect(page).to have_content("What is #{merge_request.absorbing_organisation.name}'s telephone number?") + it "shows the correct content" do + expect(page).to have_content("What is the merge date?") end end end - describe "#update" do - before { sign_in user } - - describe "#other_merging_organisations" do - let(:other_merging_organisations) { "A list of other merging organisations" } - let(:params) { { merge_request: { other_merging_organisations:, page: "organisations" } } } - let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: + describe "#existing_absorbing_organisation" do + context "when viewing helpdesk ticket page" do + before do + merge_request.update!(absorbing_organisation_id: organisation.id, merge_date: Time.zone.today) + get "/merge-request/#{merge_request.id}/existing-absorbing-organisation", headers: end - context "when adding other merging organisations" do - before do - MergeRequestOrganisation.create!(merge_request_id: merge_request.id, merging_organisation_id: other_organisation.id) - end - - it "updates the merge request" do - expect { request }.to change { merge_request.reload.other_merging_organisations }.from(nil).to(other_merging_organisations) - end + it "shows the correct content" do + expect(page).to have_content("Was #{merge_request.absorbing_organisation.name} already active before the merge date?") + expect(page).to have_content("Yes, this organisation existed before the merge") + expect(page).to have_content("No, it is a new organisation created by this merge") + expect(page).to have_link("Back", href: merge_date_merge_request_path(merge_request)) + expect(page).to have_button("Save and continue") + end + end + end - it "redirects telephone number path" do - request + describe "#helpdesk_ticket" do + context "when viewing helpdesk ticket page" do + before do + merge_request.update!(absorbing_organisation_id: organisation.id, merge_date: Time.zone.today) + get "/merge-request/#{merge_request.id}/helpdesk-ticket", headers: + end - expect(response).to redirect_to(absorbing_organisation_merge_request_path(merge_request)) - end + it "shows the correct content" do + expect(page).to have_content("Which helpdesk ticket reported this merge?") + expect(page).to have_link("Back", href: existing_absorbing_organisation_merge_request_path(merge_request)) + expect(page).to have_button("Save and continue") end end + end + describe "#update" do describe "from absorbing_organisation page" do context "when not answering the question" do let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: other_organisation) } @@ -305,7 +265,7 @@ RSpec.describe MergeRequestsController, type: :request do it "renders the error" do request - expect(page).to have_content("Select the organisation absorbing the others") + expect(page).to have_content("Select the absorbing organisation") end it "does not update the request" do @@ -313,32 +273,8 @@ RSpec.describe MergeRequestsController, type: :request do end end - context "when absorbing_organisation_id set to other" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: other_organisation) } - let(:params) do - { merge_request: { absorbing_organisation_id: "other", page: "absorbing_organisation" } } - end - let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: - end - - it "redirects to new org path" do - request - - expect(response).to redirect_to(new_organisation_name_merge_request_path(merge_request)) - end - - it "resets absorbing_organisation and sets new_absorbing_organisation to true" do - expect { request }.to change { - merge_request.reload.absorbing_organisation - }.from(other_organisation).to(nil).and change { - merge_request.reload.new_absorbing_organisation - }.from(nil).to(true) - end - end - context "when absorbing_organisation_id set to id" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, new_absorbing_organisation: true) } + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } let(:params) do { merge_request: { absorbing_organisation_id: other_organisation.id, page: "absorbing_organisation" } } end @@ -347,75 +283,62 @@ RSpec.describe MergeRequestsController, type: :request do patch "/merge-request/#{merge_request.id}", headers:, params: end - it "redirects telephone number path" do + it "redirects to merging organisations path" do request - expect(response).to redirect_to(confirm_telephone_number_merge_request_path(merge_request)) + expect(response).to redirect_to(merging_organisations_merge_request_path(merge_request)) end - it "updates absorbing_organisation_id and sets new_absorbing_organisation to false" do + it "updates absorbing_organisation_id" do expect { request }.to change { merge_request.reload.absorbing_organisation - }.from(nil).to(other_organisation).and change { - merge_request.reload.new_absorbing_organisation - }.from(true).to(false) + }.from(nil).to(other_organisation) end end - end - describe "from confirm_telephone_number page" do - context "when confirming the number" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, new_absorbing_organisation: true, new_telephone_number: "123") } + context "when updating from check_answers page" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } let(:params) do - { merge_request: { telephone_number_correct: true, page: "confirm_telephone_number" } } + { merge_request: { absorbing_organisation_id: "", page: "absorbing_organisation" } } end let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: + patch "/merge-request/#{merge_request.id}?referrer=check_answers", headers:, params: end - it "redirects telephone number path" do + it "keeps corrent links if validation fails" do request - expect(response).to redirect_to(merge_date_merge_request_path(merge_request)) - end - - it "updates telephone_number_correct and sets new_telephone_number to nil" do - expect { request }.to change { - merge_request.reload.telephone_number_correct - }.from(nil).to(true).and change { - merge_request.reload.new_telephone_number - }.from("123").to(nil) + expect(page).to have_link("Cancel", href: merge_request_path(merge_request)) + expect(page).to have_button("Save changes") end end - context "when setting new number" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, new_absorbing_organisation: true) } + context "when absorbing_organisation_id set to one of the merging organisations" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } let(:params) do - { merge_request: { telephone_number_correct: false, new_telephone_number: "123", page: "confirm_telephone_number" } } + { merge_request: { absorbing_organisation_id: other_organisation.id, page: "absorbing_organisation" } } end let(:request) do + MergeRequestOrganisation.create!(merge_request_id: merge_request.id, merging_organisation_id: other_organisation.id) patch "/merge-request/#{merge_request.id}", headers:, params: end - it "redirects telephone number path" do + it "removes organisation from merge request organisations" do request - expect(response).to redirect_to(merge_date_merge_request_path(merge_request)) - end - - it "updates telephone_number_correct and sets new_telephone_number to nil" do - expect { request }.to change { - merge_request.reload.new_telephone_number - }.from(nil).to("123") + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(0) end end + end - context "when not answering the question and the org has phone number" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: create(:organisation, phone: "123")) } + describe "from merge_date page" do + context "when not answering the question" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: other_organisation) } let(:params) do - { merge_request: { page: "confirm_telephone_number" } } + { merge_request: { page: "merge_date" } } end let(:request) do patch "/merge-request/#{merge_request.id}", headers:, params: @@ -424,7 +347,8 @@ RSpec.describe MergeRequestsController, type: :request do it "renders the error" do request - expect(page).to have_content("Select to confirm or enter a new telephone number") + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content("Enter a merge date") end it "does not update the request" do @@ -432,285 +356,410 @@ RSpec.describe MergeRequestsController, type: :request do end end - context "when not answering the question and the org does not have a phone number" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: other_organisation) } + context "when merge date set to an invalid date" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } let(:params) do - { merge_request: { page: "confirm_telephone_number" } } + { merge_request: { page: "merge_date", "merge_date(3i)": "10", "merge_date(2i)": "44", "merge_date(1i)": "2022" } } end + let(:request) do patch "/merge-request/#{merge_request.id}", headers:, params: end - it "renders the error" do + it "displays the page with an error message" do request - expect(page).to have_content("Enter a valid telephone number") - end - - it "does not update the request" do - expect { request }.not_to(change { merge_request.reload.attributes }) + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content("Enter a valid merge date") end end - context "when not answering the phone number" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: other_organisation) } + context "when merge date set to a valid date" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } let(:params) do - { merge_request: { page: "confirm_telephone_number", telephone_number_correct: false } } + { merge_request: { page: "merge_date", "merge_date(3i)": "10", "merge_date(2i)": "4", "merge_date(1i)": "2022" } } end + let(:request) do patch "/merge-request/#{merge_request.id}", headers:, params: end - it "renders the error" do + it "redirects to existing absorbing organisation path" do request - expect(page).to have_content("Enter a valid telephone number") + expect(response).to redirect_to(existing_absorbing_organisation_merge_request_path(merge_request)) end - it "does not update the request" do - expect { request }.not_to(change { merge_request.reload.attributes }) + it "updates merge_date" do + expect { request }.to change { + merge_request.reload.merge_date + }.from(nil).to(Time.zone.local(2022, 4, 10)) end end end - describe "#new_organisation_name" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, new_absorbing_organisation: true) } - - context "when viewing the new organisation name page" do - before do - get "/merge-request/#{merge_request.id}/new-organisation-name", headers: - end - - it "displays the correct question" do - expect(page).to have_content("What is the new organisation called?") - end - - it "has the correct back button" do - expect(page).to have_link("Back", href: absorbing_organisation_merge_request_path(merge_request)) - end - end - - context "when updating the new organisation name" do + describe "from merging_organisations page" do + context "when the user updates merge request with valid merging organisation ID" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } + let(:another_organisation) { create(:organisation) } let(:params) do - { merge_request: { new_organisation_name: "new org name", page: "new_organisation_name" } } + { merge_request: { page: "merging_organisations", new_merging_org_ids: another_organisation.id } } end let(:request) do patch "/merge-request/#{merge_request.id}", headers:, params: end - it "redirects to new organisation address path" do + it "updates the merge request" do request - expect(response).to redirect_to(new_organisation_address_merge_request_path(merge_request)) - end - it "updates new organisation name to the correct name" do - expect { request }.to change { - merge_request.reload.new_organisation_name - }.from(nil).to("new org name") + merge_request.reload + expect(merge_request.merging_organisations.count).to eq(1) + expect(merge_request.merging_organisations.first.id).to eq(another_organisation.id) end end + end - context "when the new organisation name is not answered" do + describe "from existing_absorbing_organisation page" do + context "when not answering the question" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: other_organisation) } let(:params) do - { merge_request: { new_organisation_name: nil, page: "new_organisation_name" } } + { merge_request: { page: "existing_absorbing_organisation" } } end - let(:request) do patch "/merge-request/#{merge_request.id}", headers:, params: end it "renders the error" do request - expect(page).to have_content("Enter an organisation name") + + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content("You must answer absorbing organisation already active?") end - it "does not update the organisation name" do + it "does not update the request" do expect { request }.not_to(change { merge_request.reload.attributes }) end end + end + end - context "when the new organisation name already exists" do - before do - create(:organisation, name: "new org name") - end + describe "#merge_start_confirmation" do + before do + get "/merge-request/#{merge_request.id}/merge-start-confirmation", headers: + end - let(:params) do - { merge_request: { new_organisation_name: "New org name", page: "new_organisation_name" } } - end + it "has correct content" do + expect(page).to have_content("Are you sure you want to begin this merge?") + expect(page).to have_content("You will not be able to undo this action") + expect(page).to have_link("Back", href: merge_request_path(merge_request)) + expect(page).to have_button("Begin merge") + end + end - let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: - end + describe "#start_merge" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, absorbing_organisation: organisation, merge_date: Time.zone.local(2022, 3, 3), existing_absorbing_organisation: true) } + let(:merging_organisation) { create(:organisation, name: "Merging Test Org") } - it "renders the error" do - request - expect(page).to have_content("An organisation with this name already exists") - end + before do + allow(ProcessMergeRequestJob).to receive(:perform_later).and_return(nil) + end - it "does not update the organisation name" do - expect { request }.not_to(change { merge_request.reload.attributes }) - end + context "when merge request is ready to merge" do + before do + create(:merge_request_organisation, merge_request:, merging_organisation: other_organisation) + create(:merge_request_organisation, merge_request:, merging_organisation:) + create_list(:scheme, 2, owning_organisation: organisation) + create(:lettings_log, owning_organisation: organisation) + create(:sales_log, owning_organisation: organisation) + end + + it "runs the job with correct merge request" do + expect(merge_request.reload.status).to eq("ready_to_merge") + expect(ProcessMergeRequestJob).to receive(:perform_later).with(merge_request:).once + patch "/merge-request/#{merge_request.id}/start-merge" + expect(merge_request.reload.status).to eq("processing") + expect(merge_request.total_users).to eq(5) + expect(merge_request.total_schemes).to eq(2) + expect(merge_request.total_stock_owners).to eq(0) + expect(merge_request.total_managing_agents).to eq(0) + expect(merge_request.total_lettings_logs).to eq(1) + expect(merge_request.total_sales_logs).to eq(1) end end - describe "#new_organisation_address" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, new_organisation_name: "New name", new_absorbing_organisation: true) } - - context "when viewing the new organisation name page" do - before do - get "/merge-request/#{merge_request.id}/new-organisation-address", headers: - end + context "when merge request is not ready to merge" do + it "does not run the job" do + expect(merge_request.status).to eq("incomplete") + expect(ProcessMergeRequestJob).not_to receive(:perform_later).with(merge_request:) + patch "/merge-request/#{merge_request.id}/start-merge" + expect(merge_request.reload.status).to eq("incomplete") + end + end + end - it "displays the correct question" do - expect(page).to have_content("What is New name’s address?") - end + describe "#show" do + before do + create(:merge_request_organisation, merge_request:, merging_organisation: other_organisation) + create_list(:scheme, 2, owning_organisation: organisation) + create_list(:scheme, 2, owning_organisation: other_organisation) + create(:lettings_log, owning_organisation: organisation) + create(:lettings_log, owning_organisation: other_organisation) + create_list(:sales_log, 2, owning_organisation: other_organisation) + create(:sales_log, owning_organisation: organisation) + get "/merge-request/#{merge_request.id}", headers: + end - it "has the correct back button" do - expect(page).to have_link("Back", href: new_organisation_name_merge_request_path(merge_request)) - end + context "when request has previously failed" do + let(:merge_request) { create(:merge_request, last_failed_attempt: Time.zone.yesterday) } - it "has a skip link" do - expect(page).to have_link("Skip for now", href: new_organisation_telephone_number_merge_request_path(merge_request)) - end + it "shows a banner" do + expect(page).to have_content("An error occurred while processing the merge.") + expect(page).to have_content("No changes have been made. Try beginning the merge again.") end + end - context "when updating the new organisation address" do - let(:params) do - { merge_request: { - new_organisation_address_line1: "first address line", - new_organisation_address_line2: "second address line", - new_organisation_postcode: "new postcode", - page: "new_organisation_address", - } } - end + context "when request has not previously failed" do + let(:merge_request) { create(:merge_request, last_failed_attempt: nil) } - let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: - end + it "does not show a banner" do + expect(page).not_to have_content("An error occurred while processing the merge.") + expect(page).not_to have_content("No changes have been made. Try beginning the merge again.") + end + end - it "redirects to new organisation telephone path" do - request - expect(response).to redirect_to(new_organisation_telephone_number_merge_request_path(merge_request)) - end + it "has begin merge button" do + expect(page).to have_link("Begin merge", href: merge_start_confirmation_merge_request_path(merge_request)) + end - it "updates new organisation address line 1 to correct address line" do - expect { request }.to change { - merge_request.reload.new_organisation_address_line1 - }.from(nil).to("first address line") - end + context "with unmerged request" do + let(:merge_request) { create(:merge_request, absorbing_organisation_id: organisation.id, merge_date: Time.zone.today, existing_absorbing_organisation: true) } + + it "shows outcomes count and has links to view merge outcomes" do + expect(page).to have_link("View", href: user_outcomes_merge_request_path(merge_request)) + expect(page).to have_link("View", href: scheme_outcomes_merge_request_path(merge_request)) + expect(page).to have_link("View", href: relationship_outcomes_merge_request_path(merge_request)) + expect(page).to have_link("View", href: logs_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("4 users") + expect(page).to have_content("4 schemes") + expect(page).to have_content("0 stock owners") + expect(page).to have_content("0 managing agents") + expect(page).to have_content("2 lettings logs") + expect(page).to have_content("3 sales logs") + end + end - it "updates new organisation address line 2 to correct address line" do - expect { request }.to change { - merge_request.reload.new_organisation_address_line2 - }.from(nil).to("second address line") - end + context "with a merged request" do + let(:merge_request) { create(:merge_request, request_merged: true, total_users: 34, total_schemes: 12, total_stock_owners: 8, total_managing_agents: 5, total_lettings_logs: 4, total_sales_logs: 5) } - it "updates new organisation postcode to correct address line" do - expect { request }.to change { - merge_request.reload.new_organisation_postcode - }.from(nil).to("new postcode") - end + it "shows saved users count and doesn't have links to view merge outcomes" do + expect(merge_request.status).to eq("request_merged") + expect(page).not_to have_link("View", href: user_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("34 users") end - context "when address is not provided" do - let(:params) do - { merge_request: { - new_organisation_address_line1: nil, - new_organisation_address_line2: nil, - new_organisation_postcode: nil, - page: "new_organisation_address", - } } - end + it "shows saved schemes count and doesn't have links to view merge outcomes" do + expect(merge_request.status).to eq("request_merged") + expect(page).not_to have_link("View", href: scheme_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("12 schemes") + end - let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: - end + it "shows stock owners and managing agents count and doesn't have links to view merge outcomes" do + expect(merge_request.status).to eq("request_merged") + expect(page).not_to have_link("View", href: relationship_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("8 stock owners") + expect(page).to have_content("5 managing agents") + end - it "does not throw an error" do - request - expect(response).to redirect_to(new_organisation_telephone_number_merge_request_path(merge_request)) - end + it "shows logs counts and doesn't have links to view merge outcomes" do + expect(merge_request.status).to eq("request_merged") + expect(page).not_to have_link("View", href: logs_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("4 lettings logs") + expect(page).to have_content("5 sales logs") end end - describe "#new_organisation_telephone_number" do - let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, new_organisation_name: "New name", new_absorbing_organisation: true) } + context "with a processing request" do + let(:merge_request) { create(:merge_request, processing: true, total_users: 51, total_schemes: 33, total_stock_owners: 15, total_managing_agents: 20) } - context "when viewing the new organisation telephone number page" do - before do - get "/merge-request/#{merge_request.id}/new-organisation-telephone-number", headers: - end + it "shows saved users count and doesn't have links to view merge outcomes" do + expect(merge_request.status).to eq("processing") + expect(page).not_to have_link("View", href: user_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("51 users") + end - it "displays the correct question" do - expect(page).to have_content("What is New name’s telephone number?") - end + it "shows saved schemes count and doesn't have links to view merge outcomes" do + expect(merge_request.status).to eq("processing") + expect(page).not_to have_link("View", href: scheme_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("33 schemes") + end - it "has the correct back button" do - expect(page).to have_link("Back", href: new_organisation_address_merge_request_path(merge_request)) - end + it "shows stock owners and managing agents count and doesn't have links to view merge outcomes" do + expect(merge_request.status).to eq("processing") + expect(page).not_to have_link("View", href: relationship_outcomes_merge_request_path(merge_request)) + expect(page).to have_content("15 stock owners") + expect(page).to have_content("20 managing agents") end + end + end - context "when updating the new organisation telephone number" do - let(:params) do - { merge_request: { new_organisation_telephone_number: "1234", page: "new_organisation_telephone_number" } } - end + describe "#user_outcomes" do + let(:merge_request) { create(:merge_request, absorbing_organisation: organisation) } + let(:organisation_with_no_users) { create(:organisation, name: "Organisation with no users", with_dsa: false) } + let(:organisation_with_no_users_too) { create(:organisation, name: "Organisation with no users too", with_dsa: false) } + let(:organisation_with_some_users) { create(:organisation, name: "Organisation with some users", with_dsa: false) } + let(:organisation_with_some_more_users) { create(:organisation, name: "Organisation with many users", with_dsa: false) } - let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: - end + before do + create_list(:user, 4, organisation: organisation_with_some_users) + create_list(:user, 12, organisation: organisation_with_some_more_users) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_no_users) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_no_users_too) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_some_users) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_some_more_users) + get "/merge-request/#{merge_request.id}/user-outcomes", headers: + end - it "redirects to new organisation type path" do - request - expect(response).to redirect_to(new_organisation_type_merge_request_path(merge_request)) - end + it "shows user outcomes after merge" do + expect(page).to have_link("View all 4 Organisation with some users users (opens in a new tab)", href: users_organisation_path(organisation_with_some_users)) + expect(page).to have_link("View all 12 Organisation with many users users (opens in a new tab)", href: users_organisation_path(organisation_with_some_more_users)) + expect(page).to have_link("View all 3 MHCLG users (opens in a new tab)", href: users_organisation_path(organisation)) + expect(page).to have_content("Organisation with no users and Organisation with no users too have no users.") + expect(page).to have_content("19 users after merge") + end + end - it "updates new organisation name to the correct telephone number" do - expect { request }.to change { - merge_request.reload.new_organisation_telephone_number - }.from(nil).to("1234") - end - end + describe "#scheme_outcomes" do + let(:merge_request) { create(:merge_request, absorbing_organisation: organisation) } + let(:organisation_with_no_schemes) { create(:organisation, name: "Organisation with no schemes") } + let(:organisation_with_no_schemes_too) { create(:organisation, name: "Organisation with no schemes too") } + let(:organisation_with_some_schemes) { create(:organisation, name: "Organisation with some schemes") } + let(:organisation_with_some_more_schemes) { create(:organisation, name: "Organisation with many schemes") } - context "when the new organisation telephone number is not answered" do - let(:params) do - { merge_request: { new_organisation_telephone_number: nil, page: "new_organisation_telephone_number" } } - end + before do + create_list(:scheme, 4, owning_organisation: organisation_with_some_schemes) + create_list(:scheme, 6, owning_organisation: organisation_with_some_more_schemes) + create_list(:scheme, 3, owning_organisation: organisation) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_no_schemes) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_no_schemes_too) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_some_schemes) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_some_more_schemes) + get "/merge-request/#{merge_request.id}/scheme-outcomes", headers: + end - let(:request) do - patch "/merge-request/#{merge_request.id}", headers:, params: - end + it "shows scheme outcomes after merge" do + expect(page).to have_link("View all 4 Organisation with some schemes schemes (opens in a new tab)", href: schemes_organisation_path(organisation_with_some_schemes)) + expect(page).to have_link("View all 6 Organisation with many schemes schemes (opens in a new tab)", href: schemes_organisation_path(organisation_with_some_more_schemes)) + expect(page).to have_link("View all 3 MHCLG schemes (opens in a new tab)", href: schemes_organisation_path(organisation)) + expect(page).to have_content("Organisation with no schemes and Organisation with no schemes too have no schemes.") + expect(page).to have_content("13 schemes after merge") + end + end - it "renders the error" do - request - expect(page).to have_content("Enter a valid telephone number") - end + describe "#logs_outcomes" do + let(:merge_request) { create(:merge_request, absorbing_organisation: organisation) } + let(:organisation_with_no_logs) { create(:organisation, name: "Organisation with no logs") } + let(:organisation_with_no_logs_too) { create(:organisation, name: "Organisation with no logs too") } + let(:organisation_with_some_logs) { create(:organisation, name: "Organisation with some logs") } - it "does not update the organisation telephone number" do - expect { request }.not_to(change { merge_request.reload.attributes }) - end - end + before do + create_list(:lettings_log, 4, owning_organisation: organisation_with_some_logs) + create_list(:sales_log, 2, owning_organisation: organisation_with_some_logs) + create_list(:lettings_log, 2, owning_organisation: organisation) + create_list(:sales_log, 3, owning_organisation: organisation) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_no_logs) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_no_logs_too) + create(:merge_request_organisation, merge_request:, merging_organisation: organisation_with_some_logs) + get "/merge-request/#{merge_request.id}/logs-outcomes", headers: + end + + it "shows logs outcomes after merge" do + expect(page).to have_link("View all 4 Organisation with some logs lettings logs (opens in a new tab)", href: lettings_logs_organisation_path(organisation_with_some_logs)) + expect(page).to have_link("View all 2 Organisation with some logs sales logs (opens in a new tab)", href: sales_logs_organisation_path(organisation_with_some_logs)) + expect(page).to have_link("View all 2 MHCLG lettings logs (opens in a new tab)", href: lettings_logs_organisation_path(organisation)) + expect(page).to have_link("View all 3 MHCLG sales logs (opens in a new tab)", href: sales_logs_organisation_path(organisation)) + expect(page).to have_content("Organisation with no logs and Organisation with no logs too have no lettings logs.") + expect(page).to have_content("Organisation with no logs and Organisation with no logs too have no sales logs.") + expect(page).to have_content("6 lettings logs after merge") + expect(page).to have_content("5 sales logs after merge") end end end - context "when user is signed in as a support user" do + context "when user is signed in with a data coordinator user" do before do - allow(support_user).to receive(:need_two_factor_authentication?).and_return(false) - sign_in support_user + sign_in user end - describe "#organisations" do - let(:params) { { merge_request: { requesting_organisation_id: other_organisation.id, status: "unsubmitted" } } } + describe "#merging_organisations" do + let(:params) { { merge_request: { requesting_organisation_id: other_organisation.id } } } - before do - post "/merge-request", headers:, params: + context "when creating a new merge request" do + before do + post "/merge-request", headers:, params: + end + + it "does not allow creating a new merge request" do + expect(response).to have_http_status(:not_found) + end end - it "creates merge request with requesting organisation" do - follow_redirect! - expect(MergeRequest.count).to eq(1) - expect(MergeRequest.first.requesting_organisation_id).to eq(other_organisation.id) + context "when viewing existing merge request" do + before do + get "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: + end + + it "does not allow viewing a merge request" do + expect(response).to have_http_status(:not_found) + end + end + end + + describe "#update_merging_organisations" do + let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + + context "when updating a merge request with a new organisation" do + before do + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: + end + + it "does not allow updaing a merge request" do + expect(response).to have_http_status(:not_found) + end + end + end + + describe "#remove_merging_organisation" do + let(:params) { { merge_request: { merging_organisation: other_organisation.id } } } + + context "when removing an organisation from merge request" do + before do + MergeRequestOrganisation.create!(merge_request_id: merge_request.id, merging_organisation_id: other_organisation.id) + get "/merge-request/#{merge_request.id}/merging-organisations/remove", headers:, params: + end + + it "does not allow removing an organisation" do + expect(response).to have_http_status(:not_found) + end + end + end + + describe "#update" do + describe "from absorbing_organisation page" do + context "when absorbing_organisation_id set to id" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } + let(:params) do + { merge_request: { absorbing_organisation_id: other_organisation.id, page: "absorbing_organisation" } } + end + + before do + patch "/merge-request/#{merge_request.id}", headers:, params: + end + + it "does not allow updating absorbing organisation" do + expect(response).to have_http_status(:not_found) + end + end end end end diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index cb74ca3ab..91dc07094 100644 --- a/spec/requests/organisations_controller_spec.rb +++ b/spec/requests/organisations_controller_spec.rb @@ -1043,6 +1043,19 @@ RSpec.describe OrganisationsController, type: :request do expect(page).to have_field("search", type: "search") end + it "shows the merge request list" do + expect(page).to have_content("Merge requests") + end + + it "has a create new merge request button" do + expect(page).to have_button("Create new merge request") + end + + it "displays 'No merge requests' when @merge_requests is empty" do + allow(MergeRequest).to receive(:visible).and_return(nil) + expect(page).to have_content("No merge requests") + end + context "when viewing a specific organisation's lettings logs" do let(:parent_organisation) { create(:organisation) } let(:child_organisation) { create(:organisation) } diff --git a/spec/views/merge_requests/show.html.erb_spec.rb b/spec/views/merge_requests/show.html.erb_spec.rb new file mode 100644 index 000000000..d03239d2a --- /dev/null +++ b/spec/views/merge_requests/show.html.erb_spec.rb @@ -0,0 +1,93 @@ +require "rails_helper" + +RSpec.describe "merge_requests/show.html.erb", type: :view do + let(:absorbing_organisation) { create(:organisation, name: "Absorbing Org", with_dsa: false) } + let(:dpo_user) { create(:user, name: "DPO User", is_dpo: true, organisation: absorbing_organisation) } + let(:merge_request) { create(:merge_request, absorbing_organisation_id: absorbing_organisation.id, signed_dsa: false) } + + before do + assign(:merge_request, merge_request) + render + end + + it "displays the correct title" do + expect(rendered).to have_selector("h1.govuk-heading-l") do |h1| + expect(h1).to have_selector("span.govuk-caption-l", text: "Merge request") + expect(h1).to have_content("Absorbing Org") + end + end + + it "displays the notification banner when DSA is not signed" do + expect(rendered).to have_selector(".govuk-notification-banner") + expect(rendered).to have_content("The absorbing organisation must accept the Data Sharing Agreement before merging.") + end + + it "displays the requester details" do + expect(rendered).to have_selector("dt", text: "Requester") + expect(rendered).to have_selector("dd", text: merge_request.requester&.name || "You didn't answer this question") + end + + it "displays the helpdesk ticket details" do + expect(rendered).to have_selector("dt", text: "Helpdesk ticket") + if merge_request.helpdesk_ticket.present? + expect(rendered).to have_link(merge_request.helpdesk_ticket, href: "https://dluhcdigital.atlassian.net/browse/#{merge_request.helpdesk_ticket}") + else + expect(rendered).to have_selector("dd", text: "You didn't answer this question") + end + end + + it "displays the status details" do + expect(rendered).to have_selector("dt", text: "Status") + expect(rendered).to have_selector("dd", text: "Incomplete") + end + + it "displays the absorbing organisation details" do + expect(rendered).to have_selector("dt", text: "Absorbing organisation") + expect(rendered).to have_selector("dd", text: merge_request.absorbing_organisation_name) + end + + it "displays the merge date details" do + expect(rendered).to have_selector("dt", text: "Merge date") + expect(rendered).to have_selector("dd", text: merge_request.merge_date || "You didn't answer this question") + end + + context "when the merge request is complete" do + before do + merge_request.update!(request_merged: true, signed_dsa: true, total_users: 10, total_schemes: 5, total_lettings_logs: 20, total_sales_logs: 30, total_stock_owners: 40, total_managing_agents: 50) + assign(:merge_request, merge_request) + render + end + + it "has status of 'Merged'" do + expect(rendered).to have_selector("dd", text: "Merged") + end + + it "displays the total users after merge details" do + expect(rendered).to have_selector("dt", text: "Total users after merge") + expect(rendered).to have_selector("dd", text: merge_request.total_users) + end + + it "displays the total schemes after merge details" do + expect(rendered).to have_selector("dt", text: "Total schemes after merge") + expect(rendered).to have_selector("dd", text: merge_request.total_schemes) + end + + it "displays the total logs after merge details" do + expect(rendered).to have_selector("dt", text: "Total logs after merge") + if merge_request.total_lettings_logs.present? || merge_request.total_sales_logs.present? + combined_text = [] + combined_text << "#{merge_request.total_lettings_logs} lettings logs" if merge_request.total_lettings_logs.present? + combined_text << "#{merge_request.total_sales_logs} sales logs" if merge_request.total_sales_logs.present? + expect(rendered).to have_selector("dd", text: combined_text.join("")) + end + end + + it "displays the total stock owners & managing agents after merge details" do + expect(rendered).to have_selector("dt", text: "Total stock owners & managing agents after merge") + combined_text = [] + combined_text << "#{merge_request.total_stock_owners} stock owners" if merge_request.total_stock_owners.present? + combined_text << "#{merge_request.total_managing_agents} managing agents" if merge_request.total_managing_agents.present? + expect(rendered).to have_selector("dd", text: combined_text.join("\n")) + end + end +end