-- Change only hierarchy fields, preserving location details and aliases. CREATE OR ALTER PROCEDURE dbo.Location_Reparent @ProjectID int, @LocationID int, @ParentLocationID int, @ExpectedParentLocationID int = NULL, @UserID int = NULL AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; BEGIN TRY BEGIN TRANSACTION; DECLARE @LockedProjectID int, @OldParentID int, @Name nvarchar(200), @ParentName nvarchar(200); SELECT @LockedProjectID=ProjectID FROM dbo.Projects WITH (UPDLOCK,HOLDLOCK) WHERE ProjectID=@ProjectID AND IsArchived=0; IF @LockedProjectID IS NULL OR @UserID IS NULL OR NOT EXISTS (SELECT 1 FROM dbo.ProjectUserAccess WHERE ProjectID=@ProjectID AND UserID=@UserID AND IsActive=1) THROW 51000, 'You do not have access to this project.', 1; SELECT @Name=LocationName,@OldParentID=ParentLocationID FROM dbo.Locations WITH (UPDLOCK,HOLDLOCK) WHERE LocationID=@LocationID AND ProjectID=@ProjectID AND IsArchived=0; SELECT @ParentName=LocationName FROM dbo.Locations WITH (UPDLOCK,HOLDLOCK) WHERE LocationID=@ParentLocationID AND ProjectID=@ProjectID AND IsArchived=0; IF @Name IS NULL OR @ParentName IS NULL THROW 51000, 'Choose active locations from the same project.', 1; IF ISNULL(@OldParentID,0)<>ISNULL(@ExpectedParentLocationID,0) THROW 51000, 'The parent has changed since this page was loaded. Review the updated tree and try again.', 1; IF @OldParentID=@ParentLocationID THROW 51000, 'This location already has that parent.', 1; DECLARE @AncestorID int=@ParentLocationID; DECLARE @Visited TABLE (LocationID int PRIMARY KEY); WHILE @AncestorID IS NOT NULL BEGIN IF @AncestorID=@LocationID OR EXISTS (SELECT 1 FROM @Visited WHERE LocationID=@AncestorID) THROW 51000, 'A location cannot be its own parent or be moved beneath one of its descendants.', 1; INSERT @Visited VALUES (@AncestorID); DECLARE @NextAncestorID int=NULL; SELECT @NextAncestorID=ParentLocationID FROM dbo.Locations WITH (UPDLOCK,HOLDLOCK) WHERE LocationID=@AncestorID; SET @AncestorID=@NextAncestorID; END; UPDATE dbo.Locations SET ParentLocationID=@ParentLocationID,UpdatedDate=SYSUTCDATETIME(), ModifiedDateUTC=SYSUTCDATETIME(),ModifiedByUserID=@UserID WHERE LocationID=@LocationID; DECLARE @Description nvarchar(1000)=CONCAT(N'Parent changed from ', COALESCE((SELECT LocationName FROM dbo.Locations WHERE LocationID=@OldParentID),N'top level'),N' to ',@ParentName,N'.'); EXEC dbo.ProjectAudit_Record @ProjectID=@ProjectID,@UserID=@UserID,@ActivityType=N'Updated', @EntityType=N'Location',@EntityID=@LocationID,@EntityName=@Name,@Description=@Description; COMMIT TRANSACTION; END TRY BEGIN CATCH IF XACT_STATE()<>0 ROLLBACK TRANSACTION; THROW; END CATCH; END; GO