public IProcessStep NavigateForward(IWorkSession session) { try { if (!this.CanNavigateForward(session)) { throw TransactionException.BuildException(TransactionException.ErrorCode.ActionNotAvailable); } // If the navigation gets blocked by a validation issue then the navigation process must fall back to the step // it started on. var fallbackStepId = this.CurrentStepId; IProcessStep startingStep = this.CurrentStep; IProcessStep nextStep = null; bool isBlocked = false; while (true) { // Get the step history record for the starting step. This record should always exist. IStepHistory stepHistory = this.TransactionHistory.GetHistoryForStep(startingStep); if (stepHistory is null) { throw TransactionException.BuildException(TransactionException.ErrorCode.StepHistoryNotFound); } bool hasExecuted = stepHistory.CompletedOn.HasValue; if (!hasExecuted) { ExecutionContext.Trace("Getting next step by executing current step."); // When the step has not been executed we need to execute it to find out what the next step in the // processes is because steps with conditional branching do not know the next step until execution. // // Additionally, any validation requirements associated with the step will be evaluated and this could // result in blocking forward navigation. In this case we still update the step history to show that the // step was executed. var executionResult = startingStep.Execute(ExecutionContext, session, this, RequirementEvaluator); nextStep = executionResult.NextStep; isBlocked = executionResult.StepIsBlocked; //validation requirements may block further advancement. this.TransactionHistory.AddToHistory(this.ExecutionContext, session, nextStep, true); } else { ExecutionContext.Trace("Getting next step from step history"); if (stepHistory.NextStepId is null) { throw TransactionException.BuildException(TransactionException.ErrorCode.StepHistoryInvalid); } var nextStepId = this.TransactionHistory.GetHistoryById(stepHistory.NextStepId).ProcessStepId; // When the step has executed then we just get the next step based on history. nextStep = this.CurrentProcess.ProcessSteps.Where(r => Id == nextStepId.Id).FirstOrDefault(); } // Stop moving forward when a UI step is found or when the step represents the last step in the process or // when additional execution is blocked. if (isBlocked || nextStep is null || nextStep.IsLastStep() || nextStep.Type.IsUIStep) { break; } // advance to the next process step and run through the loop again startingStep = nextStep; } // When any execution in this step is blocked because of validation then the process falls back to // the step that was current before we started. However any items that were completed in the above loop if (isBlocked) { this.CurrentStepId = fallbackStepId; return(this.CurrentStep); } // save the current step to the data base. this.CurrentStepId = nextStep; this.TransactionService.SaveTransactionCurrentStep(this.ExecutionContext, this, this.CurrentStepId); return(this.CurrentStep); } catch (Exception ex) { throw TransactionException.BuildException(TransactionException.ErrorCode.ProcessNavigationError, ex); } }