Forum Discussion

CLU's avatar
CLU
New Contributor III
6 months ago

Error in Business Rule for Data Mgmt Process

Data Mgmt sequence and steps work great when utilizing a 'manual' initiation of this process and business rule. However, the DM process automatically initiates twice daily at specific times using the same code and rules. When this occurs, it fails on occasion (not always), and this error message is received:

Summary: Error processing Data Management Step 'ProcessRecons_RCM'. Unable to execute Business Rule 'RCM_DataMgmtProcess'. One or more errors occurred. () Concurrency violation: the UpdateCommand affected 0 of the expected 1 records.

This is the business rule in use... asking if anyone can spot an obvious issue I have overlooked or might know the cause of the failed task on occasion.

Business Rule:

Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As ExtenderArgs) As Object

Try

Select Case args.FunctionType

Case Is = ExtenderFunctionType.ExecuteDataMgmtBusinessRuleStep

'Prepare Parameters

Dim wfProfile As String = args.NameValuePairs.XFGetValue("WFProfile", String.Empty)

Dim wfTime As String = args.NameValuePairs.XFGetValue("WFTime", String.Empty)



'Load Substitution Variables To Parse WFProfile and WFTime

Using dbConnFW As DbConnInfo = BRApi.Database.CreateFrameworkDbConnInfo(si)

Using dbConnApp As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)

Dim subVarInfo As SubstVarSourceInfo = SubstitutionVariablesHelper.CreateSubstVarSourceInfo(dbConnFW, dbConnApp, False)

wfProfile = SubstitutionVariableParser.ConvertString(si, subVarInfo, Nothing, wfProfile)

wfTime = SubstitutionVariableParser.ConvertString(si, subVarInfo, Nothing, wfTime)

End Using

End Using



'Users Can Supply Either WFProfileName or WFProfileID. If Name Is Supplied Resolve To ID

Dim wfProfileID As Guid = Guid.Empty

Dim wfProfileInfo As WorkflowProfileInfo = Nothing

If Not Guid.TryParse(wfProfile, wfProfileID) Then

'Profile Name Was Supplied

wfProfileInfo = BRApi.Workflow.Metadata.GetProfile(si, wfProfile)

Else

'ProfileID Was Supplied

wfProfileInfo = BRApi.Workflow.Metadata.GetProfile(si, wfProfileID)

End If



'Validate WFProfile Supplied Is A Valid Profile

If wfProfileInfo Is Nothing Then

Throw New XFUserMsgException(si, Nothing, Nothing, $"Error: Invalid workflow profile [{wfProfile}]")

Else

wfProfileID = wfProfileInfo.UniqueID

End If



'Users Can Supply Either WFTimeName or WFTimeID. If Name Is Supplied Resolve To ID

Dim wfTimeID As Integer = SharedConstants.Unknown

If Not Int32.TryParse(wfTime, wfTimeID) Then

'Time Name Was Supplied

wfTimeID = BRApi.Finance.Time.GetIdFromName(si, wfTime)

End If



'Validate WFTime Supplied Is A Valid Time

If wfTimeID = SharedConstants.Unknown Then

Throw New XFUserMsgException(si, Nothing, Nothing, $"Error: Invalid workflow time [{wfTime}]")

End If



'Set Task Description

Dim rScenarioID As Integer = GeneralHelpers.GetStoredSettingAsInteger(si, RCM_SettingsHelpers.StoredSettingName_ReconScenario)

Dim wfClusterPk As New WorkflowUnitClusterPk(wfProfileID, rScenarioID, wfTimeID)

Dim wfDesc As String = BRApi.Workflow.General.GetWorkflowUnitClusterPkDescription(si, wfClusterPk)

Dim fields As List(Of String) = StringHelper.SplitString(wfDesc, ":", StageConstants.ParserDefaults.DefaultQuoteCharacter)

Dim wfName As String = If(fields.Any(), fields(0), "WP#Missing")

Dim taskInformation As String = $"{wfName}:S#{ScenarioDimHelper.GetNameFromId(si, rScenarioID)}:T#{BRApi.Finance.Time.GetNameFromId(si, wfTimeID)} - {OFC_SharedConsts.DataMgmtTaskPrefixReconProcess}."

DataMgmtHelpers.SetTaskDescription(si, args.TaskActivityID, taskInformation)



'Make sure this DM job can run at this time (unless this was called from a UI action and we checked it already)

Dim canDataMgmtJobRunChecked As Boolean = args.NameValuePairs.MPGetValueToBoolean(DataMgmtProcessHelper.CanDataMgmtJobRunCheckedKey, False)

If Not canDataMgmtJobRunChecked Then

Dim dataMgmtHelper As New DataMgmtProcessHelper(si)



Dim params As New Dictionary(Of String, String) From {

{"WFProfile", wfProfileID.ToString},

{"WFTime", wfTimeID.ToString}}



Dim canDataMgmtJobRun As BoolAndTwoStrings = dataMgmtHelper.CanDataMgmtJobRun(OFC_SharedConsts.DataMgmtSeqReconProcess, params, args.TaskActivityID)

If Not canDataMgmtJobRun.Bool1 Then

Throw New XFUserMsgException(si, Nothing, Nothing, canDataMgmtJobRun.String1)

End If

End If



'Execute Process Recons

Dim errorMessage As String = String.Empty

Dim sScenarioID As Integer = GeneralHelpers.GetStoredSettingAsInteger(si, RCM_SettingsHelpers.StoredSettingName_SourceScenario)



Dim processHelper As New ProcessReconsHelper(si, wfProfileID, sScenarioID, rScenarioID, wfTimeID, args.TaskActivityID)

processHelper.ExecuteProcessRecons(errorMessage)



'Update Task Information Based On Results

If Not String.IsNullOrEmpty(errorMessage) Then

taskInformation &= errorMessage

DataMgmtHelpers.SetTaskDescription(si, args.TaskActivityID, taskInformation, True)

End If

End Select



Return Nothing

Catch ex As Exception

Throw ErrorHandler.LogWrite(si, New XFException(si, ex))

End Try

End Function



End Class



End Namespace

 

  • chul's avatar
    chul
    Contributor III

    The culprit is likely that you're using WF variables. When a user runs the job, OS knows that user's WF. Scheduled jobs shouldn't use them because the system doesn't know which WF to use.

  • Pete's avatar
    Pete
    New Contributor III

    One thing that may help is running a step prior to kicking off the ProcessRecons_RCM that changes the workflow to the correct selection. 

    brapi.Workflow.General.SetSelectedWorkflowView(si,profileName,scenarioName,timeName)

     

  • CLU's avatar
    CLU
    New Contributor III

    They are calling a step which sets the workflow prior to running the BR. Maybe the issue is with this step rather than the rule, neither really shows signs of why this is not flowing through.

  • sameburn's avatar
    sameburn
    Contributor III

    Hi CLU

    Your code has multiple calls to different helper functions. So it could be any one of them also. Adding some logging during controlled testing should help demystify

  • RobbSalzmann's avatar
    RobbSalzmann
    Valued Contributor II

    I agree with chul .  You need defaults for when a user is not running the job.  This updated code will do that, be sure to edit the defaults as indicated in the comments:

    Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As ExtenderArgs) As Object
        Try
            Select Case args.FunctionType
                Case ExtenderFunctionType.ExecuteDataMgmtBusinessRuleStep
                    ' Define defaults
                    Dim defaultWfTime As String = "2024" ' Replace with a valid default time
                    Dim defaultWfProfile As String = "DefaultProfileName" ' Replace with a valid default profile
    
                    ' Enforce wfProfile and wfTime defaults
                    Dim wfProfile As String = If(String.IsNullOrEmpty(args.NameValuePairs.XFGetValue("WFProfile", Nothing)), defaultWfProfile, args.NameValuePairs.XFGetValue("WFProfile", Nothing))
                    Dim wfTime As String = If(String.IsNullOrEmpty(args.NameValuePairs.XFGetValue("WFTime", Nothing)), defaultWfTime, args.NameValuePairs.XFGetValue("WFTime", Nothing))
    
                    ' Initialize workflow variables
                    Dim wfProfileID As Guid = Guid.Empty
                    Dim wfTimeID As Integer = If(Int32.TryParse(wfTime, Nothing), Convert.ToInt32(wfTime), BRApi.Finance.Time.GetIdFromName(si, wfTime))
                    wfTimeID = If(wfTimeID = SharedConstants.Unknown, SharedConstants.Unknown, wfTimeID)
    
                    ' Load substitution variables
                    Using dbConnFW As DbConnInfo = BRApi.Database.CreateFrameworkDbConnInfo(si),
                          dbConnApp As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)
                        Dim subVarInfo As SubstVarSourceInfo = SubstitutionVariablesHelper.CreateSubstVarSourceInfo(dbConnFW, dbConnApp, False)
                        wfProfile = SubstitutionVariableParser.ConvertString(si, subVarInfo, Nothing, wfProfile)
                        wfTime = SubstitutionVariableParser.ConvertString(si, subVarInfo, Nothing, wfTime)
                    End Using
    
                    ' Resolve workflow profile to ID
                    Dim wfProfileInfo As WorkflowProfileInfo = If(Guid.TryParse(wfProfile, wfProfileID), BRApi.Workflow.Metadata.GetProfile(si, wfProfileID), BRApi.Workflow.Metadata.GetProfile(si, wfProfile))
                    If wfProfileInfo IsNot Nothing Then wfProfileID = wfProfileInfo.UniqueID
    
                    ' Set task description
                    Dim rScenarioID As Integer = GeneralHelpers.GetStoredSettingAsInteger(si, RCM_SettingsHelpers.StoredSettingName_ReconScenario)
                    Dim wfClusterPk As New WorkflowUnitClusterPk(wfProfileID, rScenarioID, wfTimeID)
                    Dim wfDesc As String = BRApi.Workflow.General.GetWorkflowUnitClusterPkDescription(si, wfClusterPk)
                    Dim fields As List(Of String) = StringHelper.SplitString(wfDesc, ":", StageConstants.ParserDefaults.DefaultQuoteCharacter)
                    Dim wfName As String = If(fields.Any(), fields(0), "WP#Missing")
                    Dim taskInformation As String = $"{wfName}:S#{ScenarioDimHelper.GetNameFromId(si, rScenarioID)}:T#{BRApi.Finance.Time.GetNameFromId(si, wfTimeID)} - {OFC_SharedConsts.DataMgmtTaskPrefixReconProcess}."
                    DataMgmtHelpers.SetTaskDescription(si, args.TaskActivityID, taskInformation)
    
                    ' Ensure DM job can run
                    If Not args.NameValuePairs.MPGetValueToBoolean(DataMgmtProcessHelper.CanDataMgmtJobRunCheckedKey, False) Then
                        Dim dataMgmtHelper As New DataMgmtProcessHelper(si)
                        Dim params As New Dictionary(Of String, String) From {{"WFProfile", wfProfileID.ToString}, {"WFTime", wfTimeID.ToString}}
                        Dim canDataMgmtJobRun As BoolAndTwoStrings = dataMgmtHelper.CanDataMgmtJobRun(OFC_SharedConsts.DataMgmtSeqReconProcess, params, args.TaskActivityID)
                        If Not canDataMgmtJobRun.Bool1 Then Return Nothing
                    End If
    
                    ' Execute process reconciliations
                    Dim errorMessage As String = String.Empty
                    Dim sScenarioID As Integer = GeneralHelpers.GetStoredSettingAsInteger(si, RCM_SettingsHelpers.StoredSettingName_SourceScenario)
                    Dim processHelper As New ProcessReconsHelper(si, wfProfileID, sScenarioID, rScenarioID, wfTimeID, args.TaskActivityID)
                    processHelper.ExecuteProcessRecons(errorMessage)
    
                    ' Update task information if errors occurred
                    If Not String.IsNullOrEmpty(errorMessage) Then
                        taskInformation &= errorMessage
                        DataMgmtHelpers.SetTaskDescription(si, args.TaskActivityID, taskInformation, True)
                    End If
            End Select
    
            Return Nothing
        Catch ex As Exception
            Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
        End Try
    End Function