TSQL.APP Multi-Step Process Implementation Guide

Table of Contents

  1. Introduction
  2. Architecture Overview
  3. Implementation Details
  4. Complete Code Examples
  5. Setup Guide
  6. Best Practices
  7. Troubleshooting
  8. Extensions & Advanced Usage

Introduction

Purpose

This guide demonstrates how to implement multi-step processes in TSQL.APP using a centralized flow control pattern. This approach provides:

  • Clear process flow management
  • Standardized parameter passing
  • Robust error handling
  • Easy maintenance and extension

Core Concepts

  • Main action controls overall flow
  • Sub-actions perform specific tasks
  • Standardized parameter passing using JSON
  • Centralized state management

Architecture Overview

Process Flow Diagram

┌────────────────┐
│   Main Action  │◄─────────────┐
└───────┬────────┘              │
        │                       │
        ▼                       │
┌────────────────┐      Returns to
│  Sub-Action 1  │──────Main with
└───────┬────────┘      Result
        │                       │
        ▼                       │
┌────────────────┐             │
│  Sub-Action 2  │─────────────┘
└────────────────┘

Component Roles

  1. Main Action

    • Maintains process state
    • Decides next steps
    • Handles flow control
    • Manages completion
  2. Sub-Actions

    • Execute specific tasks
    • Return results to main
    • No flow control logic
    • Simple success/error status
  3. Parameter Exchange

    • JSON-based
    • Standardized structure
    • Clear naming conventions
    • Minimal necessary data

Implementation Details

State Management

Process Steps

  1. Initial state / menu
  2. First sub-action completed
  3. Second sub-action completed
  4. Process complete

Status Tracking

  • ProcessStep: Current step number
  • ProcessResult: Result from sub-actions

Parameter Structure

Main to Sub-Action

{
    "@wrapped_parameters": {
        "startTime": "2024-02-07 14:54:02",
        "sourceAction": "main_action",
        "processType": "orders"
    }
}

Sub-Action to Main

{
    "@wrapped_parameters": {
        "processResult": "SUCCESS"  // or "ERROR"
    }
}

Complete Code Examples

Main Action Code

-- Main Action: Process Controller

-- 1. Variable declarations (ALL at start)
-- Action configuration (customize these!)
DECLARE @MainActionName NVARCHAR(MAX) = N'main_action';     -- Change this into the actual modal action name!
DECLARE @SubActionName_1 NVARCHAR(MAX) = N'process_orders'; -- Change this into the actual modal sub_action name!
DECLARE @SubActionName_2 NVARCHAR(MAX) = N'view_reports';   -- Change this into the actual modal sub_action name!
--DECLARE @SubActionName_3... (etc.)

-- System variables
DECLARE @ActionButton1 NVARCHAR(MAX);
DECLARE @ActionButton2 NVARCHAR(MAX);
DECLARE @Message NVARCHAR(MAX);
DECLARE @Title NVARCHAR(MAX);
DECLARE @ButtonClass1 NVARCHAR(100);
DECLARE @ButtonClass2 NVARCHAR(100);
DECLARE @ButtonValue1 NVARCHAR(100);
DECLARE @ButtonValue2 NVARCHAR(100);
DECLARE @wrapped_parameters NVARCHAR(MAX);
DECLARE @CurrentTime NVARCHAR(100);
DECLARE @JsonValues NVARCHAR(MAX);

-- Flow control
DECLARE @ProcessStep INT;
DECLARE @ProcessResult NVARCHAR(100);

-- 2. Synchronize with modal state (MANDATORY)
EXEC sp_api_modal_get_value @name=N'@ActionButton1', @value=@ActionButton1 OUT;
EXEC sp_api_modal_get_value @name=N'@ActionButton2', @value=@ActionButton2 OUT;
EXEC sp_api_modal_get_value @name=N'@wrapped_parameters', @value=@wrapped_parameters OUT;

-- Get process state if returning from sub-action
IF @wrapped_parameters IS NOT NULL
BEGIN
    SELECT @ProcessResult = JSON_VALUE(@wrapped_parameters, '$.processResult');
END

-- Initialize or get current process step
EXEC sp_api_modal_get_value @name=N'@ProcessStep', @value=@ProcessStep OUT;
IF @ProcessStep IS NULL SET @ProcessStep = 0;

-- 4. Begin error handling
BEGIN TRY
    -- If we get an error result from any step, show error and reset
    IF @ProcessResult = N'ERROR'
    BEGIN
        SET @ProcessStep = 0;  -- Reset to start
        SET @ProcessResult = NULL;
    END
    
    -- If we get success from a step, advance to next
    IF @ProcessResult = N'SUCCESS'
    BEGIN
        SET @ProcessStep = @ProcessStep + 1;
        SET @ProcessResult = NULL;
    END

    -- Handle different steps of the process
    IF @ProcessStep = 0  -- Initial state / main menu
    BEGIN
        -- Display title and buttons
        SET @Title = N'Choose Process';
        EXEC sp_api_modal_text @text=@Title, @class=N'h3';

        EXEC sp_api_modal_button 
            @name = N'@ActionButton1',
            @value = N'Start Processing',
            @valueout = @ActionButton1 OUT,
            @class = N'btn-primary',
            @inline = 1;

        -- Handle button click
        IF @ActionButton1 IS NOT NULL
        BEGIN
            SET @ProcessStep = 1;

            -- Store process step for next round
            SET @JsonValues = (
                SELECT [@ProcessStep]=@ProcessStep
                FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
            );
            
            -- Start first sub-action
            SET @wrapped_parameters = (
                SELECT 
                    startTime = CONVERT(NVARCHAR(100), GETDATE(), 120),
                    sourceAction = @MainActionName,
                    processType = N'orders'
                FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
            );

            SET @JsonValues = (
                SELECT [@wrapped_parameters]=@wrapped_parameters 
                FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
            );
            
            EXEC sp_api_modal_run 
                @action_name = @SubActionName_1,
                @values = @JsonValues;
                
            RETURN;
        END
    END
    
    ELSE IF @ProcessStep = 1  -- After first sub-action
    BEGIN
        -- Start second sub-action
        SET @wrapped_parameters = (
            SELECT 
                startTime = CONVERT(NVARCHAR(100), GETDATE(), 120),
                sourceAction = @MainActionName,
                processType = N'reports'
            FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
        );

        SET @JsonValues = (
            SELECT [@wrapped_parameters]=@wrapped_parameters 
            FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
        );
        
        EXEC sp_api_modal_run 
            @action_name = @SubActionName_2,
            @values = @JsonValues;
            
        RETURN;
    END
    
    ELSE IF @ProcessStep = 2  -- All steps completed
    BEGIN
        SET @Message = N'All processing steps completed successfully!';
        EXEC sp_api_toast @text=@Message, @class=N'btn-success';
        EXEC sp_api_modal_clear;
        RETURN;
    END

END TRY
BEGIN CATCH
    SET @Message = ERROR_MESSAGE();
    EXEC sp_api_toast @text=@Message, @class=N'btn-danger';
    RETURN;
END CATCH

Sub-Action Code

-- Sub-Action Example ("process_orders")

-- 1. Variable declarations (ALL at start)
DECLARE @wrapped_parameters NVARCHAR(MAX);
DECLARE @Message NVARCHAR(MAX);
DECLARE @Title NVARCHAR(MAX);
DECLARE @StartTime NVARCHAR(100);
DECLARE @SourceAction NVARCHAR(100);
DECLARE @ProcessType NVARCHAR(100);
DECLARE @JsonValues NVARCHAR(MAX);
DECLARE @MainActionName NVARCHAR(MAX) = N'main_action';  -- Must match main action name!
DECLARE @ResultChoice NVARCHAR(MAX);

--local vars:
DECLARE @T NVARCHAR(MAX);

-- 2. Synchronize with modal state (MANDATORY)
EXEC sp_api_modal_get_value @name=N'@wrapped_parameters', @value=@wrapped_parameters OUT;
EXEC sp_api_modal_get_value @name=N'@ResultChoice', @value=@ResultChoice OUT;

-- 3. Parse wrapped parameters
SELECT
    @StartTime = JSON_VALUE(@wrapped_parameters, '$.startTime'),
    @SourceAction = JSON_VALUE(@wrapped_parameters, '$.sourceAction'),
    @ProcessType = JSON_VALUE(@wrapped_parameters, '$.processType');

-- 4. Begin error handling
BEGIN TRY
    -- Display information
    SET @Title = N'Process Orders';
    EXEC sp_api_modal_text @text=@Title, @class=N'h3';
    
    -- Show the received parameters
    SET @T=CONCAT(N'Started at: ', @StartTime)
    EXEC sp_api_modal_text 
        @text=@T, 
        @class=N'text-primary';
    
    SET @T=CONCAT(N'Called from: ', @SourceAction)
    EXEC sp_api_modal_text 
        @text=@T, 
        @class=N'text-info';
    
    SET @T=CONCAT(N'Process type: ', @ProcessType)
    EXEC sp_api_modal_text 
        @text=@T, 
        @class=N'text-info';

    -- Let user choose the result
    EXEC sp_api_modal_choose 
        @name = N'@ResultChoice',
        @value = @ResultChoice OUT,
        @list = N'SUCCESS,ERROR',
        @class = N'mt-3';

    -- If user made a choice, return to main action
    IF @ResultChoice IS NOT NULL
    BEGIN
        -- Return to main action with chosen result
        SET @wrapped_parameters = (
            SELECT 
                processResult = @ResultChoice
            FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
        );

        SET @JsonValues = (
            SELECT [@wrapped_parameters]=@wrapped_parameters 
            FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
        );
        
        -- Return to main action
        EXEC sp_api_modal_run 
            @action_name = @MainActionName,
            @values = @JsonValues;
    END

END TRY
BEGIN CATCH
    SET @Message = ERROR_MESSAGE();
    EXEC sp_api_toast @text=@Message, @class=N'btn-danger';
    
    -- Return to main with error status
    SET @wrapped_parameters = (
        SELECT 
            processResult = N'ERROR'
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
    );

    SET @JsonValues = (
        SELECT [@wrapped_parameters]=@wrapped_parameters 
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
    );
    
    EXEC sp_api_modal_run 
        @action_name = @MainActionName,
        @values = @JsonValues;
    RETURN;
END CATCH

Setup Guide

1. Create Actions

  1. Main Action Setup

    • Create new action in TSQL.APP
    • Name it according to process (e.g., 'process_controller')
    • Copy main action code
    • Update action names in variables
  2. Sub-Actions Setup

    • Create action for each step
    • Copy sub-action template
    • Customize processing logic
    • Update MainActionName variable

2. Configuration

  1. Action Names

    DECLARE @MainActionName NVARCHAR(MAX) = N'your_main_action';     
    DECLARE @SubActionName_1 NVARCHAR(MAX) = N'your_sub_action_1'; 
    DECLARE @SubActionName_2 NVARCHAR(MAX) = N'your_sub_action_2';
    
  2. Process Steps

    • Review step numbering
    • Adjust flow as needed
    • Add/remove steps

3. Testing

  1. Basic Flow

    • Start main action
    • Test SUCCESS path
    • Verify process completion
  2. Error Handling

    • Test ERROR responses
    • Verify reset behavior
    • Check error messages

Best Practices

1. Code Organization

  1. Variable Declaration

    • ALL variables at start
    • Clear grouping
    • Descriptive names
  2. Error Handling

    • Proper TRY/CATCH
    • Clear messages
    • Graceful recovery

2. Flow Control

  1. State Management

    • Clear step numbers
    • Simple progression
    • Error reset logic
  2. Parameter Passing

    • Minimal necessary data
    • Clear JSON structure
    • Proper synchronization

3. Maintenance

  1. Documentation

    • Clear comments
    • Step descriptions
    • Parameter explanations
  2. Code Structure

    • Logical grouping
    • Consistent formatting
    • Clear flow

Troubleshooting

Common Issues

  1. Parameter Problems

    • Check JSON structure
    • Verify synchronization
    • Validate parameter names
  2. Flow Issues

    • Verify step numbers
    • Check state handling
    • Review progression logic

Debug Tips

  1. Add Status Messages

    SET @Message = CONCAT(N'Current step: ', @ProcessStep);
    EXEC sp_api_toast @text=@Message, @class=N'btn-info';
    
  2. Check Parameters

    SET @Message = CONCAT(N'Parameters: ', @wrapped_parameters);
    EXEC sp_api_toast @text=@Message, @class=N'btn-info';
    

Extensions & Advanced Usage

Adding Steps

  1. New Sub-Action

    DECLARE @SubActionName_3 NVARCHAR(MAX) = N'new_step';
    
  2. Additional Step Handler

    ELSE IF @ProcessStep = 3  -- New step
    BEGIN
        SET @wrapped_parameters = (
            SELECT 
                startTime = CONVERT(NVARCHAR(100), GETDATE(), 120),
                sourceAction = @MainActionName,
                processType = N'new_process'
            FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
        );
    
        SET @JsonValues = (
            SELECT [@wrapped_parameters]=@wrapped_parameters 
            FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
        );
        
        EXEC sp_api_modal_run 
            @action_name = @SubActionName_3,
            @values = @JsonValues;
            
        RETURN;
    END
    

Conditional Flow Control

  1. Step Branching

    IF @ProcessResult = N'SUCCESS'
    BEGIN
        -- Check conditions for next step
        IF EXISTS (SELECT 1 FROM your_table WHERE status = N'pending')
        BEGIN
            SET @ProcessStep = 4;  -- Branch to special handling
        END
        ELSE
        BEGIN
            SET @ProcessStep = @ProcessStep + 1;  -- Normal flow
        END
    END
    
  2. Dynamic Next Step

    -- Get next step from business rules
    DECLARE @NextStep INT;
    SELECT @NextStep = next_step_id 
    FROM process_flow_rules 
    WHERE current_step = @ProcessStep 
    AND condition_met = 1;
    
    SET @ProcessStep = ISNULL(@NextStep, @ProcessStep + 1);
    

Adding Parameters

  1. Extended Parameter Structure

    SET @wrapped_parameters = (
        SELECT 
            startTime = CONVERT(NVARCHAR(100), GETDATE(), 120),
            sourceAction = @MainActionName,
            processType = N'orders',
            customData = (
                SELECT 
                    priority = N'high',
                    category = N'retail',
                    maxItems = 100
                FOR JSON PATH
            )
        FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
    );
    
  2. Reading Complex Parameters

    DECLARE @Priority NVARCHAR(100);
    DECLARE @Category NVARCHAR(100);
    DECLARE @MaxItems INT;
    
    SELECT
        @Priority = JSON_VALUE(@wrapped_parameters, '$.customData.priority'),
        @Category = JSON_VALUE(@wrapped_parameters, '$.customData.category'),
        @MaxItems = JSON_VALUE(@wrapped_parameters, '$.customData.maxItems');
    

UI Enhancements

  1. Progress Indicator

    -- In main action
    DECLARE @TotalSteps INT = 3;
    DECLARE @ProgressText NVARCHAR(MAX);
    
    SET @ProgressText = CONCAT(N'Step ', @ProcessStep + 1, N' of ', @TotalSteps);
    
    EXEC sp_api_modal_text 
        @text = @ProgressText,
        @class = N'text-muted mb-3';
    
  2. Step Navigation

    -- Allow going back a step
    EXEC sp_api_modal_button 
        @name = N'@BackButton',
        @value = N'← Previous Step',
        @valueout = @BackButton OUT,
        @class = N'btn-secondary',
        @inline = 1;
    
    IF @BackButton IS NOT NULL
    BEGIN
        SET @ProcessStep = @ProcessStep - 1;
        -- Reset state and restart
        EXEC sp_api_modal_restart;
        RETURN;
    END
    

Advanced Error Handling

  1. Detailed Error Logging

    -- In catch block
    DECLARE @ErrorNumber INT = ERROR_NUMBER();
    DECLARE @ErrorLine INT = ERROR_LINE();
    DECLARE @ErrorMessage NVARCHAR(MAX) = ERROR_MESSAGE();
    
    INSERT INTO process_error_log (
        process_step,
        error_number,
        error_line,
        error_message,
        process_parameters
    )
    VALUES (
        @ProcessStep,
        @ErrorNumber,
        @ErrorLine,
        @ErrorMessage,
        @wrapped_parameters
    );
    
  2. Recovery Options

    -- Allow retry or skip on error
    IF @ProcessResult = N'ERROR'
    BEGIN
        EXEC sp_api_modal_text 
            @text = N'Error occurred. Choose action:',
            @class = N'text-danger';
    
        EXEC sp_api_modal_choose 
            @name = N'@ErrorAction',
            @value = @ErrorAction OUT,
            @list = N'RETRY,SKIP,RESET',
            @class = N'mt-3';
    
        IF @ErrorAction = N'RETRY'
            SET @ProcessStep = @ProcessStep;  -- Stay on current step
        ELSE IF @ErrorAction = N'SKIP'
            SET @ProcessStep = @ProcessStep + 1;  -- Skip to next
        ELSE IF @ErrorAction = N'RESET'
            SET @ProcessStep = 0;  -- Start over
    END
    

Integration Patterns

  1. External System Calls

    -- Make API call and handle response
    DECLARE @ApiResponse NVARCHAR(MAX);
    DECLARE @ApiStatus INT;
    
    EXEC sp_api_fetch_json
        @url = N'https://api.example.com/process',
        @method = N'POST',
        @body = @wrapped_parameters,
        @json = @ApiResponse OUTPUT;
    
    SELECT @ApiStatus = JSON_VALUE(@ApiResponse, '$.status');
    
    IF @ApiStatus = 200
        SET @ProcessResult = N'SUCCESS';
    ELSE
        SET @ProcessResult = N'ERROR';
    
  2. File Processing

    -- Handle file upload in sub-action
    DECLARE @FileId INT;
    
    EXEC sp_api_modal_file
        @name = N'@ProcessFile',
        @to_file_context = N'process_docs',
        @to_file_context_id = @ProcessStep,
        @api_files_id = @FileId OUTPUT;
    
    IF @FileId IS NOT NULL
    BEGIN
        -- Process uploaded file
        -- Your file processing logic here
        SET @ProcessResult = N'SUCCESS';
    END
    

Maintenance & Monitoring

Performance Monitoring

  1. Step Timing

    DECLARE @StepStartTime DATETIME = GETDATE();
    
    -- Process step logic here...
    
    INSERT INTO step_performance_log (
        step_number,
        execution_time_ms,
        parameters
    )
    VALUES (
        @ProcessStep,
        DATEDIFF(MILLISECOND, @StepStartTime, GETDATE()),
        @wrapped_parameters
    );
    
  2. Process Analytics

    -- Track process metrics
    INSERT INTO process_analytics (
        process_id,
        step_number,
        step_result,
        execution_date,
        user_id
    )
    VALUES (
        @ProcessId,
        @ProcessStep,
        @ProcessResult,
        GETDATE(),
        @user_id
    );
    

Maintenance Tasks

  1. Clean Up Old Data

    -- Cleanup job
    DELETE FROM process_analytics 
    WHERE execution_date < DATEADD(DAY, -30, GETDATE());
    
    DELETE FROM process_error_log
    WHERE error_date < DATEADD(DAY, -90, GETDATE());
    
  2. Process Monitoring

    -- Monitor for stuck processes
    INSERT INTO process_alerts (process_id, alert_type, alert_message)
    SELECT 
        process_id,
        N'STUCK_PROCESS',
        N'Process has not advanced in over 1 hour'
    FROM process_state
    WHERE last_update < DATEADD(HOUR, -1, GETDATE())
    AND status = N'RUNNING';
    

Conclusion

This implementation guide provides a robust foundation for building multi-step processes in TSQL.APP. The pattern can be extended and customized while maintaining:

  • Clear flow control
  • Proper error handling
  • Clean parameter passing
  • Easy maintenance

Remember to:

  • Keep the main action focused on flow control
  • Keep sub-actions simple and focused
  • Handle errors gracefully
  • Document all customizations
  • Monitor performance and errors

For additional support or questions, consult the TSQL.APP documentation or contact the development team.