Sample Workflow AgentsApplication Note Date August 24, 2011 Applies To Kofax Capture 8.0, 9.0, 10.0 Ascent Capture 7.x Summary Revision This application note provides a few sample Workflow Agents for the Capture 7.x, 8.0, 9.0 and 10.0 products. 2.2 Contents Move Loose Pages Workflow Agent......................................................................................................... 2 Get the Data Type of All Index Fields in a Batch Workflow Agent ............................................................. 3 Data Lookup Workflow Agent................................................................................................................... 6 A Custom Batch History Workflow Agent ................................................................................................. 8 Externally Programmable Required Field Workflow Agent ...................................................................... 10 Programmatically Set a Batch Field ....................................................................................................... 13 Programmatically Set a Batch Priority .................................................................................................... 14 A Batch Totaling Workflow Agent ........................................................................................................... 16 Parsing the Original File Name .............................................................................................................. 17 ACWFLib. System.FindChildElementByName("Pages").Generic. if (oPageCol. ACDataElement oBatch = oRoot. oDocPages = oDoc. ACDataElement oRoot = oWorkflowData. // Create a new Document element then iterate through the // Batch Pages collection moving each to the new Document. if (oWorkflowData. using using using using using using System.exe") { // First get the Root & Batch elements. Listing 1 contains the entire source code for the project. Kofax. namespace MoveLoosePages { [Guid("167BAE04-0127-4280-AA57-B199D255C97A")] [ClassInterface(ClassInterfaceType.CurrentModule. Kofax. ACDataElement oDocuments = oBatch.FindChildElementByName("Documents"). } catch (Exception ex) { oWorkflowData. ACDataElementCollection oPageCol = oPages.ACWorkflowData oWorkflowData) { // We only want to do the separation after Scan. // Get the Document collection element.None)] [ProgId("MoveLoosePages. } // Get the Pages element and collection from the Batch. if (oDocuments == null) { oBatch.MoveToBack(ref oDocPages).CreateChildElement("Document").Runtime.ExtractRuntimeACDataElement(0). ACDataElement oDoc = oDocuments.Message. pages that are not assigned to any Document).CreateChildElement("Documents").FindChildElementByName("Pages").FindChildElementByName("Pages"). if (oDocPages == null) { oDoc..Collections.ErrorText = ex.e. } } } }}}} Listing 1 Page 2 .CreateChildElement("Pages").InteropServices.LoosePages")] public class LoosePages : _IACWorkflowAgent { public void ProcessWorkflow(ref Kofax. they are // moved into the Document's Pages element. oDocuments = oBatch. then a new Document is created and the loose pages are moved into that Document.Sample Workflow Agents Application Note Overview This application note provides a few sample Workflow Agents written in the C# language that demonstrate some solutions to some typical scenarios. System. Move Loose Pages Workflow Agent This Workflow Agent checks if there are any loose pages in a Batch (i.ACWFLib. System.ID == "scan. These are Loose Pages.FindChildElementsByName("Page").FindChildElementByName("Batch"). if there are.Count > 0) { // Get the Document's Pages element.Text. try { oDocPage. } foreach(ACDataElement oDocPage in oPageCol) { // When moving Pages into the Document. ACDataElement oPages = oBatch.DBLiteOpt.FindChildElementByName("Documents"). ACDataElement oDocPages = oDoc. One reason could be to determine the data type for insertion into a database at some later point in the workflow. get the Index Field definitions Get the Document Classes for the Documents in the current Batch The source code for the project is located on the next two pages. 5. Finally.0. Create ACDataElements for the Root and Batch. moving each to the new Document’s Pages collection. Get the Data Type of All Index Fields in a Batch Workflow Agent Consider that there is a requirement for obtaining the data type for all the Index Fields in a Batch.htm C:\Documents and Settings\All Users\Application Data\Kofax\CaptureSV\AcSetup. iterate through the Pages collection in the Batch. Check to see if there are any pages in the Batch’s Pages collection (loose pages). These are defined in the following files: Ascent Capture 7. 2. then create a new Document based on an existing Document Class in the Batch. If there are loose pages.htm Kofax Capture 8.0.htm C:\Program Files\AscentSS \CaptureSV\AcSetup. 9. Find or create a Pages collection in the new Document. Check to see if there is a Document element and create one if there is not.Sample Workflow Agents Application Note The steps to move loose pages are as follows: 1. 3.x • • • • C:\Program Files\AscentSS\CaptureSV\AcBatch. Page 3 . but requires using both the AscentCaptureRuntime and AscentCaptureSetup ACDataElements. This can be done in a Workflow Agent. and 10.htm The flow to obtain Index Field data types follows this general path: Get the runtime root and current Batch ACDataElements Get the Documents collection from the Batch Get the BatchClass element associated with the current Batch Get the ACSetup root element Output results Get the SQL Data Type of each Index Field For each Document Class. 4. 6.0 C:\Documents and Settings\All Users\Application Data\Kofax\CaptureSV\AcBatch. FindChildElementsByName("Document"). ACDataElementCollection oDocCol = oDoc. Kofax. Listing 2 Page 4 .FindChildElementByName("BatchClasses"). System.txt")) { foreach (DataType dt in lstInfo) { sw. namespace WFAGetFieldInfo { [Guid("ED1B171D-C72B-4152-972C-2B1F3C4E7F49")] [ClassInterface(ClassInterfaceType.AppendText(@"c:\WFATestFile. ACDataElement oCurrBatchClass = GetCurrentBatchClass(oSetupRoot.Collections. // Get the SetupACDataElement root. } } // Now that we have a generic List populated with the information // we need.FindChildElementByName("Documents"). ACDataElement CurrBatch) { // Get the Batch Class definition element for the current Batch. System. // Iterate through the Document Classes in the Batch Class.IndexFieldName).FindChildElementsByName("BatchClass").ToLower() == "scan. oCurrBatchClass. we // are simply going to write the List items to a file.FindChildElementByName("Batch"). foreach (ACDataElement oIndexDef in oIndexDefsCol) { DataType dt = new DataType(). For each Document. // get it's Document Class and iterate through it's Index Fields // to get the field types.DocumentClass). sw.IndexFieldType = GetIndexType(oIndexDef["FieldTypeName"].ExtractRuntimeACDataElement(0). ACDataElement oBatchClass = SetupRoot. oBatch).IO. ACDataElement oSetupRoot = oWorkflowData. // Obtain the Batch Class definition of the current Batch.Data.ExtractSetupACDataElement(0).FieldInfo")] public class FieldInfo : _IACWorkflowAgent { public void ProcessWorkflow(ref Kofax.DocumentClass = oDocClass["Name"]. dt.CurrentModule. // Add the Field info to the list while ensuring unique objects. dt. ACDataElement oDoc = oBatch.FindChildElementsByName("IndexFieldDefinition"). System.SqlClient. ACDataElement oBatch = oRoot.Add(dt). dt. System.None)] [ProgId("WFAGetFieldInfo.WriteLine(" ").DBLiteOpt. // Instantiate a generic list to contain the data types for the // Document classes in this Batch. sw.WriteLine("Index Field : " + dt. This list is of type DataType // which is defined in a class in this project.Text. lstInfo. sw.Generic. // Iterate through the Index Field collection and populate // the generic list.ACWFLib. we can now do with it what we want.ACWorkflowData oWorkflowData) { if (oWorkflowData.Runtime.InteropServices.WriteLine("Field Type : " + dt. oSetupRoot).ToString().ToString(). List<DataType> lstInfo = new List<DataType>().ToString().Sample Workflow Agents Application Note using using using using using using using using using System. // Get the Documents collection. In this case.WriteLine("Document Class: " + dt. using (StreamWriter sw = File.exe") { // Get the root & Batch elements. } } } } private ACDataElement GetCurrentBatchClass(ACDataElement SetupRoot.ID. foreach (ACDataElement doc in oDocCol) { ACDataElement oDocClass = GetDocumentClass(doc.Windows.IndexFieldType). System. Kofax. ACDataElementCollection oBatchClassCol = oBatchClass. ACDataElement oIndexDefs = oDocClass. // Iterate through the Index Fields in the Document Classes. oSetupRoot). ACDataElementCollection oIndexDefsCol = oIndexDefs.ACWFLib.IndexFieldName = oIndexDef["Name"]. // Iterate through the Documents in this Batch. System.FindChildElementByName("IndexFieldDefinitions").Forms. ACDataElement oRoot = oWorkflowData. ToString()) { // Get the Document Class that this link is pointing to by // iterating through the DocumentClasses collection and // searching for the specific DocumentClass element.ToString() == oDocClass["Name"]. ACDataElement oDocLinks = BatchClass. foreach (ACDataElement FType in oFieldTypeCol) { if (FType["Name"].ToString()) { return oDocClass.FindChildElementByName("DocumentClassLinks"). } } return null. case 8: return "DOUBLE". ACDataElement oDocClasses = SetupRoot.ToString()). // First. } } }} Listing 2 (continued) Page 5 . } private string GetIndexType(string name. iterate through the DocumentClassLinks collection // looking for the first link that applies to this Batch Class // and is the same as the current Document Class passed in.Parse(FType["SqlType"]. // Search for a specific FieldType Name.FindChildElementsByName("FieldType"). case 2: return "NUMERIC".ToString() == CurrBatch["BatchClassName"]) { return oBC. case 5: return "SMALLINT". case 9: return "DATETIME". return GetTypeString(iType).FindChildElementsByName("DocumentClass"). case 12: return "VARCHAR".FindChildElementByName("DocumentClasses"). foreach (ACDataElement oDocLink in oDocLinksCol) { if (oDocLink["PublishedBatchDefID"].ToString() == BatchClass["PublishedBatchDefID"]. case 4: return "INTEGER". ACDataElement BatchClass. ACDataElement oFieldTypes = SetupRoot. ACDataElement SetupRoot) { // Get the FieldTypes collection. case 3: return "DECIMAL". default: return "Unknown". ACDataElementCollection oDocLinksCol = oDocLinks. } private ACDataElement GetDocumentClass(ACDataElement doc.Sample Workflow Agents Application Note foreach (ACDataElement oBC in oBatchClassCol) { if (oBC["Name"]. ACDataElementCollection oFieldTypeCol = oFieldTypes.FindChildElementByName("FieldTypes"). case 7: return "REAL".FindChildElementsByName("DocumentClassLink"). } } return "Unknown". case 6: return "FLOAT". ACDataElementCollection oDocClassesCol = oDocClasses. ACDataElement SetupRoot) { // Obtain the DocumentClass links collection. } private string GetTypeString(int itype) { switch (itype) { case 1: return "CHAR". foreach (ACDataElement oDocClass in oDocClassesCol) { if (oDocLink["DocumentClassName"]. } } } } return null.ToString() == name) { int iType = int. This method gets the collection of BatchClasses from the setup root. From this element the current Batch ACDataElement is derived. the next thing to do is obtain the information about its Index Fields. the same is true for the FieldTypes collection. The List is of type DataType which is a class defined containing three public member variables: DocumentClassName. Once a match is found. IndexFieldName and IndexFieldType. since a Field Type can be used in different Document Classes. this could be easily modified to output this information to a database. When obtaining a Document Class. and then the collection is iterated through comparing Class Names with each Document Class in the DocumentClasses collection in the SetupRoot object. because one Document Class can be used in different Batch Classes. we are writing the output to a text file. In this loop we populate a generic List of DataType objects. etc. Here is a Workflow Agent that implements the native . The first thing to do is obtain the AscentCaptureRuntime ACDataElement which represents the root object. We then pass this setup root object and the Batch object to the GetCurrentBatchClass method. We then obtained the AscentCaptureSetup ACDataElement which serves as the setup root object. Page 6 . Regarding the Document Classes and their relationship to Batch Classes. then this is the correct Document Class. BatchClass and SetupRoot object. This is accomplished by first obtaining the DocumentClassLinks collection. We decided to create the generic List at this time. We then iterate through the List and output some formatted information about the Index Fields to a text file. For this. Folder Classes. iterates through the collection while comparing the BatchClassNames until one is found that equals the Batch that was passed in to the method. We have implemented the GetDocumentClass method to handle this issue. The next step is to obtain the Documents collection from the Batch. Now that we have our Document Class.NET SqlClient namespace to perform a database lookup to obtain a sequence number. However. The method takes three ACDataElement parameters: Document. The number is assigned to an Index Field then incremented in the database. Batch Classes.Sample Workflow Agents Application Note In this Workflow Agent (above). one must be careful to make sure the correct Document Class is being procured for the Batch Class in question. the actual DocumentClasses collection exists in the setup root element. XML file. The DocumentLinks collection is obtained from the Batch Class. If they are equal. The source code for this project is in Listing 3 on the next page. Likewise. the IndexFieldDefinitions collection from the DocumentClass element is iterated through. Data Lookup Workflow Agent Sometimes there is a need to perform a data lookup based on some Index Value or the like. the correct Document Class is returned from the method. then referencing the PublishedBatchClassDefID attribute in both the DocumentClassLink and BatchClass elements. etc. BeginTransaction(). oConn). SeqNum++. // Set the sequence value into the Document Index Field ACDataElement oIndexes = oDoc. // Retrieve the value out of the database and set the Index Field to the value SqlCommand oCmdSel = new SqlCommand("SELECT TOP 1 SequenceNumber FROM SequenceNumber".ToString()).ExtractRuntimeACDataElement(0).Message.ExecuteNonQuery().SqlClient. break. oCmdUpdate.CurrentModule.ACWorkflowData oWorkflowData) { if (oWorkflowData. // Determine if this is the correct Batch Class if (oBatch["BatchClassName"].Open().Generic. ACDataElementCollection oDocCol = oDocs.ToString().DBLiteOpt. System. oCmdSel.Initial Catalog=TestData. foreach (ACDataElement oIndex in oIndexesCol) { if (oIndex["Name"] == "Number Field") { oIndex["Value"] = SeqNum. } } // Update the Sequence in the database with the incremented value.FindChildElementByName("IndexFields").exe") { // Set the Index Field Value ACDataElement root = oWorkflowData. ACDataElement oDocs = oBatch. } // Commit the transaction oTran.FindChildElementsByName("Document").Sample Workflow Agents Application Note using using using using using using using System. namespace DataLookup { [Guid("9B3281D9-6C5B-4dee-9209-E55A23984CBE")] [ClassInterface(ClassInterfaceType.None)] [ProgId("DataLookup. SqlCommand oCmdUpdate = new SqlCommand("UPDATE SequenceNumber SET SequenceNumber=" + SeqNum. oConn). } catch (Exception ex) { oWorkflowData. System. System.InteropServices. System. oCmdUpdate.Integrated Security=True")) { try { // Create a database connection and open the database oConn.FindChildElementByName("Documents"). increment the value then // update the value in the SQL Server database.ToString().ACWFLib.ID. ACDataElementCollection oIndexesCol = oIndexes. foreach(ACDataElement oDoc in oDocCol) { // Connect to an SQL Server database.Transaction = oTran.ToString() == "Test Numeric") { using (SqlConnection oConn = new SqlConnection( @"Data Source=mrobertson-wks\sqlexpress.FindChildElementsByName("IndexField"). ACDataElement oBatch = root.Runtime.ToLower() == "scan.Commit().Collections. Kofax.Text. get the current sequence value. // assign the value to an Index Field.Data.ErrorText = ex.ACWFLib. SeqNum = int.FindChildElementByName("Batch"). } } // using SqlConnection } // if BatchClassName } // if ModuleID = scan. int SeqNum = 0.SequenceNumber")] public class SequenceNumber : _IACWorkflowAgent { public void ProcessWorkflow(ref Kofax.ExecuteScalar().exe } } } Listing 3 Page 7 . Kofax.Transaction = oTran.Parse(oCmdSel. // Start a transaction process SqlTransaction oTran = oConn. And after running a Batch. the Batch workflow contains the Scan. NOTE: Workflow Agents do not fire after Release. Recognition Server and Validation modules.Sample Workflow Agents Application Note A Custom Batch History Workflow Agent Let’s assume there is a need to create a custom Batch History to your own database that is tailored to your specific needs. this was the output in the table: As the entries show. Here is a Workflow Agent that writes out the following values to a database: • • • • • BatchID ModuleID UserID TimeStart Current Time & Date We created this table in SQL Express. Page 8 . oCmdInsert.ID == "index.CurrentModule. } } } } } } } Listing 4 Page 9 . System.Now. TimeStart. ACDataElement oHistory = oBatch. ACDataElement oBatch = root.CurrentModule. oConn). ACDataElement root = oWorkflowData.FindChildElementByName("BatchHistoryEntries").ToString() == oWorkflowData.Count].None)] [ProgId("BatchHistory.ExecuteNonQuery(). oTran. We are using the current system Time and Date for the TimeEnd field because the Workflow Agent is fired as a part of the Batch closing process and the EndDateTime attribute in the BatchHistoryEntry has not been populated yet. System.ID. Kofax.exe" || oWorkflowData. This attribute is actually populated when the Batch is fully closed.ToString() + "')". if (oHist["ModuleID"]. namespace BatchHistory { [Guid("E1D00C79-C8F6-4a70-849F-8E7FF7DFB7E8")] [ClassInterface(ClassInterfaceType.ACWFLib.Sample Workflow Agents Application Note Listing 4 (below) contains the code for this Workflow Agent. // Get the last entry in the collection.exe") { // First drill down and get the Batch ACDataElement and the // BatchHistories collection. // However we will check to make sure.ACWorkflowData oWorkflowData) { if (oWorkflowData.Initial Catalog=TestData.ID.SqlClient. '" + oHist["StartDateTime"].Open().ToString() + "'.History")] public class History : _IACWorkflowAgent { public void ProcessWorkflow(ref Kofax.ToString() + "'.Transaction = oTran. using (SqlConnection oConn = new SqlConnection( @"Data Source=mrobertson-wks\sqlexpress.FindChildElementsByName("BatchHistoryEntry"). oCmdInsert.CurrentModule. System. UserID. TimeEnd)" + "VALUES ('" + oBatch["ExternalBatchID"]. '" + oWorkflowData.ID == "fp.ACWFLib.exe" || oWorkflowData. } catch (Exception ex) { // Put exception handling here. ACDataElementCollection oHistoryCol = oHistory.ID == "scan. System.Collections.Integrated Security=True")) { try { // Create a database connection and open the database oConn.ToString() + "'.Runtime.Generic.ToString()) { // We found the element. Kofax. string SQLInsert = "INSERT INTO BatchHistory (BatchID.Text.ExtractRuntimeACDataElement(0). '" + DateTime.CurrentModule.ToString() + "'.Data.BeginTransaction().DBLiteOpt. This should be for the current ModuleID.InteropServices. In the ProcessWorkflow event.CurrentModule.Commit(). Insert a record into the database. SqlCommand oCmdInsert = new SqlCommand(SQLInsert. we are trapping all of the trappable modules specified in this Batch Class.FindChildElementByName("Batch"). // Start a transaction process SqlTransaction oTran = oConn. using using using using using using using System. We get the last item in the BatchHistoryEntries collection and write out some values to a database. '" + oHist["UserID"]. ACDataElement oHist = oHistoryCol[oHistoryCol. ModuleID. The steps that need to be performed in the Workflow Agent are as follows: • • • • • • • The ProcessWorkflow event will execute code after Recognition only.Elements("IndexField") select f). they want the Batch to be routed to Validation. A generic List is populated with the Index Field names. if one or more required field is not populated. For each Index Field in the Document that has a name contained in the generic List.0" encoding="utf-8"?> <IndexFields> <IndexField name="FirstName" /> <IndexField name="LastName" /> </IndexFields> For this Workflow Agent. beginning on the next page. The complete source code for the Workflow Agent is in Listing 5.Element("IndexFields"). route the Batch to Release. If all Fields are populated.Sample Workflow Agents Application Note Externally Programmable Required Field Workflow Agent A customer wants to have required Index Fields in their Documents but.5) is required to use the LINQ libraries. They want to be able to configure specific fields externally in an XML file. Page 10 . The XML file will have the following format: <?xml version="1.NET 3. we will be using a LINQ query to obtain the Index Field names from the XML file. route the Batch to Release. drill down to the Index Fields. If all target Fields are populated. NOTE: Visual Studio 2008 (. they do not want to configure them within the Document Class properties. exit the loop and route the Batch to Validation. Open and read the XML file and obtain the Index Field names. verify the Field has been populated with a value. After Recognition. If a target Field is not populated. For each Document in the Batch. Here is the LINQ query: IEnumerable<XElement> ireqFields = (from f in xFile. System. XElement reqFields = xFile.ToString().Win32. foreach (ACDataElement oIndex in oIndexesCol) { if (ReqFieldsList.Xml.Forms.FindChildElementByName("Documents").IO.CurrentModule. // set the flag and exit the loops.xml").Windows. Kofax.ACWorkflowData oWorkflowData) { ACDataElement oRoot = null. } // Load up the XML file and populate the generic List // with the required Index Field names. try { // This code only executes after Recognition.None)] [ProgId("RequiredFieldsWFA.Registry. // Make sure that the XML file exists. ACDataElementCollection oIndexesCol = null.Trim() == "" ? true : false. if (oWorkflowData.xml file was not present.ExtractRuntimeACDataElement(0).Text. oDocsCol = oDocs. ACDataElement oDocs = null.ToString().Exists(ap => ap == oIndex["Name"].Load(sXMLPath).Elements("IndexField") select f). // This LINQ query gets all the Field names from the // IndexFields top element. using (Microsoft. System.0".Collections.FindChildElementsByName("Document").Attribute("name").RegistryKey rk = Microsoft. System. bool bNotPopulated = false.Exists(sXMLPath)) { throw new ApplicationException("The RequiredFields.ToString()).StartupPath.Value.Element("IndexFields"). oIndexes = oDoc.OpenSubKey(@"SOFTWARE\Kofax Image Products\Ascent Capture\3. Listing 5 Page 11 . IEnumerable<XElement> ireqFields = (from f in xFile. foreach (XElement x in ireqFields) { ReqFieldsList. false)) { sXMLPath = Path.GetValue("ServerPath"). } // Get the ACDataElements oRoot = oWorkflowData.Add(x.ToLower() == "fp.Generic.FindChildElementByName("IndexFields"). ACDataElement oIndexes = null.FindChildElementByName("Batch"). "RequiredFields.ACWFLib. System.Runtime.xml"). System. } List<string> ReqFieldsList = new List<string>(). System. if (bNotPopulated) break.Sample Workflow Agents Application Note using using using using using using using using using using System.ID.FindChildElementsByName("IndexField").RequiredFields")] public class RequiredFields : _IACWorkflowAgent { public void ProcessWorkflow(ref Kofax. "RequiredFields.Linq.Combine(Application. if (!File.ToString())) { bNotPopulated = oIndex["Value"]. } Marshal.Element("IndexFields").Combine(rk.ACWFLib. oBatch = oRoot. System. Kofax. oDocs = oBatch.LocalMachine. oIndexesCol = oIndexes.InteropServices.ReleaseComObject(oIndex). namespace RequiredFieldsWFA { [Guid("DA01B20B-2610-4f7d-8B3D-BB6FDD82E31F")] [ClassInterface(ClassInterfaceType.Linq.DBLiteOpt. XDocument xFile = XDocument.").exe") { string sXMLPath = Path. ACDataElement oBatch = null.Win32. ACDataElementCollection oDocsCol = null. // Iterate through the Documents foreach (ACDataElement oDoc in oDocsCol) { // Iterate the Index Fields in the Document // If an unpopulated required field is detected. } } } } catch (Exception ex) { MessageBox.ID.PossibleModules. oIndexesCol = null.Sample Workflow Agents Application Note } Marshal. if (bNotPopulated) break. } if (oDocs != null) { Marshal. } finally { // Release the COM objects.ReleaseComObject(oDocsCol). if (!bNotPopulated) { ACWorkflowModules oPossibleModules = oWorkflowData. } if (oBatch != null) { Marshal. } // Iterate through the possible Modules for this // Batch Class definition.ReleaseComObject(oIndexes).Message).ToLower() == "release.ReleaseComObject(oModule).ReleaseComObject(oIndexesCol). oDocs = null. } if (oRoot != null) { Marshal.ReleaseComObject(oDocs). oBatch = null. Route to Release. } if (oIndexes != null) { Marshal. } Marshal. } if (oDocsCol != null) { Marshal. oDocsCol = null.ReleaseComObject(oRoot). if (oIndexesCol != null) { Marshal. foreach (ACWorkflowModule oModule in oPossibleModules) { if (oModule. oWorkflowData.set_NextModule(ref tMod). oIndexes = null.ReleaseComObject(oBatch). break.exe") { ACWorkflowModule tMod = oModule.ReleaseComObject(oDoc). } } } } } Listing 5 (continued) Page 12 .Show("Application error: " + ex. oRoot = null. "TestValue").BatchField")] public class BatchField : _IACWorkflowAgent { public void ProcessWorkflow(ref Kofax. we still check the Batch Class name. This can easily be accomplished in a Workflow Agent. However.ExtractRuntimeACDataElement(0). from a file.DBLiteOpt. This value will be propagated to all Documents that have a Field which was properly mapped to this Batch-level Field. In this case.ACWFLib. In all Document Classes in this Batch Class.DBLite. System. This value will be set in a Field within all Documents of the Batch. ACDataElement oFields = oBatch. Create a Workflow Agent to set the Batch Field value (see Listing 6).FindChildElementByAttribute("BatchField". In this case we named the Field “TestValue”.ACWFLib. Kofax.ID. ACDataElement oField = oFields.Runtime. Kofax.Sample Workflow Agents Application Note Programmatically Set a Batch Field There is a need to set a common value into all Documents in a specific Batch. oField["Value"] = DateTime. This method takes three parameters.None)] [ProgId("WFACopyBatchField. namespace WFACopyBatchField { [Guid("FEB73FE4-FBAE-46db-B25E-E4597727F0C6")] [ClassInterface(ClassInterfaceType.CurrentModule. ACDataElement root = oWorkflowData.Collections. System. The Field name is hard coded because this WFA will only be used with this Batch Class.Generic.FindChildElementByName("Batch"). we are retrieving the “BatchField” element with the “Name” attribute equaling “TestValue”.FindChildElementByName("BatchFields").ToLower() == "fp. // // // if { Retrieve the Batch Field to copy.Text. Make the Batch Field hidden so that the user cannot set it at Batch creation.Now.ACWorkflowData oWorkflowData) { // This WFA processes after the Recognition Module. map the Default value to “{$TestValue}”. For this example. } } } } } Listing 6 Note the use of the FindChildElementByName( ) method. Follow these steps: • • • • using using using using using using using Create a Batch Class containing a Batch Field. Kofax. "Name". etc. if (oWorkflowData.InteropServices.ToString(). System. ACDataElement oBatch = root.exe") { // Retrieve the Batch element. This value will need to be set automatically. Page 13 . (oBatch["BatchClassName"] == "TEST") // Retrieve the BatchField element with the Name attribute = TestValue. we are going to set a Batch Field value to the current Date & Time. a date value. which is the Batch Field. The “Value” attribute of this element is then set to the current system Date and Time. possibly from a database lookup. System. For this example. If a Document exists based on the Document Class with the higher importance. One of the Document Classes is to have an importance that is placed at a higher value than the other Document Classes in this Batch Class. the Batch Priority attribute will be changed to “1”. let’s assume that the Batch Class this Workflow Agent is added to has several Document Classes.” here is a Workflow Agent that sets the Priority of a Batch based on some criteria.Sample Workflow Agents Application Note Programmatically Set a Batch Priority Related to the previous topic. “Programmatically Set a Batch Field. The Workflow Agent will analyze the Documents in the current Batch. Extract both the runtime & setup root ACDataElements Extract the Document ACDataElement Extract the Batch ACDataElement Iterate through the Documents in the Batch No Documents found based on the high priority Document Class Extract the Document Class ACDataElementCollection from the setup root Iterate through the Document Class ACDataElementCollection No Extract the FormTypes ACDataElementCollection for this Document Class Iterate through the FormTypes collection for this Document Class No match found Was a match found? Yes Set the Batch Priority Page 14 . Collections. ACDataElement oBatch = root. foreach (ACDataElement oForm in oFormTypeColl) { if (oForm["Name"] == oDoc["FormTypeName"]) { if (oSetupDoc["Name"] == "MyClassName") { // Document Class Name found.MessageBox. } break. System. // For each Document Class in the Setup root.FindChildElementsByName("FormType"). using using using using using using System.. } } } } return false. Instead of hard coding the name. System.FindChildElementByName("Documents"). Another method for trapping a Document Class Name is to implement a hidden field in the Document Class then setting its default value to “{Document Class Name}”. if (oWorkflowData. ACDataElementCollection oDocClassColl = oDocClass. if (IsPriorityDocument(oWorkflowData)) { oBatch["Priority"] = "1".ToLower() == "fp.Sample Workflow Agents Application Note The procedure for obtaining the Document Class names of the Documents in a Batch follows this sequence (see Listing 7). ACDataElement root = oWorkflowData. System.ID.ExtractSetupACDataElement(0). // for each Document in this current Batch. ACDataElementCollection oDocColl = oDocument. return true.ExtractRuntimeACDataElement(0).Windows.ExtractRuntimeACDataElement(0). ACDataElement oRoot = oWorkflowData. ACDataElement oSetup = oWorkflowData.Forms.Generic.DBLiteOpt.None)] [ProgId("SetPriority.FindChildElementsByName("DocumentClass"). } } } Listing 7 Here we are setting the Batch Priority when a Document Class named “MyClassName” exists in the Batch. The Workflow Agent could then simply look at the Index value and set the priority attribute accordingly.FindChildElementByName("Batch").. foreach (ACDataElement oDoc in oDocColl) { // Get the setup info of the Document's Class settings.ACWFLib. text file or XML file. ACDataElement oDocument = oBatch. ACDataElementCollection oFormTypeColl = oFormType. ACDataElement oDocClass = oSetup. an external value might be used from a database. namespace SetPriority { [Guid("5B9A4F33-4FAD-4288-9FEC-7897777F2FB7")] [ClassInterface(ClassInterfaceType.FindChildElementsByName("Document").. Page 15 .Text. Kofax..FindChildElementByName("Batch").CurrentModule.FindChildElementByName("DocumentClasses").Runtime.FindChildElementByName("FormTypes"). foreach (ACDataElement oSetupDoc in oDocClassColl) { ACDataElement oFormType = oSetupDoc. ACDataElement oBatch = oRoot.SetPriority")] public class SetPriority : _IACWorkflowAgent { public void ProcessWorkflow(ref ACWorkflowData oWorkflowData) { // This executes after the Recognition Module.exe") { //global::System.. } } } private bool IsPriorityDocument(ACWorkflowData oWorkflowData) { // We will need both the Runtime and the SetupRuntime ACDataElements. // Iterate through the FormTypes collection. Kofax.Show("Test3")..InteropServices. See Listing 8 for the source code. ACDataElement oBField = oBFields. } } } } Listing 8 Note the use of the “FindChildElementByAttribute on the IndexFields element. foreach (ACDataElement oDoc in oDocCol) { // We are doing a blind Try. "Sum"). System.CurrentModule. "Total"). ACDataElement oDocument = oBatch. Page 16 .ToString()). try { ACDataElement oIndexes = oDoc. ACDataElementCollection oDocCol = oDocument.DocTotal")] public class DocTotal : _IACWorkflowAgent { public void ProcessWorkflow(ref ACWorkflowData oWorkflowData) { // This Workflow Agent processes after the Validation Module. dTotal += Decimal. "Name".Generic. Kofax.ACWFLib.FindChildElementByName("Documents"). ACDataElement oBFields = oBatch. After Validation. } catch {} } GC.Text.FindChildElementByAttribute("IndexField".Catch here because there // could be a Document that may not have an Index Field // with a Name that equals "Sum". the value of the “Sum” field is accumulated into the “Total” Field in the Batch.FindChildElementByName("Batch").DBLiteOpt.FindChildElementsByName("Document"). ACDataElement oSumField = oIndexes.FindChildElementByName("BatchFields"). For each Document.Collections. ACDataElement root = oWorkflowData. System. the Documents in the Batch are iterated through. oBField["Value"] = dTotal. namespace WFADocTotal { [Guid("35E8AAE9-45D0-43d7-B75C-A89EAAC26846")] [ClassInterface(ClassInterfaceType. Kofax.ExtractRuntimeACDataElement(0).ID == "index. ACDataElement oBatch = root.Runtime.ToString().FindChildElementByAttribute("BatchField". This is much simpler than iterating through all the Index Fields looking for a match on the Name attribute. "Name". using using using using using using System.FindChildElementByName("IndexFields").None)] [ProgId("WFADocTotal.exe") { Decimal dTotal = 0.InteropServices.Parse(oSumField["Value"]. if (oWorkflowData.Sample Workflow Agents Application Note A Batch Totaling Workflow Agent Here is a simple Workflow Agent that maintains a Batch Field to contain a summation of a specific Field in the Documents of the Batch.KeepAlive(oDocCol). System.. tif).None)] [ProgId("GetOriginalFilename. oPart1["Value"] = BaseStr[0]. ACDataElement oDocuments = oBatch.ToString().FindChildElementByAttribute("IndexField". The Workflow Agent iterates through all Documents in the Batch. "Part1").ACWFLib. To keep things simple.FindChildElementsByName("Document"). "Part2"). all three Field Types are VARCHAR with a length of 10 characters.InteropServices. System. (e. } GC.KeepAlive(oDocCol). using using using using using using using System. NAME_ABC_12345. For each Document.exe") { ACDataElement root = oWorkflowData. Part2 and Part3 respectively. ACDataElementCollection oDocCol = oDocuments.Collections. oPart3["Value"] = BaseStr[2]. string[] BaseStr = oPagesCol[1]["OriginalFileName"].g.FindChildElementByAttribute("IndexField". "Name".Sample Workflow Agents Application Note Parsing the Original File Name A customer wants to use the file name of the first page in a Document and parse out its individual parts into a series of Index Fields. foreach (ACDataElement oDoc in oDocCol) { ACDataElement oPages = oDoc. Kofax.DBLiteOpt.CurrentModule. separated with underscore characters. ACDataElement oPart2 = oIndexes.ToLower() == "scan.ACWorkflowData oWorkflowData) { if (oWorkflowData. For this example.ID.Text. Page 17 . System. ACDataElementCollection oPagesCol = oPages. Part2 and Part3. ACDataElement oPart1 = oIndexes. "Name". the first Page in the Pages collection is acquired and its “OriginalFilename” attribute is read and parsed using the Split method.FindChildElementByName("IndexFields").ACWFLib.GetOriginalFilename")] public class GetOriginalFilename : _IACWorkflowAgent { public void ProcessWorkflow(ref Kofax.Split('_')..FindChildElementByName("Pages"). Kofax.Windows. System. we are going to configure a Document Class with three Index Fields named Part1. ACDataElement oIndexes = oDoc.Runtime. System. ACDataElement oBatch = root. namespace WFAGetOriginalFilename { [Guid("AF07DF16-36F7-44c3-BF40-6BC293EA1D6B")] [ClassInterface(ClassInterfaceType.FindChildElementByName("Documents").FindChildElementByName("Batch"). "Name". oPart2["Value"] = BaseStr[1].FindChildElementByAttribute("IndexField". The string values are then written into the three Index Fields. } } } } Listing 9 For our example. ACDataElement oPart3 = oIndexes. Part1. "Part3").Generic.FindChildElementsByName("Page"). the filenames will be three strings concatenated together.Forms.ExtractRuntimeACDataElement(0).