Processware 2019 coding agent
Documentation
<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- Global Site Tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-110097722-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'UA-110097722-1');
</script>
<!-- Start of HubSpot Embed Code -->
<script type="text/javascript" id="hs-script-loader" async defer src="//js.hs-scripts.com/1901151.js"></script>
<!-- End of HubSpot Embed Code -->
<title>TaskContext | Processware Developer Guide </title>
<meta name="viewport" content="width=device-width">
<meta name="title" content="TaskContext | Processware Developer Guide ">
<meta name="generator" content="docfx ">
<link rel="shortcut icon" href="../../media/favicon.ico">
<link rel="stylesheet" href="../../styles/docfx.vendor.min.css">
<link rel="stylesheet" href="../../styles/docfx.css">
<link rel="stylesheet" href="../../styles/main.css">
<link rel="stylesheet" href="../../styles/flowcentric.css">
<meta property="docfx:navrel" content="../../toc.html">
<meta property="docfx:tocrel" content="../toc.html">
<meta property="flowcentric:siteVersion" content="1.0.0">
<meta property="flowcentric:version" content="1.1.0">
<meta property="docfx:rel" content="../../">
<meta property="docfx:newtab" content="true">
</head> <body data-spy="scroll" data-target="#affix" data-offset="120">
<div id="wrapper">
<header>
<nav id="autocollapse" class="navbar navbar-inverse ng-scope" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../../media/companyLogo_RGB.png" alt="">
</a>
</div>
<div class="collapse navbar-collapse" id="navbar">
<form class="navbar-form navbar-right" role="search" id="search">
<div class="form-group">
<input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
</div>
</form>
</div>
</div>
</nav>
<div class="subnav navbar navbar-default">
<div class="container hide-when-search" id="breadcrumb">
<ul class="breadcrumb">
<li></li>
</ul>
</div>
</div>
</header>
<div class="container body-content">
<div id="search-results">
<div class="search-list">Search Results for <span></span></div>
<div class="sr-items">
<p><i class="glyphicon glyphicon-refresh index-loading"></i></p>
</div>
<ul id="pagination" data-first=First data-prev=Previous data-next=Next data-last=Last></ul>
</div>
</div>
<div role="main" class="container body-content hide-when-search">
<div class="sidenav hide-when-search">
<a class="btn toc-toggle collapse" data-toggle="collapse" href="#sidetoggle" aria-expanded="false" aria-controls="sidetoggle">Show / Hide Table of Contents</a>
<div class="sidetoggle collapse" id="sidetoggle">
<div id="sidetoc"></div>
</div>
</div>
<div class="article row grid-right">
<div class="col-md-10">
<article class="content wrap" id="_content" data-uid="taskContext">
<h1 id="taskcontext"><em><strong>TaskContext</strong></em></h1>
<p>The <a class="xref" href="index.html"><strong>Managed Code</strong></a> counterpart of the <a class="xref" href="../vbScripting.html">VbScript</a> construct, <a class="xref" href="../fcContext.html"><em><strong>fcContext</strong></em></a>, is defined in the interface <code>FlowCentric.Engine.Managed.ITaskContext</code>. This interface is implemented by the types <code>FormContext</code> and <code>GroupContext</code> - both of which defines the base types for the parameters (indicated below with lines) presented to <code>TaskForm</code> and <code>ColumnsGroup</code> implementations respectively, as is shown here:</p>
<p><img src="media/taskContext.png" alt="Task Context Hierarchy"></p>
<p>The properties of this interface, and therefore that common to the base implementations are discussed next.</p>
<h2 id="businessrules">BusinessRules</h2>
<p>The <strong>BusinessRules</strong> property is a <a href="https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/reference-types#the-dynamic-type">dynamically typed</a> (or late-bound) instance providing access to the <a class="xref" href="../../manual/processSuite/businessRules.html"><em><strong>Business Rules</strong></em></a> and <a class="xref" href="../../manual/processSuite/businessRuleVariable.html"><em><strong>Business Rule Variables</strong></em></a> defined in the <a class="xref" href="../../manual/processSuite/introduction.html"><strong>Process Suite</strong></a> and deployed to the associated <a class="xref" href="../../manual/activityServer/introduction.html"><strong>Activity Server</strong></a>.</p>
<h3 id="how-do-i-access-a-business-rule-variable">How do I access a <a class="xref" href="../../manual/processSuite/businessRuleVariable.html">Business Rule Variable</a></h3>
<p>The value of a <a class="xref" href="../../manual/processSuite/businessRuleVariable.html"><strong>Business Rule Variable</strong></a> is retrieved as follows (in C#):</p>
<pre><code class="lang-c#">var value = (string)context.BusinessRules.VariableName;
</code></pre>
<p>where <code>VariableName</code> is the name of the variable to be retrieved. Every <a class="xref" href="../../manual/processSuite/businessRuleVariable.html"><strong>Business Rule Variable</strong></a> becomes a string-based property on the <em><strong>BusinessRules</strong></em> instance.</p>
<h3 id="how-do-i-invoke-a-business-rule">How do I invoke a <a class="xref" href="../../manual/processSuite/businessRules.html">Business Rule</a></h3>
<p>A <a class="xref" href="../../manual/processSuite/businessRules.html"><strong>Business Rule</strong></a> is invoked as follows (in C#):</p>
<pre><code class="lang-c#">var result = context.BusinessRules.RuleName(param1, ..., paramN);
</code></pre>
<p>where <code>RuleName</code> is the name of the <a class="xref" href="../../manual/processSuite/businessRules.html"><strong>Business Rule</strong></a> to be invoked. Every <a class="xref" href="../../manual/processSuite/businessRules.html"><strong>Business Rule</strong></a> becomes a method on the <em><strong>BusinessRules</strong></em> instance.</p>
<div class="WARNING">
<h5>Warning</h5>
<p>In both <a class="xref" href="../vbScripting.html">VbScript</a> and <a class="xref" href="index.html"><strong>Managed Code</strong></a>, <a class="xref" href="../../manual/processSuite/businessRules.html"><strong>Business Rule</strong></a> and <a class="xref" href="../../manual/processSuite/businessRuleVariable.html"><strong>Business Rule Variable</strong></a> are only evaluated at runtime (a technique referred to as late binding). Therefore any issues, including missing or renamed rules or variables, will only reflect at runtime.</p>
</div>
<h2 id="currentuser">CurrentUser</h2>
<p>The <strong>CurrentUser</strong> property is a <a href="https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/reference-types#the-dynamic-type">dynamically typed</a> (or late-bound) instance providing access to the information pertaining to the user currently participating in an <a class="xref" href="../activity/introduction.html">activity</a> instance.</p>
<h3 id="how-do-i-use-currentuser">How do I use CurrentUser</h3>
<p>The information pertaining to the current user can be accessed as follows (in C#):</p>
<pre><code class="lang-c#">// User specific information.
long id = context.CurrentUser.Id;
string userName = context.CurrentUser.Name;
string firstName = context.CurrentUser.FirstName;
string lastName = context.CurrentUser.LastName;
string emailAddress = context.CurrentUser.EmailAddress;
// Role information.
string role = context.CurrentUser.Role;
// Other user properties (based on user property definitions).
string value = context.CurrentUser.PropertyName
// Updating user properties (based on user property definitions).
context.CurrentUser.PropertyName = value;
await context.CurrentUser.SaveChangesAsync(cancellationToken);
</code></pre>
<h3 id="how-do-i-get-access-to-stores-related-functionality-via-currentuser">How do I get access to Stores-related functionality via CurrentUser</h3>
<p>The <a href="#stores">Stores</a>-based entity backing the current user enables <a href="#stores">Stores</a>-related functionality as shown (in C#):</p>
<pre><code class="lang-c#">// Get the user entity.
var currentUser = (User)context.CurrentUser;
// User specific information.
long id = currentUser.Id.GetValueOrDefault();
string userName = currentUser.Name;
string firstName = currentUser.FirstName;
string lastName = currentUser.LastName;
string emailAddress = currentUser.EmailAddress;
// Role information.
string role = currentUser.Role;
// Other user properties.
var properties = await context.Stores.Company.Users
.GetPropertiesForAsync(currentUser, cancellationToken);
foreach(var property in properties)
{
if(StringComparer.Ordinal.Equals(name1, property.Name))
{
value1 = property.Value;
}
else if(StringComparer.Ordinal.Equals(name2, property.Name))
{
// Update and save.
property.Value = value2;
await context.Stores.Company.Users
.SetPropertyForAsync(currentUser, property);
}
}
</code></pre>
<p>This code snippet presents functionality similar as before; far more is possible via the <a href="#stores">Stores</a> functionality presented later.</p>
<h2 id="parameters">Parameters</h2>
<p>The <strong>Parameters</strong> property is a <a href="https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/reference-types#the-dynamic-type">dynamically typed</a> (or late-bound) instance providing access to the task parameters associated with the currently executing <a class="xref" href="../activity/introduction.html">activity</a> instance. Task parameters is a collection of named values that can be created and modified on an <a class="xref" href="../activity/introduction.html">activity</a> instance to keep state that is local to that instance, and will always reflect the final changes made across client refreshes until the instance is finally completed. Clients can use task parameters to pass through client-specific and <a class="xref" href="../activity/introduction.html">activity</a>-specific values that will then be available on instance load. Instances can also pass parameters to subsequent instances on completion.</p>
<p><em><strong>Processware</strong></em> clients can specify a range of client-specific task parameters to <a class="xref" href="../activity/introduction.html">activities</a>, such as the form factor presented by the client (e.g. <a class="xref" href="../../manual/webNavigator/mobileSkin.html"><strong>mobile</strong></a>, <a class="xref" href="../../manual/webNavigator/desktopSkin.html"><strong>desktop</strong></a>), capabilities available to the client (e.g. web, thick client), and even the GPS location of the mobile device running a native client. Some of the more common scenarios enabled by client-specific task parameters are covered next (even though many more exist).</p>
<h3 id="how-do-i-augment-activities-based-on-client-specific-task-parameters">How do I augment Activities based on Client-Specific Task Parameters</h3>
<p>The <a class="xref" href="../../manual/webNavigator/introduction.html"><strong>Web Navigator</strong></a> adds two client-specific task parameters that are available to <a class="xref" href="../activity/introduction.html">activity</a> instances to make some informed decisions. The first is to indicate the type of client (a web client) indicating that it supports web-related functionality (such as HTML, CSS, and JavaScript). This would allow <a class="xref" href="../activity/introduction.html">activities</a> to use some of these web technologies, conversely the absence of this type could warn <a class="xref" href="../activity/introduction.html">activities</a> to not rely on web-related functionality adjusting themselves accordingly.</p>
<p>The second task parameter specified by the <a class="xref" href="../../manual/webNavigator/introduction.html"><strong>Web Navigator</strong></a> indicates the form factor currently in use, and includes the values, <code>Desktop</code> and <code>Phone</code>. Consider a scenario where an <a class="xref" href="../activity/introduction.html">activity</a> has been designed in such a way as to neatly group related content horizontally as (fictitiously) shown here:</p>
<p><img src="../media/vbscript_CI_WebNavigator.png" alt="Web Navigator"></p>
<p>Note how the combination of <a class="xref" href="../objects/index.html">object</a> indexes and column numbers dictate the rendered positions of the controls. Designing such an <a class="xref" href="../activity/introduction.html">activity</a> could look something like what is shown next (note that the <a class="xref" href="../objects/index.html">objects</a> have been ordered by column number to indicate the rendering presented above).</p>
<p><img src="../media/vbscript_CI_ProcessSuite.png" alt="Process Suite"></p>
<p>The <a class="xref" href="../../manual/webNavigator/introduction.html"><strong>Web Navigator</strong></a>, when rendering on a mobile device, ignores column numbers presenting <a class="xref" href="../objects/index.html">objects</a> within a single column – the <a class="xref" href="../objects/index.html">objects</a> are still rendered based on their indexes – and would therefore render the same <a class="xref" href="../activity/introduction.html">activity</a> as shown next:</p>
<p><img src="../media/vbscript_CI_Mobile_Default.png" alt="Mobile Default"></p>
<p>This will clearly not do! The <a class="xref" href="../../manual/webNavigator/introduction.html"><strong>Web Navigator</strong></a> sends the value <code>Phone</code> for task parameter <code>FormFactor</code> in this case, allowing the <a class="xref" href="../activity/introduction.html">activity</a> to be adjusted accordingly. The simplest solution might be to hide all the <a class="xref" href="../objects/index.html">objects</a> and display a friendly unsupported message. Another approach would be to swap the indexes of the <a class="xref" href="../objects/index.html">objects</a> resulting in the following (much more friendly) rendition:</p>
<p><img src="../media/vbscript_CI_Mobile_Friendly.png" alt="Mobile Friendly"></p>
<p>And all that is required is the following bit of (transformational) magic</p>
<pre><code class="lang-c#">if(StringComparer.Ordinal.Equals("Phone",
(string)context.Parameters.FormFactor))
{
// Change Layout From:
// H01 H02 H03 H04
// C11 C21 C31 C41
// C12 C22 C32 C42
// C13 C23 C33 C43
// To:
// H01 C11 C12 C13
// H02 C21 C22 C23
// H03 C31 C32 C33
// H04 C41 C42 C43
}
</code></pre>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Be careful with modifying the indexes of <a class="xref" href="../objects/index.html">objects</a>, as no duplicate indexes are allowed – this will result in a runtime error.</p>
</div>
<p>Other scenarios might include hiding redundant (informative-only) <a class="xref" href="../objects/index.html">objects</a> for mobile clients to ease the flow in completing the instances on these smaller form factors. As the lines of <a class="xref" href="../objectGroup/introduction.html">Object Groups</a> are mostly rendered as buttons opening the line in a new window, care should be taken when designing <a class="xref" href="../activity/introduction.html">activities</a> to make it friendly on smaller form factors. And, obviously, the number of <a class="xref" href="../objectGroup/introduction.html">object groups</a> also play an important role in the design. A <a class="xref" href="../objects/browsePage/introduction.html">Browse Page</a> is also mostly opened in a new window, and the use of these should also be kept to a minimum where possible (to preserve a user’s logical flow through the rendered <a class="xref" href="../activity/introduction.html">activity</a> instance).</p>
<h3 id="how-do-i-use-task-parameters-to-keep-state">How do I use Task Parameters to Keep State</h3>
<p>As mentioned earlier, task parameters can be used to keep state between the possibly many refreshes that might occur before an <a class="xref" href="../activity/introduction.html">activity</a> instance is finally completed. As an example, hidden <a class="xref" href="../objects/referenceLabel/introduction.html">Reference Labels</a> were used extensively in the past to persist, amongst other things, values of <a class="xref" href="../objects/index.html">objects</a> sent to a client as to determine whether some value was changed by a user. On refresh the hidden <a class="xref" href="../objects/referenceLabel/introduction.html">Reference Labels</a> were compared with the values of the <a class="xref" href="../objects/index.html">objects</a>, and changes were handled accordingly, the <a class="xref" href="../objects/referenceLabel/introduction.html">Reference Labels</a> would then be refreshed before the <a class="xref" href="../activity/introduction.html">activity</a> is send back to the client. <strong>Task Parameters</strong> obsoletes these techniques.</p>
<p>How do I set the value of a task parameter?</p>
<pre><code class="lang-c#">context.Parameters.Name = "Value";
</code></pre>
<p>How do I get the value of a task parameter?</p>
<pre><code class="lang-c#">var value = context.Parameters.Name;
</code></pre>
<h3 id="how-do-i-pass-parameters-to-subsequent-activities">How do I pass parameters to subsequent activities</h3>
<p>The string-based <a class="xref" href="../vbScripting.html">VBScript</a> variable, <a class="xref" href="../vbScripting.html#fctrigger"><em><strong>fcTrigger</strong></em></a>, that is used on <a class="xref" href="../objects/commandOption/introduction.html">Command Options</a> to designate the <a class="xref" href="../activity/introduction.html">activities</a> that are to follow the current instance (and when the specific <a class="xref" href="../objects/commandOption/introduction.html">Command Option</a> is selected) also allows sending parameters to the designated <a class="xref" href="../activity/introduction.html">activity</a> instances. In the following example, the instance based on the <a class="xref" href="../activity/introduction.html">activity</a> with the specified name will receive the parameters named <code>name1</code> and <code>name2</code> with the values <code>value1</code> and <code>value2</code>, respectively.</p>
<pre><code class="lang-vbscript">fcTrigger = "NextActivity::::::name1=value1;name2=value2"
</code></pre>
<h3 id="how-can-i-communicate-with-a-subsequent-activity-instance">How can I communicate with a subsequent activity instance</h3>
<p><a class="xref" href="index.html"><strong>Managed Code</strong></a> do not currently support process flow logic directly, requiring the use of the <a class="xref" href="../vbScripting.html">VBScript</a> variable <a class="xref" href="../vbScripting.html#fctrigger"><em><strong>fcTrigger</strong></em></a>. Communication between a <code>TaskForm</code> and its associated <a class="xref" href="../activity/introduction.html">activity</a> is, however, enabled via <strong>Parameters</strong> using the following.</p>
<pre><code class="lang-c#">context.Parameters.Trigger = "NextActivity::::::name1=value1;name2=value2";
</code></pre>
<p>And on the associated <a class="xref" href="../objects/commandOption/introduction.html">Command Option</a>:</p>
<pre><code class="lang-vbscript">fcTrigger = fcContext.Task.Parameters.ValueOf("Trigger")
</code></pre>
<div class="NOTE">
<h5>Note</h5>
<p>The parameter name, <code>Trigger</code>, is an arbitrarily chosen name and has no specific meaning. The only requirement is that both the <a class="xref" href="index.html"><strong>Managed Code</strong></a> and the <a class="xref" href="../vbScripting.html">VBScript</a> should use the same name.</p>
</div>
<p>On the <a class="xref" href="../vbScripting.html">VBScript</a> of the next <a class="xref" href="../activity/introduction.html">activity</a>, these values can be read, as in</p>
<pre><code class="lang-vbscript">value1 = fcContext.Task.Parameters.ValueOf("name1")
value2 = fcContext.Task.Parameters.ValueOf("name2")
</code></pre>
<p>And in <a class="xref" href="index.html"><strong>Managed Code</strong></a> these will be available as shown here</p>
<pre><code class="lang-c#">string value1 = context.Parameters.Name1;
string value2 = context.Parameters.Name2;
</code></pre>
<h2 id="process">Process</h2>
<p>The <strong>Process</strong> property exposes information pertaining to the currently running process instance. A process is instantiated during the execution of one of the <a class="xref" href="../objects/commandOption/introduction.html">Command Options</a> on an <a class="xref" href="../activity/introduction.html">activity</a> designated as process starter (as in <a class="xref" href="../activity/composition.html#creation-type">Creation Type</a> set to <code>First Activity</code>).</p>
<h3 id="how-do-i-use-process-to-access-process-instance-related-information">How do I use <em><strong>Process</strong></em> to access process instance related information</h3>
<p>The design-time information pertaining to the current process instance is available using the following (in C#):</p>
<pre><code class="lang-c#">long designTimeId = context.Process.DesignTimeId;
string name = context.Process.Name;
string description = context.Process.Description;
Version version = context.Process.Version;
</code></pre>
<p>Once a process is instantiated, its identifier can be accessed as follows (in C#):</p>
<pre><code class="lang-c#">long instanceId = context.Process.Id;
</code></pre>
<p>This is mostly used to associate custom data with an instance, to allow tracking changes to process instances.</p>
<div class="NOTE">
<h5>Note</h5>
<p>The id is invalidated when any (either validation or other) error occurs in the <a class="xref" href="../objects/commandOption/introduction.html">Command Option</a> of the first (<a class="xref" href="../activity/composition.html#creation-type">Creation Type</a> set to <code>First Activity</code>) <a class="xref" href="../activity/introduction.html">activity</a>.</p>
</div>
<p>The <code>TaskContextExtensions</code> class adds additional functionality to an <code>ITaskContext</code> implementation enabling the traversal of the entire process instance as collection of <code>IImmutableTask</code> instances. As an example of its use, consider the following:</p>
<pre><code class="lang-c#">var root = context.Root(); // Root is the first instance in the process tree.
var startedAt = root.DateActivated; // When process instance was started.
var startedBy = root.Originator; // Who started the process instance.
</code></pre>
<p>Unlike its <a class="xref" href="../vbScripting.html">VbScript</a> counterpart, <a class="xref" href="../vbScripting.html#process">Process</a>, there is not a method for calculating the number of pending <a class="xref" href="../activity/introduction.html">activity</a> instances. But with the functionality available the implementation is simply:</p>
<pre><code class="lang-c#">private static int CountNumberOfPendingTaskInstances(
ITaskContext context,
string? taskName = null) =>
CountNumberOfPendingTaskInstances(context, context.Root(), taskName);
private static int CountNumberOfPendingTaskInstances(
ITaskContext context,
IImmutableTask task,
string? taskName)
{
var count = 0;
foreach(var child in context.ChildrenOf(task))
{
if(child.DateCompleted is null) // Pending Task.
{
if(String.IsNullOrEmpty(taskName) // All.
|| StringComparer.OrdinalIgnoreCase.Equals(
taskName, child.Name)) // Specific.
{
count++;
}
}
else
{
// Traverse next level.
count += CountNumberOfPendingTaskInstances(context, child, taskName);
}
}
return count;
}
</code></pre>
<p>The method above calculates the number of pending <a class="xref" href="../activity/introduction.html">activity</a> instances - either all of them or only those with a specific name - in the process instance. This is mostly used to prevent a certain number of process branches, duplicated due to splits, from continuing past a certain point. In the following snippets, all branches that include the <a class="xref" href="../activity/introduction.html">activity</a> with the <a class="xref" href="../objects/commandOption/introduction.html">Command Option</a> with the <code>Trigger</code> script specified will be archived, and therefore not continue, until there is only one such branch left and only this last branch will be allowed to continue.</p>
<pre><code class="lang-c#">if(CountNumberOfPendingTasks(context, Form.Name) == 0)
{
context.Parameters.Trigger = "[NextActivity]";
}
else
{
context.Parameters.Trigger = String.Empty;
}
</code></pre>
<pre><code class="lang-vbscript">fcTrigger = fcContext.Task.Parameters.ValueOf("Trigger")
</code></pre>
<p>This effectively 'mimics' a process join - the remainder of the branch will not have direct access to the <a class="xref" href="../objects/index.html">object</a> values persisted with the other, parallel, branches, as would have been the case with a proper process join. The use of <a href="#parameters"><strong>Parameters</strong></a> for communicating back with the <a class="xref" href="../activity/introduction.html">activity</a> instance was discussed <a href="#how-can-i-communicate-with-a-subsequent-activity-instance">previously</a>.</p>
<h2 id="resources">Resources</h2>
<p>The <strong>Resources</strong> property provides access to the <a class="xref" href="../../manual/processSuite/tools.html#custom-resources"><strong>custom language resources</strong></a> as maintained in the <a class="xref" href="../../manual/processSuite/introduction.html"><strong>Process Suite</strong></a>.</p>
<h3 id="how-do-i-access-language-resources">How do I access language resources</h3>
<p>Resources specific to some language is available once the specific <code>LanguageResource</code> instance is retrieved, which can be done in one of the following ways</p>
<pre><code class="lang-c#">// The current language resource.
var languageResources = context.Resources.Current;
// Locating a specific language resource (by name or id).
var languageId = 1L;
var languageCode = "DEFAULT";
foreach(var resources in context.Resources.All)
{
if(languageId == resources.Id)
{
languageResources = resources;
break;
}
if(StringComparer.OrdinalIgnoreCase.Equals(languageCode, resources.Code))
{
languageResources = resources;
break;
}
}
</code></pre>
<p>Any execution occurs in some language-related context and access to the context-specific current language is provided, as shown. Locating a specific language resource by name or id is also similar to what has been shown above.</p>
<h3 id="how-do-i-use-language-resources">How do I use language resources</h3>
<p>Once the <code>LanguageResources</code> instance is retrieved, it can be used to access custom resources as defined in the <a class="xref" href="../../manual/processSuite/tools.html#custom-resources"><strong>Process Suite</strong></a>. Custom resources are accessed using the set (container) name in conjunction with the resource name, with string-based resources accessed as shown next:</p>
<pre><code class="lang-c#">string resourceValue = languageResource.Custom.Set.Name.Resource;
</code></pre>
<p>The <strong>Custom</strong> property is a <a href="https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/reference-types#the-dynamic-type">dynamically typed</a> (or late-bound) instance that allows access to resource values using their full path ("Set.Name" for the set name, and "Resource" for the resource name in the example above).</p>
<p>The <code>LanguageResources</code> also includes a <em>Culture</em> property which is a Microsoft.NET <code>CultureInfo</code> instance for use in culture-aware code as shown here.</p>
<pre><code class="lang-c#">// Current full date:
var dateString = String.Format(
languageResources.Culture,
"{0:F}",
DateTime.Now);
// Or simply:
dateString = DateTime.Now.ToString("F", languageResources.Culture);
// Welcome message:
var welcome = String.Format(
languageResources.Culture,
"Hi {0} {1}!",
title,
surname));
</code></pre>
<h2 id="settings">Settings</h2>
<p>The <strong>Settings</strong> property provides access to the server and custom settings as maintained in the <a class="xref" href="../../manual/processSuite/processwareSettings.html"><strong>Process Suite</strong></a>. Server settings are exposed via the property <code>Server</code> on <strong>Settings</strong>, while custom settings are exposed via the property <code>Custom</code>. These properties are <a href="https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/reference-types#the-dynamic-type">dynamically typed</a> (or late-bound) instances allowing access to the associated setting values, as properties on these instances. The list of commonly used server settings and how to access them follows:</p>
<table>
<thead>
<tr>
<th style="text-align: left;"><strong>Source</strong></th>
<th style="text-align: left;"><strong>Name</strong></th>
<th style="text-align: left;"><strong>Accessed Using</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">web.config</td>
<td style="text-align: left;">activityServer\</td>
<td style="text-align: left;">N/A</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">@environment</td>
<td style="text-align: left;">context.Settings.Server.IsProductionEnvironment</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">repository\virtualDirectory</td>
<td style="text-align: left;">context.Settings.Server.RepositoryUrl</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">repository\physicalPath</td>
<td style="text-align: left;">context.Settings.Server.RepositoryDirectory</td>
</tr>
<tr>
<td style="text-align: left;">datastore</td>
<td style="text-align: left;">Engine</td>
<td style="text-align: left;">N/A</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">ClientAddress</td>
<td style="text-align: left;">context.Settings.Server.ClientAddress</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">CompanyName</td>
<td style="text-align: left;">context.Settings.Server.CompanyName</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">ServerAddress</td>
<td style="text-align: left;">context.Settings.Server.ServerAddress</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">SharePointRepositoryPath</td>
<td style="text-align: left;">context.Settings.Server.SharePointRepositoryPath</td>
</tr>
<tr>
<td style="text-align: left;"></td>
<td style="text-align: left;">SharePointSiteUrl</td>
<td style="text-align: left;">context.Settings.Server.SharePointSiteUrl</td>
</tr>
</tbody>
</table>
<p>Typical use cases for using these settings include the ability to write portable and more maintainable <a class="xref" href="index.html">Managed Code</a>, as highlighted by the following examples.</p>
<h3 id="how-do-i-map-an-uploaded-file-to-its-physical-location">How do I map an uploaded file to its physical location</h3>
<p>A <a class="xref" href="../objects/fileAttachment/introduction.html">File Attachment</a> with <a class="xref" href="../objects/fileAttachment/objectComposition.html#upload-file-to">Upload File To</a> property set to <code>FlowCentric</code> will be uploaded to the physical path as configured in an <strong>Activity Server’s</strong> web.config; the resulting value will be a URI relative to the virtual directory under the server’s address. The following snippet maps the <a class="xref" href="../objects/fileAttachment/introduction.html">File Attachment’s</a> value to one or more physical uploaded file paths.</p>
<pre><code class="lang-c#">string physicalPath = context.Settings.Server.RepositoryDirectory;
var uploadedRootPath =
(string)context.Settings.Server.ServerAddress
+ (string)context.Settings.Server.RepositoryUrl;
foreach(var filePath in uploadedValue.Split("|".ToCharArray(),
StringSplitOptions.RemoveEmptyEntries))
{
var fullFilePath = filePath.Replace(uploadedRootPath, physicalPath);
}
</code></pre>
<h3 id="how-to-i-create-a-uri-for-a-path-relative-to-some-client">How to I create a URI for a path relative to some client</h3>
<p>It is often necessary to create a URI relative to a client to reference some custom content. Using a combination of custom (discussed next) and server settings makes this easy.</p>
<pre><code class="lang-c#">var contentRelativePath = "..."; // initialize from custom settings.
// ...
var contentFullPath =
context.Settings.Server.ClientAddress
+ contentRelativePath;
</code></pre>
<p>Working with custom settings is as easy as working with server settings, the main difference lies with the fact that custom settings define a hierarchy rooted in the Company custom settings group. Without any customization, the Company setting parents a collection of connection and communication settings.</p>
<h3 id="how-do-i-retrieve-a-configured-custom-connection-string">How do I retrieve a configured custom connection string</h3>
<p>As connection strings are usually configured under the <code>Connections</code> custom settings group, accessing a specific connection string is performed by simply:</p>
<pre><code class="lang-c#">string connectionString =
context.Settings.Company.Connections.ConnectionString;
</code></pre>
<p>The connection setting is referenced directly by specifying the full path to it.</p>
<h2 id="stores">Stores</h2>
<p>The <strong>Stores</strong> property is a container providing access to predefined stores, for <em><strong>Processware</strong></em>-related entities and integration catalogs.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>The <a class="xref" href="../../manual/integrationService/introduction.html"><strong>Integration Service</strong></a> with its related catalogs is considered a <a class="xref" href="../vbScripting.html">VbScript</a>-enabling integration technology as <a class="xref" href="index.html">Managed Code</a> comes with a plethora of technologies that targets the Microsoft.NET Framework. <a class="xref" href="../fcContext.html#integration-catalogs">Integration Catalogs</a> is therefore presented as part of the <a class="xref" href="../vbScripting.html">VbScript</a> object <a class="xref" href="../fcContext.html">fcContext</a>.</p>
</div>
<h3 id="processware-stores">Processware Stores</h3>
<p>The <strong>Company</strong> store exposes a small number of <em><strong>Processware</strong></em>-related entities, specifically participants and licensing related entities, permitting extending the <em><strong>Processware</strong></em> design-time environment to customized runtime processes.</p>
<h4 id="license">License</h4>
<p>The first entity exposed on the <strong>Company</strong> store provides read-only access to the licensing information pertaining to the <em><strong>Processware</strong></em> server.</p>
<h5 id="how-do-i-retrieve-the-processware-servers-license">How do I retrieve the Processware server’s license</h5>
<p>The following snippet retrieves the representation of the current <em><strong>Processware</strong></em> server’s license.</p>
<pre><code class="lang-c#">var license = context.Stores.Company.License;
</code></pre>
<p>The resulting object provides detail regarding the license for the <em><strong>Processware</strong></em> server and is discussed next.</p>
<h5 id="how-do-i-retrieve-the-current-and-maximum-number-of-licensed-users-allowed-on-the-server">How do I retrieve the current and maximum number of licensed users allowed on the server</h5>
<p>Where applicable, the current and maximum number of users allowed by the license is available using the following:</p>
<pre><code class="lang-c#">var currentCount = license.CurrentNumberOfUsers;
var licensedCount = license.LicensedNumberOfUsers;
</code></pre>
<h5 id="how-do-i-retrieve-the-current-and-maximum-number-of-licensed-processes-allowed-on-the-server">How do I retrieve the current and maximum number of licensed processes allowed on the server</h5>
<p>Where applicable, the current and maximum number of processes allowed by the license is available using the following:</p>
<pre><code class="lang-c#">var currentCount = license.CurrentNumberOfProcesses;
var licensedCount = license.LicensedNumberOfProcesses;
</code></pre>
<h5 id="how-do-i-retrieve-the-number-of-days-left-before-the-license-expires">How do I retrieve the number of days left before the license expires</h5>
<p>The number of days available before the license expires can be retrieved using the following:</p>
<pre><code class="lang-c#">var daysLeft = license.NumberOfDaysLeft;
</code></pre>
<h4 id="users">Users</h4>
<p>The <strong>Users</strong> property provides access to the User store that includes functionality for the creation, retrieval, modification and retiring of Users. The modifications allowed include properties on Users as well as passwords, their associated self-relationships, and their participant-related memberships and relationships.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p><strong>Roles</strong> in <a class="xref" href="index.html">Managed Code</a> is surfaced as a string-based property on Users to reflect the one-to-one mapping between Users and Roles (and to be forward compatible with the next major version of <em><strong>Processware</strong></em>). Role Groups, grouping Roles, is replaced by <a href="#participants">Participants</a> grouping Users.</p>
</div>
<h5 id="how-do-i-retrieve-a-collection-of-processware-users">How do I retrieve a collection of Processware users</h5>
<p>The <strong>Users</strong> store provides the following mechanisms to retrieve a collection of users.</p>
<pre><code class="lang-c#">// Get all users on the server.
var users = await context.Stores.Company.Users
.GetAllAsync(cancellationToken, activeOnly: false);
// Get all active users on the server.
users = await context.Stores.Company.Users
.GetAllAsync(cancellationToken, activeOnly: true);
// Filter by the following (optional) values.
userName = "UserName"; // If empty, excluded from filter.
firstName = "FirstName"; // If empty, excluded from filter.
lastName = "LastName"; // If empty, excluded from filter.
users = await context.Stores.Company.Users
.GetByAsync(userName, firstName, lastName, cancellationToken);
// Get all users with the specified role name.
users = await context.Stores.Company.Users
.GetByAsync("RoleName", cancellationToken);
</code></pre>
<p>Due to the mentioned self-relationships on Users, a collection of Users can also be retrieved based on reversing these relationships, as in:</p>
<pre><code class="lang-c#">users = await context.Stores.Company.Users
.GetUsersReportingToAsync(user, cancellationToken);
</code></pre>
<p>The final method available for retrieval are based on <a href="#participants">Participant</a> memberships, as in:</p>
<pre><code class="lang-c#">users = await context.Stores.Company.Users
.GetMembersOfAsync(participant, activeOnly, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<h5 id="how-do-i-retrieve-individual-processware-users">How do I retrieve individual Processware users</h5>
<p>Retrieving a specific User, as opposed to a collection of Users, requires either the user’s id or name as in:</p>
<pre><code class="lang-c#">// Locate by user id.
var user = await context.Stores.Company.Users
.GetByIdAsync(userId, cancellationToken);
// Locate by user name.
user = await context.Stores.Company.Users
.GetByNameAsync(userName, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<h5 id="ho-do-i-retrieve-the-processware-user-relationships-on-users">Ho do I retrieve the Processware user relationships on users</h5>
<p>To retrieve the <em><strong>Processware</strong></em> relationships on Users, the following can be used:</p>
<pre><code class="lang-c#">var reportTo = await context.Stores.Users
.GetReportToAsync(user, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<h5 id="how-do-i-use-processware-users">How do I use Processware users</h5>
<p>The <em><strong>Processware</strong></em>-specific user object provides a simplified representation of a User in <em><strong>Processware</strong></em>. The following properties exist directly on the user, and where specified the associated properties may be changed.</p>
<table>
<thead>
<tr>
<th style="text-align: left;"><strong>Name</strong></th>
<th style="text-align: left;"><strong>Type</strong></th>
<th style="text-align: left;"><strong>Editable</strong></th>
<th style="text-align: left;"><strong>Description</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">Id</td>
<td style="text-align: left;">long?</td>
<td style="text-align: left;">No</td>
<td style="text-align: left;">Unique identifier for the User.</td>
</tr>
<tr>
<td style="text-align: left;">Name</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">The unique user name of the User.</td>
</tr>
<tr>
<td style="text-align: left;">FirstName</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">First name of the User.</td>
</tr>
<tr>
<td style="text-align: left;">LastName</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Last name (surname) of the User.</td>
</tr>
<tr>
<td style="text-align: left;">EmailAddress</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Email address of the User.</td>
</tr>
<tr>
<td style="text-align: left;">IsAvailable</td>
<td style="text-align: left;">bool</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Indicates whether the User is currently available.</td>
</tr>
<tr>
<td style="text-align: left;">IsAdministrator</td>
<td style="text-align: left;">bool</td>
<td style="text-align: left;">No</td>
<td style="text-align: left;">Indicates whether the User is an Administrator.</td>
</tr>
<tr>
<td style="text-align: left;">IsActive</td>
<td style="text-align: left;">bool</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Indicates whether the User is currently active.</td>
</tr>
<tr>
<td style="text-align: left;">HasBeenLocked</td>
<td style="text-align: left;">bool</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Indicates whether the User’s account has been locked.</td>
</tr>
<tr>
<td style="text-align: left;">ShouldBeNotified</td>
<td style="text-align: left;">bool</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Indicates whether the User should be notified by a <a class="xref" href="../../manual/notificationEscalation/introduction.html"><strong>Notification & Escalation Service</strong></a> of activity instances (in various states).</td>
</tr>
<tr>
<td style="text-align: left;">WorkTelephoneNumber</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Work telephone number of the User.</td>
</tr>
<tr>
<td style="text-align: left;">HomeTelephoneNumber</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Home telephone number of the User.</td>
</tr>
<tr>
<td style="text-align: left;">CellphoneNumber</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Mobile number of the User.</td>
</tr>
<tr>
<td style="text-align: left;">SmsAddress</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">Alternative email address for SMS messages.</td>
</tr>
</tbody>
</table>
<p>Any changes made to either a new or an existing user will not be persisted until it has been explicitly saved, as described later.</p>
<h5 id="how-do-i-create-new-processware-users">How do I create new Processware users</h5>
<p>Creating a new <em><strong>Processware</strong></em> user is done via the following:</p>
<pre><code class="lang-c#">var user = await context.Stores.Company.Users.CreateAsync(cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<div class="NOTE">
<h5>Note</h5>
<p>The resulting user is not persisted to the data store until explicitly saved, as described next.</p>
</div>
<h5 id="how-do-i-save-changes-made-on-processware-users">How do I save changes made on Processware users</h5>
<p>Any modifications made on new (see the previous section) or existing <em><strong>Processware</strong></em> users will only be persisted once saved. This is done using the associated operation on the <strong>Users</strong> store:</p>
<pre><code class="lang-c#">await context.Stores.Company.Users.SaveAsync(user, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<h5 id="how-do-i-specify-or-change-the-password-for-processware-users">How do I specify or change the password for Processware users</h5>
<p>A new <em><strong>Processware</strong></em> user requires a password, and an existing user’s password might need to be changed, this can be done as follows</p>
<pre><code class="lang-c#">await context.Stores.Company.Users
.ChangePasswordAsync(user, password, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<div class="NOTE">
<h5>Note</h5>
<p>The password should not contain XML characters, such as <code><</code>, <code>&</code> and <code>></code> that is not web-friendly, and should be cleansed for the associated user to be able to sign in.</p>
</div>
<h5 id="how-do-i-safely-retire-processware-users">How do I (safely) retire Processware users</h5>
<p>Since Users cannot be deleted once they have participated in processes, and in general it is not recommended to delete Users, a method has been made available allowing <em><strong>Processware-</strong></em> users to be safely retired, while keeping historical data intact.</p>
<pre><code class="lang-c#">// Retires the user specified.
await context.Stores.Company.Users
.RetireAsync(user, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<div class="NOTE">
<h5>Note</h5>
<p>A retired User does not add to the number of licensed Users.</p>
</div>
<h5 id="how-do-i-access-and-modify-processware-users-roles">How do I access and modify Processware Users' Roles</h5>
<p>To retrieve the <em><strong>Processware</strong></em> Role associated with a User, the following can be used:</p>
<pre><code class="lang-c#">var role = user.Role;
</code></pre>
<p>A User in <em><strong>Processware</strong></em> can only participate in a process once it has been assigned to a Role (and that is a member of some Role Group, effectively realizing a <a href="#participants">Participant</a>); associating a Role with a User is done as follows:</p>
<pre><code class="lang-c#">user.Role = role;
</code></pre>
<div class="IMPORTANT">
<h5>Important</h5>
<p>The resulting User is not persisted to the data store until explicitly saved, as described previously.</p>
</div>
<h5 id="user-property-definitions">User Property Definitions</h5>
<p>The <strong>Users</strong> Store facilitates in managing the metadata on Users, providing functionality to create, retrieve and modify metadata, and associate these with Users. Metadata in its current form consists of two parts, the first defines the User-agnostic form, and the second associates a value for the definition with a specific User.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p><strong>Role Metadata</strong> in <a class="xref" href="index.html">Managed Code</a> is surfaced as User Property Definitions associated with Users to reflect the one-to-one mapping between Users and Roles (and to be forward compatible with the next major version of <em><strong>Processware</strong></em>).</p>
</div>
<h6 id="how-do-i-retrieve-a-collection-of-processware-user-property-definitions">How do I retrieve a collection of Processware user property definitions</h6>
<p>The <strong>Users</strong> Store provides the following mechanisms to retrieve a collection of user property definitions:</p>
<pre><code class="lang-c#">// Get all properties (as definitions) on the server.
var definitions = await context.Stores.Company.Users
.GetAllPropertyDefinitionsAsync(cancellationToken);
// Get all properties for the specified user.
var properties = await context.Stores.Company.Users
.GetPropertiesForAsync(user, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<p>In the first case, the resulting collection contains User-agnostic properties (or definitions), whilst in the second case the collection is specific to the User specified.</p>
<p>The <em><strong>Processware</strong></em> user property instance implements a name-value pair where only the value may be modified. Modifications are automatically persisted on creation and/or (re-)association with a specific User.</p>
<h6 id="how-do-i-create-new-or-update-existing-processware-user-property--definitions">How do I create new or update existing Processware user property definitions</h6>
<p>Creating a new <em><strong>Processware</strong></em> user property definition is done by using the following:</p>
<pre><code class="lang-c#">var definition = await context.Stores.Users
.CreatePropertyDefinitionAsync(
name,
isEncrypted,
description,
defaultValue,
cancellationToken);
</code></pre>
<p>The same method is used for updating an existing property definition, using the name of the existing instance.</p>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<div class="IMPORTANT">
<h5>Important</h5>
<p>The resulting definition is persisted immediately, and the resulting instance can then be explicitly (re-)associated with Users to specify User-specific values associated with the definition, as shown next.</p>
</div>
<h6 id="how-do-i-associate-processware-user-property-definitions-with-users">How do I associate Processware user property definitions with users</h6>
<p>A user property definition is specialized for Users by overwriting the default value of the former and then associating it with the applicable User. This is done as shown here.</p>
<pre><code class="lang-c#">// Override default (or current) value.
definition.Value = "Specific to User";
// (Re-)Associate with specific user, persisting accordingly.
await context.Stores.Company.Users
.SetPropertyForAsync(user, definition, cancellationToken);
</code></pre>
<p>If the definition specified was not associated with the specified User before, as in the case of a new definition, a new property for that User is persisted accordingly. If it was associated before, the modified value is persisted accordingly.</p>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Users</strong> store also provides synchronous versions for the above.</p>
</div>
<h4 id="participants">Participants</h4>
<p>The <strong>Participants</strong> property provides access to the Participants store that includes functionality for the creation, retrieval and modification of Participants. The modifications allowed include properties on Users as well as passwords, their associated self-relationships, and their user-based memberships and relationships.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p><a href="#participants">Participants</a> replaces role groups in the next major version of <em><strong>Processware</strong></em>, as a collection of <a href="#users">Users</a> (as opposed to the collection of Roles with a one-to-one mapping to <a href="#users">Users</a>).</p>
</div>
<h5 id="how-do-i-retrieve-a-collection-of-processware-participants">How do I retrieve a collection of Processware participants</h5>
<p>The <strong>Participants</strong> Store provides the following mechanisms to retrieve a collection of Participants:</p>
<pre><code class="lang-c#">// Get all participants on the server.
var participants = await context.Stores.Company.Participants
.GetAllAsync(cancellationToken);
// Filter by the following (optional) values.
name = "RoleGroup"; //If empty, excluded from filter.
participants = await context.Stores.Company.Participants
.GetByAsync(name, cancellationToken);
</code></pre>
<p>As Participants collects Users, by definition, the collection of Participants that include a specific User as member can be retrieved as follows:</p>
<pre><code class="lang-c#">// Get participants of which the specified user is a member.
participants = await context.Stores.Company.Participants
.GetParticipantsForAsync(user, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Participants</strong> store also provides synchronous versions for the above.</p>
</div>
<h4 id="how-do-i-retrieve-individual-processware-participants">How do I retrieve individual Processware participants</h4>
<p>Retrieving a specific Participant, as opposed to a collection of Participants, requires either the Participant’s id or name as in:</p>
<pre><code class="lang-c#">// Locate by id.
var participant = await context.Stores.Company.Participants
.GetByIdAsync(id, cancellationToken);
// Locate by name.
participant = context.Stores.Company.Participants
.GetByNameAsync(name, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Participants</strong> store also provides synchronous versions for the above.</p>
</div>
<h4 id="how-do-i-use-processware-participants">How do I use Processware participants</h4>
<p>The <em><strong>Processware</strong></em>-specific participant provides a simplified representation of a Participant in <em><strong>Processware</strong></em>. The following properties exist directly on the Participant, and where specified the associated properties may be changed.</p>
<table>
<thead>
<tr>
<th style="text-align: left;"><strong>Name</strong></th>
<th style="text-align: left;"><strong>Type</strong></th>
<th style="text-align: left;"><strong>Editable</strong></th>
<th style="text-align: left;"><strong>Description</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">Id</td>
<td style="text-align: left;">long?</td>
<td style="text-align: left;">No</td>
<td style="text-align: left;">Unique identifier of the Participant.</td>
</tr>
<tr>
<td style="text-align: left;">Name</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">The unique name of the Participant.</td>
</tr>
<tr>
<td style="text-align: left;">Description</td>
<td style="text-align: left;">string</td>
<td style="text-align: left;">Yes</td>
<td style="text-align: left;">The unique description of the Participant.</td>
</tr>
</tbody>
</table>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Any changes made to either a new or an existing participant will not be persisted until it has been explicitly saved, as described later.</p>
</div>
<h4 id="how-do-i-create-new-processware-participant">How do I create new Processware participant</h4>
<p>Creating new <em><strong>Processware</strong></em> Participant is done by using the following:</p>
<pre><code class="lang-c#">var participant = context.Stores.Company.Participants.Create()
</code></pre>
<div class="IMPORTANT">
<h5>Important</h5>
<p>The resulting participant is not persisted to the data store until explicitly saved, as described next.</p>
</div>
<h4 id="how-do-i-save-changes-made-on-processware-participants">How do I save changes made on Processware participants</h4>
<p>Any modifications made on new (see the previous section) or existing <em><strong>Processware</strong></em> Participants will only be persisted once saved. The is done using the associated operation on the <strong>Participants</strong> stores:</p>
<pre><code class="lang-c#">await context.Stores.Company.Participants
.SaveAsync(participant, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Participants</strong> store also provides synchronous versions for the above.</p>
</div>
<h4 id="how-do-i-maintain-processware-participant-members">How do I maintain Processware participant members</h4>
<p>A Participant in <em><strong>Processware</strong></em> collects Users as members; the addition of which is performed as shown:</p>
<pre><code class="lang-c#">// Adds the specified user as member of the specified participant.
await context.Stores.Company.Participants
.AddMemberAsync(participant, user, cancellationToken);
</code></pre>
<p>Members can also be removed, as is shown next:</p>
<pre><code class="lang-c#">// Removes the specified user from the specified participant.
await context.Stores.Company.Participants
.RemoveMemberAsync(participant, user, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Participants</strong> store also provides synchronous versions for the above.</p>
</div>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Removing members from participants only disassociates these objects, neither user nor participant is deleted.</p>
</div>
<h5 id="how-do-i-retrieve-processware-participants-from-users">How do I retrieve Processware participants from users</h5>
<p>A User in <em><strong>Processware</strong></em> is grouped by a <a href="#participants">Participant</a>, becoming a member of the latter. Retrieving <em><strong>Processware</strong></em> <a href="#participants">Participants</a> from a user can be performed using the following approach:</p>
<pre><code class="lang-c#">var participants = await context.Stores.Participants
.GetParticipantsForAsync(user, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Participants</strong> store also provides synchronous versions for the above.</p>
</div>
<h4 id="how-do-i-retrieve-the-processware-participant-members">How do I retrieve the Processware participant members</h4>
<p>To retrieve the <em><strong>Processware</strong></em> Participant members, the following can be used:</p>
<pre><code class="lang-c#">// Get the members for the specified participant
var users = await context.Stores.Company.Users
.GetMembersOfAsync(participant, cancellationToken);
</code></pre>
<div class="TIP">
<h5>Tip</h5>
<p>The <strong>Participants</strong> store also provides synchronous versions for the above.</p>
</div>
<h2 id="formcontext">FormContext</h2>
<p><strong>FormContext</strong> is the base <code>ITaskContext</code> implementation provided as context-specific parameter to the methods implemented by <code>TaskForm</code> (and therefore <a class="xref" href="../activity/introduction.html">activity</a>) implementations in <a class="xref" href="index.html">Managed Code</a>. It provides access to all the <code>ITaskContext</code> properties in the preceding sections, as well as to the <code>Form</code>-based representation of the <a class="xref" href="../activity/introduction.html">activity</a> being implemented.</p>
<div class="TIP">
<h5>Tip</h5>
<p>Refer to the <a class="xref" href="../activity/introduction.html#the-activity">overview</a> on <a class="xref" href="../activity/introduction.html">activity</a> for more details on the below.</p>
</div>
<h3 id="formloadcontext"><strong>FormLoadContext</strong></h3>
<p>The <strong>FormContext</strong>-based concrete type supplied to the <em><strong>LoadAsync</strong></em> method of a <code>TaskForm</code> implementation, providing the context for loading the associated <code>TaskForm</code>.</p>
<h3 id="formrefreshcontext"><strong>FormRefreshContext</strong></h3>
<p>The <strong>FormContext</strong>-based concrete type supplied to the <em><strong>RefreshAsync</strong></em> method of a <code>TaskForm</code> implementation. <strong>FormRefreshContext</strong> adds additional properties to pinpoint a specific normal/grouped control as source of the refresh where applicable, or actions to perform on a specific <code>ColumnGroup</code>. <code>TaskForm</code> also exposes methods to allow calling <em><strong>RefreshAsync</strong></em> from either <em><strong>LoadAsync</strong></em> or <em><strong>ValidateAsync</strong></em>, allowing <em><strong>RefreshAsync</strong></em> to be reusable in those contexts.</p>
<h3 id="formdatacontext"><strong>FormDataContext</strong></h3>
<p>The <strong>FormContext</strong>-based concrete type supplied to the <em><strong>GetDataAsync</strong></em> method of a <code>TaskForm</code> implementation. <strong>FormDataContext</strong> adds additional properties to target a specific <a class="xref" href="../objects/browsePage/introduction.html">Normal/Advanced Browse Page</a> for which data is to be retrieved, as well as the query shaping the resulting data set.</p>
<div class="TIP">
<h5>Tip</h5>
<p>Refer to <a class="xref" href="../objects/browsePage/howDoI_mgdCode.html">How do I (using Managed Code (.NET))</a> as it relates to <a class="xref" href="../objects/browsePage/introduction.html">Normal/Advanced Browse Pages</a> for more information.</p>
</div>
<h3 id="formvalidationcontext"><strong>FormValidationContext</strong></h3>
<p>The <strong>FormContext</strong>-based concrete type supplied to the <em><strong>ValidateAsync</strong></em> method of a <code>TaskForm</code> implementation. <strong>FormValidationContext</strong> adds as additional property the name of the source <a class="xref" href="../objects/commandOption/introduction.html">Command Option</a> as well as methods for communicating validation failures back to the originating client.</p>
<h3 id="formcompletioncontext"><strong>FormCompletionContext</strong></h3>
<p>The <strong>FormContext</strong>-based concrete type supplied to the <em><strong>CompleteAsync</strong></em> method of a <code>TaskForm</code> implementation. <strong>FormCompletionContext</strong> adds as additional property the name of the source <a class="xref" href="../objects/commandOption/introduction.html">Command Option</a> responsible for the completion of the associated <a class="xref" href="../activity/introduction.html">activity</a> instance. <em><strong>RevertAsync</strong></em> is called with the same <strong>FormCompletionContext</strong> specified to the <em><strong>CompleteAsync</strong></em> method on any failure to complete the associated <a class="xref" href="../activity/introduction.html">activity</a> instance allowing changes made in <em><strong>CompleteAsync</strong></em> to be reverted.</p>
<h2 id="columnsgroupcontext">ColumnsGroupContext</h2>
<p><strong>ColumnsGroupContext</strong> is the base <code>ITaskContext</code> implementation provided as context-specific parameter to the methods implemented by <code>ColumnsGroup</code> (and therefore <a class="xref" href="../objectGroup/introduction.html">Object Group</a>) implementations in <a class="xref" href="index.html">Managed Code</a>. It provides access to all the <code>ITaskContext</code> properties in the preceding sections, as well as to the <code>Form</code>-based representation of the <a class="xref" href="../activity/introduction.html">activity</a> and the <code>ColumnGroup</code>based representation of the <a class="xref" href="../objectGroup/introduction.html">Object Group</a> being implemented.</p>
<h3 id="columnsgrouploadcontext"><strong>ColumnsGroupLoadContext</strong></h3>
<p>The <strong>ColumnsGroupContext</strong>-based concrete type supplied to the <em><strong>LoadAsync</strong></em> method of a <code>ColumnsGroup</code> implementation, providing the context for loading the associated <code>ColumnGroup</code>. <code>ColumnsGroup</code> exposes a method to allow calling <em><strong>LoadAsync</strong></em> from the <em><strong>LoadAsync</strong></em> method on the encapsulating <code>TaskForm</code>.</p>
<h3 id="columnsgrouprefreshcontext"><strong>ColumnsGroupRefreshContext</strong></h3>
<p>The <strong>ColumnsGroupContext</strong>-based concrete type supplied to the <em><strong>RefreshAsync</strong></em> method of a <code>ColumnsGroup</code> implementation. <strong>ColumnsGroupContext</strong> adds additional properties to pinpoint a specific grouped control as source of the refresh where applicable, or actions to perform on the associated <code>ColumnGroup</code>. <code>ColumnsGroup</code> also exposes methods to allow calling <em><strong>RefreshAsync</strong></em> from either the <em><strong>LoadAsync</strong></em> or <em><strong>RefreshAsync</strong></em> method on the encapsulating <code>TaskForm</code>.</p>
<div class="TIP">
<h5>Tip</h5>
<p>Refer to <a class="xref" href="../objectGroup/howDoI_mgdCode.html">How do I (using Managed Code (.NET))</a> as it relates to <a class="xref" href="../objectGroup/introduction.html">Object Groups</a> for more information.</p>
</div>
</article>
</div>
<div class="hidden-sm col-md-2" role="complementary">
<div class="sideaffix">
<div class="toggle-mode">
<div class="icon">
<i aria-hidden="true">☀</i>
</div>
<label class="switch">
<input type="checkbox" id="switch-style">
<span class="slider round"></span>
</label>
<div class="icon">
<i aria-hidden="true">☾</i>
</div>
</div>
<nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
<h5>In this article</h5>
<div></div>
<!-- <p><a class="back-to-top" href="#top">Back to top</a><p> -->
</nav>
</div>
</div>
</div>
</div>
<footer>
<div class="grad-bottom"></div>
<div class="footer">
<div class="container">
<span class="pull-right">
<a href="#top">Back to top</a>
</span>
<div class="pull-left">
Copyright © FlowCentric (PTY) Ltd - Processware Developer Guide
</div>
<div class="toggle-mode pull-right visible-sm visible-xs">
<div class="icon">
<i aria-hidden="true">☀</i>
</div>
<label class="switch">
<input type="checkbox" id="switch-style-m">
<span class="slider round"></span>
</label>
<div class="icon">
<i aria-hidden="true">☾</i>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="../../styles/toggle-theme.js"></script>
</footer> </div>
<script type="text/javascript" src="../../styles/docfx.vendor.min.js"></script>
<script type="text/javascript" src="../../styles/highlight.min.js"></script>
<script type="text/javascript" src="../../styles/docfx.js"></script>
<script type="text/javascript" src="../../styles/main.js"></script>
<script type="text/javascript" src="../../styles/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: false
});
mermaid.init(undefined, ".lang-mermaid");
</script> </body>
</html>
Stored Procedures
-- Procedure for Fuel Issuance Corrections
----------------------------------------------
CREATE OR ALTER PROCEDURE FI_Upsert_FuelIssuanceCorrections
@ID INT = NULL, -- Use NULL for new records
@TransactionID VARCHAR(255),
@ErrorMessage VARCHAR(MAX),
@AccountCode VARCHAR(50),
@CorrectedAccountCode VARCHAR(50),
@StockCode VARCHAR(50),
@ItemDescription VARCHAR(255),
@TransType VARCHAR(50),
@WarehouseFrom VARCHAR(50),
@CorrectedWarehouseFrom VARCHAR(50),
@WarehouseTo VARCHAR(50),
@CorrectedWarehouseTo VARCHAR(50),
@PlantCode VARCHAR(50),
@CorrectedPlantCode VARCHAR(50),
@DateIssued VARCHAR(50),
@CostCategory VARCHAR(50),
@LitreQty VARCHAR(50),
@AuthID VARCHAR(50),
@QtyDiscrepancyResolved BIT,
@IsActive BIT = 1,
@CreatedBy INT = NULL,
@ModifiedBy INT = NULL
AS
BEGIN
-- Check if the record exists
IF EXISTS (SELECT 1 FROM FuelIssuanceCorrections WHERE ID = @ID)
BEGIN
-- Update the record
UPDATE FuelIssuanceCorrections
SET TransactionID = @TransactionID,
ErrorMessage = @ErrorMessage,
AccountCode = @AccountCode,
CorrectedAccountCode = @CorrectedAccountCode,
StockCode = @StockCode,
ItemDescription = @ItemDescription,
TransType = @TransType,
WarehouseFrom = @WarehouseFrom,
CorrectedWarehouseFrom = @CorrectedWarehouseFrom,
WarehouseTo = @WarehouseTo,
CorrectedWarehouseTo = @CorrectedWarehouseTo,
PlantCode = @PlantCode,
CorrectedPlantCode = @CorrectedPlantCode,
DateIssued = @DateIssued,
CostCategory = @CostCategory,
LitreQty = @LitreQty,
AuthID = @AuthID,
QtyDiscrepancyResolved = @QtyDiscrepancyResolved,
IsActive = @IsActive,
ModifiedDate = GETDATE(),
ModifiedBy = @ModifiedBy
WHERE ID = @ID
END
ELSE
BEGIN
-- Insert a new record
INSERT INTO FuelIssuanceCorrections (TransactionID, ErrorMessage, AccountCode, CorrectedAccountCode, StockCode, ItemDescription, TransType, WarehouseFrom, CorrectedWarehouseFrom, WarehouseTo, CorrectedWarehouseTo, PlantCode, CorrectedPlantCode, DateIssued, CostCategory, LitreQty, AuthID, QtyDiscrepancyResolved, IsActive, CreatedDate, CreatedBy)
VALUES (@TransactionID, @ErrorMessage, @AccountCode, @CorrectedAccountCode, @StockCode, @ItemDescription, @TransType, @WarehouseFrom, @CorrectedWarehouseFrom, @WarehouseTo, @CorrectedWarehouseTo, @PlantCode, @CorrectedPlantCode, @DateIssued, @CostCategory, @LitreQty, @AuthID, @QtyDiscrepancyResolved, @IsActive, GETDATE(), @CreatedBy)
END
END
GO
-- Procedure for retrieving fuel issuance corrections
----------------------------------------------
CREATE OR ALTER PROCEDURE FI_Get_FuelIssuanceCorrections
@ID INT = NULL,
@TransactionID VARCHAR(255) = NULL,
@IncludeDeleted BIT = 0
AS
BEGIN
SELECT ID, TransactionID, ErrorMessage, AccountCode, CorrectedAccountCode, StockCode, ItemDescription, TransType, WarehouseFrom, CorrectedWarehouseFrom, WarehouseTo, CorrectedWarehouseTo, PlantCode, CorrectedPlantCode, DateIssued, CostCategory, LitreQty, AuthID, QtyDiscrepancyResolved, IsActive, CreatedDate, CreatedBy, ModifiedDate, ModifiedBy
FROM FuelIssuanceCorrections
WHERE (@ID IS NULL OR ID = @ID)
AND (@TransactionID IS NULL OR TransactionID = @TransactionID)
AND (IsActive = 1 OR @IncludeDeleted = 1)
END
GO
-- Procedure for soft-deleting fuel issuance corrections
----------------------------------------------
CREATE OR ALTER PROCEDURE FI_Delete_FuelIssuanceCorrections
@ID INT,
@DeletedBy INT
AS
BEGIN
UPDATE FuelIssuanceCorrections
SET IsActive = 0,
DeletedDate = GETDATE(),
DeletedBy = @DeletedBy
WHERE ID = @ID
END
GO
-- Add further stored procedures for other tables and functionalities if necessary
-- Depending on each activity's complexity, further procedures might differActivity File
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using Tasks = System.Threading.Tasks;
using FlowCentric.Engine.Entities;
using FlowCentric.Engine.Entities.Classic;
using FlowCentric.Engine.Managed;
namespace FuelIssuanceandStockManagement.Activities
{
///<summary>
///<c>{{ActivityName}}</c> implements the <see cref = "TaskForm"/> for the task <c>SA160_ReviewSyncErrors</c>.
///</summary>
public sealed partial class {{ActivityName}} : TaskForm
{
#region TaskForm Implementation
///<inheritdoc/>
protected override async Tasks.Task LoadAsync(FormLoadContext context, CancellationToken cancellationToken)
{
this.SA16_CurrentDate.Text = DateTime.Now.ToString();
this.SA16_CurrentUser.Text = context.CurrentUser.FirstName;
//
// ======================================= Load SA160_OGErrors ========================================
await SA160_OGErrors.LoadAsync(context, cancellationToken);
// Calling base.RefreshAsync(context, cancellationToken) to forward to Refresh method.
await RefreshAsync(context, cancellationToken);
}
///<inheritdoc/>
protected override async Tasks.Task RefreshAsync(FormRefreshContext context, CancellationToken cancellationToken)
{
if (!(String.IsNullOrWhiteSpace(context.GroupName)))
{
switch (context.GroupName)
{
case Groups.SA160_OGErrors.Name:
if (await RefreshColumnsGroupOnlyAsync(context, SA160_OGErrors, cancellationToken))
{
// Refresh handled!
return;
}
break;
}
}
//
// ====================================== Refresh SA160_OGErrors ======================================
await SA160_OGErrors.RefreshAsync(context, GroupAction.Refresh, null, null, cancellationToken);
}
///<inheritdoc/>
protected override Tasks.Task ValidateAsync(FormValidationContext context, CancellationToken cancellationToken)
{
// Calling base.RefreshAsync(context, cancellationToken) to forward to Refresh method.
return RefreshAsync(context, cancellationToken);
}
///<inheritdoc/>
protected override Tasks.Task CompleteAsync(FormCompletionContext context, CancellationToken cancellationToken)
{
return Tasks.Task.CompletedTask;
}
#endregion
#region Class Methods
private static async Tasks.Task<bool> RefreshColumnsGroupOnlyAsync(FormRefreshContext context, ColumnsGroup columnsGroup, CancellationToken cancellationToken)
{
if (((context) == (null)) || ((columnsGroup) == (null)))
{
return false;
}
var groupAction = GroupAction.Refresh;
var rowNumber = context.RowNumber;
if (context.RowAddition)
{
groupAction = (GroupAction.RowAddition);
switch (columnsGroup.Group.GetExecutionOnRowAddition())
{
case ScriptExecution.PerPage:
columnsGroup = (null);
break;
case ScriptExecution.PerGroup:
rowNumber = (null);
break;
default:
// Handled!
return true;
}
}
else if (context.RowRemoval)
{
groupAction = (GroupAction.RowRemoval);
switch (columnsGroup.Group.GetExecutionOnRowRemoval())
{
case ScriptExecution.PerPage:
columnsGroup = (null);
break;
case ScriptExecution.PerGroup:
rowNumber = (null);
break;
default:
// Handled!
return true;
}
}
else
{
switch (columnsGroup.Group.GetExecutionOnRowRefresh())
{
case ScriptExecution.PerPage:
columnsGroup = (null);
break;
case ScriptExecution.PerGroup:
rowNumber = (null);
break;
}
}
if (columnsGroup != null)
{
await columnsGroup.RefreshAsync(context, groupAction, context.ControlName, rowNumber, cancellationToken);
// Group Refresh Handled!
return true;
}
return false;
}
#endregion
#region Inner Types
partial class SA160_OGErrorsColumnsGroup
{
#region ColumnsGroup Implementation
///<inheritdoc/>
protected override Tasks.Task LoadAsync(ColumnsGroupLoadContext context, CancellationToken cancellationToken)
{
return Tasks.Task.CompletedTask;
}
///<inheritdoc/>
protected override Tasks.Task RefreshAsync(ColumnsGroupRefreshContext context, CancellationToken cancellationToken)
{
var numberOfRows = Group.NumberOfRows;
if (context.RowNumber.HasValue)
{
numberOfRows = ((context.RowNumber.Value) + (1));
}
var rowNumber = context.RowNumber.GetValueOrDefault(0);
while ((rowNumber) < (numberOfRows))
{
rowNumber++;
}
return Tasks.Task.CompletedTask;
}
#endregion
}
#endregion
}
}Instructions
Your task is to ensure that the following is done on the Activity Code:
- All controls should be populated from the database using the provided stored procedures
- All validations should be done where needed.
- Any other task that ensures security.
- DO NOT make any mistakes.
📇 Additional Metadata
- 🗂 Type:: note
- 🏷️ Tags::
- 📡 Status:: #status/🌱