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? %>
+
+ 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 %>
+
-
-
- 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