I have prepared below test for you. As you can see, If nested procedure returns 0 (as error) we can do rollback in first procedure (parent procedure)
CREATE TABLE test1010
(
ID Int identity (1,1),
Name nvarchar(20)
)
GO
--DROP PROCEDURE dbo.A1
CREATE PROCEDURE dbo.A1
@name nvarchar(20)
AS
BEGIN
INSERT INTO test1010 VALUES (@name)
return 0
END
GO
--DROP PROCEDURE dbo.AA
CREATE PROCEDURE dbo.AA
@name1 nvarchar(20)
AS
BEGIN
DECLARE @nRC INT;
SET NOCOUNT ON;
BEGIN TRANSACTION;
EXECUTE @nRC = dbo.A1 @name = @name1;
IF(@nRC <> 1)
ROLLBACK TRANSACTION;
ELSE
COMMIT TRANSACTION;
END;
GO
SELECT * FROM test1010
GO
EXECUTE dbo.AA @name1 = 'aa'
GO
SELECT * FROM test1010

And there is an other things. In each procedure we have to check number of transaction. If we don't have a transaction we open it, if we have we save it. At the end we check, if we opened the transaction, we commite it if not we let parent procedure to work on transaction.
You can see my answerhere.
CREATE PROCEDURE Ardi_Sample_Test
@InputCandidateID INT
AS
DECLARE @TranCounter INT;
SET @TranCounter = @@TRANCOUNT;
IF @TranCounter > 0
SAVE TRANSACTION ProcedureSave;
ELSE
BEGIN TRANSACTION;
BEGIN TRY
/*
<Your Code>
*/
IF @TranCounter = 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @TranCounter = 0
ROLLBACK TRANSACTION;
ELSE
IF XACT_STATE() <> -1
ROLLBACK TRANSACTION ProcedureSave;
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
GO
Use always this pattern in your procedures.