From 9abde36f944e15c1d1c175b54a3e80d6ab5aa529 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Wed, 2 Aug 2023 12:36:33 +0100 Subject: [PATCH] CLDC-2570 Add merge organisations service and rake task (#1801) * Create merge_organisations_service * Refactor tests * update schemes merge * rename scope * merge letings logs * Merge sales logs * refactor merge organisations service * Rollback transaction if there's an error merging orgs * Add logging * add merge_organisations rake task * mark organisations as merged * update rake task * Update rake task add tests for multiple merging orgs * Add more tests for merging relationships * Update merge logging * set absorbing organisation relationship --- app/controllers/locations_controller.rb | 4 +- app/controllers/schemes_controller.rb | 4 +- app/models/lettings_log.rb | 2 +- app/models/sales_log.rb | 1 + .../merge/merge_organisations_service.rb | 131 +++++++ db/schema.rb | 2 +- lib/tasks/merge_organisations.rake | 12 + spec/lib/tasks/merge_organisations_spec.rb | 41 +++ .../merge/merge_organisations_service_spec.rb | 323 ++++++++++++++++++ 9 files changed, 514 insertions(+), 6 deletions(-) create mode 100644 app/services/merge/merge_organisations_service.rb create mode 100644 lib/tasks/merge_organisations.rake create mode 100644 spec/lib/tasks/merge_organisations_spec.rb create mode 100644 spec/services/merge/merge_organisations_service_spec.rb diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb index f89325b41..83e90c373 100644 --- a/app/controllers/locations_controller.rb +++ b/app/controllers/locations_controller.rb @@ -173,7 +173,7 @@ class LocationsController < ApplicationController end def deactivate_confirm - @affected_logs = @location.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date]) + @affected_logs = @location.lettings_logs.visible.after_date(params[:deactivation_date]) if @affected_logs.count.zero? deactivate else @@ -274,7 +274,7 @@ private end def reset_location_and_scheme_for_logs! - logs = @location.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date].to_time) + logs = @location.lettings_logs.visible.after_date(params[:deactivation_date].to_time) logs.update!(location: nil, scheme: nil, unresolved: true) logs end diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index a433832b5..e026a70b9 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -51,7 +51,7 @@ class SchemesController < ApplicationController end def deactivate_confirm - @affected_logs = @scheme.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date]) + @affected_logs = @scheme.lettings_logs.visible.after_date(params[:deactivation_date]) if @affected_logs.count.zero? deactivate else @@ -335,7 +335,7 @@ private end def reset_location_and_scheme_for_logs! - logs = @scheme.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date].to_time) + logs = @scheme.lettings_logs.visible.after_date(params[:deactivation_date].to_time) logs.update!(location: nil, scheme: nil, unresolved: true) logs end diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 0ac0e4bf5..bc4a44a5f 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -50,7 +50,7 @@ class LettingsLog < Log .or(filter_by_postcode(param)) .or(filter_by_id(param)) } - scope :filter_by_before_startdate, ->(date) { where("lettings_logs.startdate >= ?", date) } + scope :after_date, ->(date) { where("lettings_logs.startdate >= ?", date) } scope :unresolved, -> { where(unresolved: true) } scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) } diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index fc57f3ba3..2a0a6d5c4 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -51,6 +51,7 @@ class SalesLog < Log .where.not(postcode_full: nil) .where("age1 IS NOT NULL OR age1_known = 1 OR age1_known = 2") } + scope :after_date, ->(date) { where("saledate >= ?", date) } OPTIONAL_FIELDS = %w[purchid othtype].freeze RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze diff --git a/app/services/merge/merge_organisations_service.rb b/app/services/merge/merge_organisations_service.rb new file mode 100644 index 000000000..5a417ef02 --- /dev/null +++ b/app/services/merge/merge_organisations_service.rb @@ -0,0 +1,131 @@ +class Merge::MergeOrganisationsService + def initialize(absorbing_organisation_id:, merging_organisation_ids:) + @absorbing_organisation = Organisation.find(absorbing_organisation_id) + @merging_organisations = Organisation.find(merging_organisation_ids) + end + + def call + ActiveRecord::Base.transaction do + @merged_users = {} + @merged_schemes = {} + merge_organisation_details + @merging_organisations.each do |merging_organisation| + merge_rent_periods(merging_organisation) + merge_organisation_relationships(merging_organisation) + merge_users(merging_organisation) + merge_schemes_and_locations(merging_organisation) + merge_lettings_logs(merging_organisation) + merge_sales_logs(merging_organisation) + mark_organisation_as_merged(merging_organisation) + end + @absorbing_organisation.save! + log_success_message + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error("Organisation merge failed with: #{e.message}") + raise ActiveRecord::Rollback + end + end + +private + + def merge_organisation_details + @absorbing_organisation.holds_own_stock = merge_boolean_organisation_attribute("holds_own_stock") + end + + def merge_rent_periods(merging_organisation) + merging_organisation.rent_periods.each do |rent_period| + @absorbing_organisation.organisation_rent_periods << OrganisationRentPeriod.new(rent_period:) unless @absorbing_organisation.rent_periods.include?(rent_period) + end + end + + def merge_organisation_relationships(merging_organisation) + merging_organisation.parent_organisation_relationships.each do |parent_organisation_relationship| + if parent_relationship_exists_on_absorbing_organisation?(parent_organisation_relationship) + parent_organisation_relationship.destroy! + else + parent_organisation_relationship.update!(child_organisation: @absorbing_organisation) + end + end + merging_organisation.child_organisation_relationships.each do |child_organisation_relationship| + if child_relationship_exists_on_absorbing_organisation?(child_organisation_relationship) + child_organisation_relationship.destroy! + else + child_organisation_relationship.update!(parent_organisation: @absorbing_organisation) + end + end + end + + def merge_users(merging_organisation) + @merged_users[merging_organisation.name] = merging_organisation.users.map { |user| { name: user.name, email: user.email } } + merging_organisation.users.update_all(organisation_id: @absorbing_organisation.id) + end + + def merge_schemes_and_locations(merging_organisation) + @merged_schemes[merging_organisation.name] = [] + merging_organisation.owned_schemes.each do |scheme| + next if scheme.deactivated? + + new_scheme = Scheme.create!(scheme.attributes.except("id", "owning_organisation_id").merge(owning_organisation: @absorbing_organisation)) + scheme.locations.each do |location| + new_scheme.locations << Location.new(location.attributes.except("id", "scheme_id")) unless location.deactivated? + end + @merged_schemes[merging_organisation.name] << { name: new_scheme.service_name, code: new_scheme.id } + SchemeDeactivationPeriod.create!(scheme:, deactivation_date: Time.zone.now) + end + end + + def merge_lettings_logs(merging_organisation) + merging_organisation.owned_lettings_logs.after_date(Time.zone.today).each do |lettings_log| + if lettings_log.scheme.present? + scheme_to_set = @absorbing_organisation.owned_schemes.find_by(service_name: lettings_log.scheme.service_name) + location_to_set = scheme_to_set.locations.find_by(name: lettings_log.location&.name, postcode: lettings_log.location&.postcode) + + lettings_log.scheme = scheme_to_set if scheme_to_set.present? + lettings_log.location = location_to_set if location_to_set.present? + end + lettings_log.owning_organisation = @absorbing_organisation + lettings_log.save! + end + merging_organisation.managed_lettings_logs.after_date(Time.zone.today).each do |lettings_log| + lettings_log.managing_organisation = @absorbing_organisation + lettings_log.save! + end + end + + def merge_sales_logs(merging_organisation) + merging_organisation.sales_logs.after_date(Time.zone.today).each do |sales_log| + sales_log.update(owning_organisation: @absorbing_organisation) + end + end + + def mark_organisation_as_merged(merging_organisation) + merging_organisation.update(merge_date: Time.zone.today, absorbing_organisation: @absorbing_organisation) + end + + def log_success_message + @merged_users.each do |organisation_name, users| + Rails.logger.info("Merged users from #{organisation_name}:") + users.each do |user| + Rails.logger.info("\t#{user[:name]} (#{user[:email]})") + end + end + @merged_schemes.each do |organisation_name, schemes| + Rails.logger.info("New schemes from #{organisation_name}:") + schemes.each do |scheme| + Rails.logger.info("\t#{scheme[:name]} (S#{scheme[:code]})") + end + end + end + + def merge_boolean_organisation_attribute(attribute) + @absorbing_organisation[attribute] ||= @merging_organisations.any? { |merging_organisation| merging_organisation[attribute] } + end + + def parent_relationship_exists_on_absorbing_organisation?(parent_organisation_relationship) + parent_organisation_relationship.parent_organisation == @absorbing_organisation || @absorbing_organisation.parent_organisation_relationships.where(parent_organisation: parent_organisation_relationship.parent_organisation).exists? + end + + def child_relationship_exists_on_absorbing_organisation?(child_organisation_relationship) + child_organisation_relationship.child_organisation == @absorbing_organisation || @absorbing_organisation.child_organisation_relationships.where(child_organisation: child_organisation_relationship.child_organisation).exists? + end +end diff --git a/db/schema.rb b/db/schema.rb index ee5c1c1ad..7b5167a24 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: 2023_07_25_081029) do +ActiveRecord::Schema[7.0].define(version: 2023_07_19_150610) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/lib/tasks/merge_organisations.rake b/lib/tasks/merge_organisations.rake new file mode 100644 index 000000000..820343883 --- /dev/null +++ b/lib/tasks/merge_organisations.rake @@ -0,0 +1,12 @@ +namespace :merge do + desc "Merge organisations into one" + task :merge_organisations, %i[absorbing_organisation_id merging_organisation_ids] => :environment do |_task, args| + absorbing_organisation_id = args[:absorbing_organisation_id] + merging_organisation_ids = args[:merging_organisation_ids]&.split(" ")&.map(&:to_i) + + raise "Usage: rake merge:merge_organisations[absorbing_organisation_id, merging_organisation_ids]" if merging_organisation_ids.blank? || absorbing_organisation_id.blank? + + service = Merge::MergeOrganisationsService.new(absorbing_organisation_id:, merging_organisation_ids:) + service.call + end +end diff --git a/spec/lib/tasks/merge_organisations_spec.rb b/spec/lib/tasks/merge_organisations_spec.rb new file mode 100644 index 000000000..4155a4389 --- /dev/null +++ b/spec/lib/tasks/merge_organisations_spec.rb @@ -0,0 +1,41 @@ +require "rails_helper" +require "rake" + +RSpec.describe "emails" do + describe ":merge_organisations", type: :task do + subject(:task) { Rake::Task["merge:merge_organisations"] } + + let(:organisation) { create(:organisation) } + let(:merging_organisation) { create(:organisation) } + + let(:merge_organisations_service) { Merge::MergeOrganisationsService.new(absorbing_organisation_id: organisation.id, merging_organisation_ids: [merging_organisation.id]) } + + before do + allow(Merge::MergeOrganisationsService).to receive(:new).and_return(merge_organisations_service) + allow(merge_organisations_service).to receive(:call).and_return(nil) + Rake.application.rake_require("tasks/merge_organisations") + Rake::Task.define_task(:environment) + task.reenable + end + + context "when the rake task is run" do + it "raises an error when no parameters are given" do + expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake merge:merge_organisations[absorbing_organisation_id, merging_organisation_ids]") + end + + it "raises an error when only absorbing organisation is given" do + expect { task.invoke(1, nil) }.to raise_error(RuntimeError, "Usage: rake merge:merge_organisations[absorbing_organisation_id, merging_organisation_ids]") + end + + it "raises an error when only merging organisations are given" do + expect { task.invoke(nil, "1 2") }.to raise_error(RuntimeError, "Usage: rake merge:merge_organisations[absorbing_organisation_id, merging_organisation_ids]") + end + + it "raises runs the service with correct organisation IDs" do + expect(Merge::MergeOrganisationsService).to receive(:new).with(absorbing_organisation_id: 1, merging_organisation_ids: [2, 3]).once + expect(merge_organisations_service).to receive(:call).once + task.invoke(1, "2 3") + end + end + end +end diff --git a/spec/services/merge/merge_organisations_service_spec.rb b/spec/services/merge/merge_organisations_service_spec.rb new file mode 100644 index 000000000..2c2af0f3e --- /dev/null +++ b/spec/services/merge/merge_organisations_service_spec.rb @@ -0,0 +1,323 @@ +require "rails_helper" + +RSpec.describe Merge::MergeOrganisationsService do + subject(:merge_organisations_service) { described_class.new(absorbing_organisation_id: absorbing_organisation.id, merging_organisation_ids: [merging_organisation_ids]) } + + let(:absorbing_organisation) { create(:organisation, holds_own_stock: false) } + let(:absorbing_organisation_user) { create(:user, organisation: absorbing_organisation) } + + describe "#call" do + context "when merging a single organisation into an existing organisation" do + let(:merging_organisation) { create(:organisation, holds_own_stock: true, name: "fake org") } + + let(:merging_organisation_ids) { [merging_organisation.id] } + let!(:merging_organisation_user) { create(:user, organisation: merging_organisation, name: "fake name", email: "fake@email.com") } + + it "moves the users from merging organisation to absorbing organisation" do + expect(Rails.logger).to receive(:info).with("Merged users from fake org:") + expect(Rails.logger).to receive(:info).with("\tDanny Rojas (#{merging_organisation.data_protection_officers.first.email})") + expect(Rails.logger).to receive(:info).with("\tfake name (fake@email.com)") + expect(Rails.logger).to receive(:info).with("New schemes from fake org:") + merge_organisations_service.call + + merging_organisation_user.reload + expect(merging_organisation_user.organisation).to eq(absorbing_organisation) + end + + it "sets merge date on merged organisation" do + merge_organisations_service.call + + merging_organisation.reload + expect(merging_organisation.merge_date.to_date).to eq(Time.zone.today) + expect(merging_organisation.absorbing_organisation_id).to eq(absorbing_organisation.id) + end + + it "combines organisation data" do + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.holds_own_stock).to eq(true) + end + + it "rolls back if there's an error" do + allow(Organisation).to receive(:find).with([merging_organisation_ids]).and_return(Organisation.find(merging_organisation_ids)) + allow(Organisation).to receive(:find).with(absorbing_organisation.id).and_return(absorbing_organisation) + allow(absorbing_organisation).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + expect(Rails.logger).to receive(:error).with("Organisation merge failed with: Record invalid") + merge_organisations_service.call + + absorbing_organisation.reload + merging_organisation.reload + expect(absorbing_organisation.holds_own_stock).to eq(false) + expect(merging_organisation.merge_date).to eq(nil) + expect(merging_organisation.absorbing_organisation_id).to eq(nil) + expect(merging_organisation_user.organisation).to eq(merging_organisation) + end + + context "and merging organisation rent periods" do + before do + OrganisationRentPeriod.create!(organisation: absorbing_organisation, rent_period: 1) + OrganisationRentPeriod.create!(organisation: absorbing_organisation, rent_period: 3) + OrganisationRentPeriod.create!(organisation: merging_organisation, rent_period: 1) + OrganisationRentPeriod.create!(organisation: merging_organisation, rent_period: 2) + end + + it "combines organisation rent periods" do + expect(absorbing_organisation.rent_periods.count).to eq(2) + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.rent_periods.count).to eq(3) + expect(absorbing_organisation.rent_periods).to include(1) + expect(absorbing_organisation.rent_periods).to include(2) + expect(absorbing_organisation.rent_periods).to include(3) + end + + it "rolls back if there's an error" do + allow(Organisation).to receive(:find).with([merging_organisation_ids]).and_return(Organisation.find(merging_organisation_ids)) + allow(Organisation).to receive(:find).with(absorbing_organisation.id).and_return(absorbing_organisation) + allow(absorbing_organisation).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + expect(Rails.logger).to receive(:error).with("Organisation merge failed with: Record invalid") + merge_organisations_service.call + + absorbing_organisation.reload + merging_organisation.reload + expect(absorbing_organisation.rent_periods.count).to eq(2) + expect(merging_organisation.rent_periods.count).to eq(2) + end + end + + context "and merging organisation relationships" do + let(:other_organisation) { create(:organisation) } + let!(:merging_organisation_relationship) { create(:organisation_relationship, parent_organisation: merging_organisation) } + let!(:absorbing_organisation_relationship) { create(:organisation_relationship, parent_organisation: absorbing_organisation) } + + before do + create(:organisation_relationship, parent_organisation: absorbing_organisation, child_organisation: merging_organisation) + create(:organisation_relationship, parent_organisation: merging_organisation, child_organisation: other_organisation) + create(:organisation_relationship, parent_organisation: absorbing_organisation, child_organisation: other_organisation) + end + + it "combines organisation relationships" do + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.child_organisations).to include(other_organisation) + expect(absorbing_organisation.child_organisations).to include(absorbing_organisation_relationship.child_organisation) + expect(absorbing_organisation.child_organisations).to include(merging_organisation_relationship.child_organisation) + expect(absorbing_organisation.child_organisations).not_to include(merging_organisation) + expect(absorbing_organisation.parent_organisations.count).to eq(0) + expect(absorbing_organisation.child_organisations.count).to eq(3) + end + + it "rolls back if there's an error" do + allow(Organisation).to receive(:find).with([merging_organisation_ids]).and_return(Organisation.find(merging_organisation_ids)) + allow(Organisation).to receive(:find).with(absorbing_organisation.id).and_return(absorbing_organisation) + allow(absorbing_organisation).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + expect(Rails.logger).to receive(:error).with("Organisation merge failed with: Record invalid") + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.child_organisations.count).to eq(3) + expect(absorbing_organisation.child_organisations).to include(other_organisation) + expect(absorbing_organisation.child_organisations).to include(merging_organisation) + expect(absorbing_organisation.child_organisations).to include(absorbing_organisation_relationship.child_organisation) + end + end + + context "and merging organisation schemes and locations" do + let!(:scheme) { create(:scheme, owning_organisation: merging_organisation) } + let!(:location) { create(:location, scheme:) } + let!(:deactivated_location) { create(:location, scheme:) } + let!(:deactivated_scheme) { create(:scheme, owning_organisation: merging_organisation) } + let!(:owned_lettings_log) { create(:lettings_log, :sh, scheme:, location:, startdate: Time.zone.tomorrow, owning_organisation: merging_organisation) } + let!(:owned_lettings_log_no_location) { create(:lettings_log, :sh, scheme:, startdate: Time.zone.tomorrow, owning_organisation: merging_organisation) } + + before do + create(:location, scheme:, name: "fake location", postcode: "A1 1AA") + create(:location, scheme: deactivated_scheme) + create(:scheme_deactivation_period, scheme: deactivated_scheme, deactivation_date: Time.zone.today - 1.month) + create(:location_deactivation_period, location: deactivated_location, deactivation_date: Time.zone.today - 1.month) + create(:lettings_log, scheme:, location:, startdate: Time.zone.yesterday) + create(:lettings_log, startdate: Time.zone.tomorrow, managing_organisation: merging_organisation) + end + + it "combines organisation schemes and locations" do + expect(Rails.logger).to receive(:info).with("Merged users from fake org:") + expect(Rails.logger).to receive(:info).with("\tDanny Rojas (#{merging_organisation.data_protection_officers.first.email})") + expect(Rails.logger).to receive(:info).with("\tfake name (fake@email.com)") + expect(Rails.logger).to receive(:info).with("New schemes from fake org:") + expect(Rails.logger).to receive(:info).with(/\t#{scheme.service_name} \(S/) + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.owned_schemes.count).to eq(1) + expect(absorbing_organisation.owned_schemes.first.service_name).to eq(scheme.service_name) + expect(absorbing_organisation.owned_schemes.first.locations.count).to eq(2) + expect(absorbing_organisation.owned_schemes.first.locations.first.postcode).to eq(location.postcode) + expect(scheme.scheme_deactivation_periods.count).to eq(1) + expect(scheme.scheme_deactivation_periods.first.deactivation_date.to_date).to eq(Time.zone.today) + end + + it "moves relevant logs and assigns the new scheme" do + merge_organisations_service.call + + absorbing_organisation.reload + merging_organisation.reload + expect(absorbing_organisation.owned_lettings_logs.count).to eq(2) + expect(absorbing_organisation.managed_lettings_logs.count).to eq(1) + expect(absorbing_organisation.owned_lettings_logs.find(owned_lettings_log.id).scheme).to eq(absorbing_organisation.owned_schemes.first) + expect(absorbing_organisation.owned_lettings_logs.find(owned_lettings_log.id).location).to eq(absorbing_organisation.owned_schemes.first.locations.first) + expect(absorbing_organisation.owned_lettings_logs.find(owned_lettings_log_no_location.id).scheme).to eq(absorbing_organisation.owned_schemes.first) + expect(absorbing_organisation.owned_lettings_logs.find(owned_lettings_log_no_location.id).location).to eq(nil) + end + + it "rolls back if there's an error" do + allow(Organisation).to receive(:find).with([merging_organisation_ids]).and_return(Organisation.find(merging_organisation_ids)) + allow(Organisation).to receive(:find).with(absorbing_organisation.id).and_return(absorbing_organisation) + allow(absorbing_organisation).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + expect(Rails.logger).to receive(:error).with("Organisation merge failed with: Record invalid") + merge_organisations_service.call + + absorbing_organisation.reload + merging_organisation.reload + expect(absorbing_organisation.owned_schemes.count).to eq(0) + expect(scheme.scheme_deactivation_periods.count).to eq(0) + expect(owned_lettings_log.owning_organisation).to eq(merging_organisation) + expect(owned_lettings_log_no_location.owning_organisation).to eq(merging_organisation) + end + end + + context "and merging sales logs" do + let!(:sales_log) { create(:sales_log, saledate: Time.zone.tomorrow, owning_organisation: merging_organisation) } + + before do + create(:sales_log, saledate: Time.zone.yesterday, owning_organisation: merging_organisation) + end + + it "moves relevant logs" do + merge_organisations_service.call + + absorbing_organisation.reload + expect(SalesLog.filter_by_owning_organisation(absorbing_organisation).count).to eq(1) + expect(SalesLog.filter_by_owning_organisation(absorbing_organisation).first).to eq(sales_log) + end + + it "rolls back if there's an error" do + allow(Organisation).to receive(:find).with([merging_organisation_ids]).and_return(Organisation.find(merging_organisation_ids)) + allow(Organisation).to receive(:find).with(absorbing_organisation.id).and_return(absorbing_organisation) + allow(absorbing_organisation).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + expect(Rails.logger).to receive(:error).with("Organisation merge failed with: Record invalid") + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.sales_logs.count).to eq(0) + expect(sales_log.owning_organisation).to eq(merging_organisation) + end + end + end + + context "when merging a multiple organisations into an existing organisation" do + let(:merging_organisation) { create(:organisation, holds_own_stock: true, name: "fake org") } + let(:merging_organisation_too) { create(:organisation, holds_own_stock: true, name: "second org") } + + let(:merging_organisation_ids) { [merging_organisation.id, merging_organisation_too.id] } + let!(:merging_organisation_user) { create(:user, organisation: merging_organisation, name: "fake name", email: "fake@email.com") } + + before do + create_list(:user, 5, organisation: merging_organisation_too) + end + + it "moves the users from merging organisations to absorbing organisation" do + expect(Rails.logger).to receive(:info).with("Merged users from fake org:") + expect(Rails.logger).to receive(:info).with("\tDanny Rojas (#{merging_organisation.data_protection_officers.first.email})") + expect(Rails.logger).to receive(:info).with("\tfake name (fake@email.com)") + expect(Rails.logger).to receive(:info).with("Merged users from second org:") + expect(Rails.logger).to receive(:info).with(/\tDanny Rojas/).exactly(6).times + expect(Rails.logger).to receive(:info).with("New schemes from fake org:") + expect(Rails.logger).to receive(:info).with("New schemes from second org:") + merge_organisations_service.call + + merging_organisation_user.reload + expect(merging_organisation_user.organisation).to eq(absorbing_organisation) + end + + it "sets merge date and absorbing organisation on merged organisations" do + merge_organisations_service.call + + merging_organisation.reload + merging_organisation_too.reload + expect(merging_organisation.merge_date.to_date).to eq(Time.zone.today) + expect(merging_organisation.absorbing_organisation_id).to eq(absorbing_organisation.id) + expect(merging_organisation_too.merge_date.to_date).to eq(Time.zone.today) + expect(merging_organisation_too.absorbing_organisation_id).to eq(absorbing_organisation.id) + end + + it "combines organisation data" do + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.holds_own_stock).to eq(true) + end + + it "rolls back if there's an error" do + allow(Organisation).to receive(:find).with([merging_organisation_ids]).and_return(Organisation.find(merging_organisation_ids)) + allow(Organisation).to receive(:find).with(absorbing_organisation.id).and_return(absorbing_organisation) + allow(absorbing_organisation).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + expect(Rails.logger).to receive(:error).with("Organisation merge failed with: Record invalid") + merge_organisations_service.call + + absorbing_organisation.reload + merging_organisation.reload + expect(absorbing_organisation.holds_own_stock).to eq(false) + expect(merging_organisation.merge_date).to eq(nil) + expect(merging_organisation.absorbing_organisation_id).to eq(nil) + expect(merging_organisation_user.organisation).to eq(merging_organisation) + end + + context "and merging organisation relationships" do + let(:other_organisation) { create(:organisation) } + let!(:merging_organisation_relationship) { create(:organisation_relationship, parent_organisation: merging_organisation) } + let!(:absorbing_organisation_relationship) { create(:organisation_relationship, parent_organisation: absorbing_organisation) } + + before do + create(:organisation_relationship, parent_organisation: merging_organisation, child_organisation: absorbing_organisation) + create(:organisation_relationship, parent_organisation: merging_organisation, child_organisation: other_organisation) + create(:organisation_relationship, parent_organisation: absorbing_organisation, child_organisation: other_organisation) + create(:organisation_relationship, parent_organisation: merging_organisation, child_organisation: merging_organisation_too) + end + + it "combines organisation relationships" do + merge_organisations_service.call + + absorbing_organisation.reload + expect(absorbing_organisation.child_organisations).to include(other_organisation) + expect(absorbing_organisation.child_organisations).to include(absorbing_organisation_relationship.child_organisation) + expect(absorbing_organisation.child_organisations).to include(merging_organisation_relationship.child_organisation) + expect(absorbing_organisation.child_organisations).not_to include(merging_organisation) + expect(absorbing_organisation.parent_organisations).not_to include(merging_organisation) + expect(absorbing_organisation.child_organisations).not_to include(merging_organisation_too) + expect(absorbing_organisation.parent_organisations).not_to include(merging_organisation_too) + expect(absorbing_organisation.parent_organisations.count).to eq(0) + expect(absorbing_organisation.child_organisations.count).to eq(3) + end + + it "rolls back if there's an error" do + allow(Organisation).to receive(:find).with([merging_organisation_ids]).and_return(Organisation.find(merging_organisation_ids)) + allow(Organisation).to receive(:find).with(absorbing_organisation.id).and_return(absorbing_organisation) + allow(absorbing_organisation).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + expect(Rails.logger).to receive(:error).with("Organisation merge failed with: Record invalid") + merge_organisations_service.call + + absorbing_organisation.reload + merging_organisation.reload + expect(absorbing_organisation.child_organisations.count).to eq(2) + expect(absorbing_organisation.parent_organisations.count).to eq(1) + expect(absorbing_organisation.child_organisations).to include(other_organisation) + expect(absorbing_organisation.parent_organisations).to include(merging_organisation) + expect(absorbing_organisation.child_organisations).to include(absorbing_organisation_relationship.child_organisation) + end + end + end + end +end