Skip to main content

Document Approval Document Signers - Post Operation Update

This plugin automatically updates the Document Signer records when the Service Line is changed on a Document Approval that's still in Draft status.


What Does This Do?

When someone changes which department (Service Line) should approve a document, this plugin automatically updates the list of people who need to sign off on it.

For example, if a document was initially set to be approved by the "Audit" team but then gets changed to "UK Tax", this plugin will:

  • Remove the Audit approvers
  • Add the UK Tax approvers

Important: This only works while the document is still in "Draft" status. Once it's submitted for approval, for information or for signing, the signers are locked in and cannot be changed.


When Does This Run?

PropertyValue
EntityDocument Approval (tt_documentapproval)
MessageUpdate
StagePost-Operation (runs after the update is saved)
Execution ModeSynchronous
ConditionsOnly runs when the Service Line field changes AND the Status Reason is Draft

How It Works

Step 1: Check if Service Line was changed

The plugin first checks if the tt_serviceline field is present in the update. If not, it exits immediately since there's nothing to process.

Exit condition: Service Line not in update target

Step 2: Check if Status Reason is Draft

Retrieves the Document Approval record and verifies the status code is 206340002 (Draft). Signers can only be updated while in Draft status.

Exit condition: Status is not Draft (e.g., Awaiting Approval, Signature Required)

Step 3: Validate Service Line exists

Ensures that the Document Approval has a valid Service Line value set. This is critical because we need the Service Line to determine which approvers should be assigned.

Exit condition: Throws InvalidPluginExecutionException if Service Line is missing or invalid

Step 4: Get Contact ID

Retrieves the Contact ID from the Document Approval record. This identifies the person who submitted the document for approval and is needed to determine the appropriate signers.

What happens: Extracts the Contact lookup field from the Document Approval

Step 5: Get Service Line label

Fetches the display name (label) of the Service Line option set value (e.g., "Audit", "Business Advisory", "UK Tax"). This label is used to match approvers with the correct department.

What happens: Converts the numeric Service Line value to its readable text equivalent

Step 6: Delete existing Document Signers

Removes all current Document Signer records associated with this Document Approval. This ensures we start fresh with the correct approvers for the new Service Line.

What happens: Queries and deletes all related tt_documentsigner records

Step 7: Create new Document Signers

Creates new Document Signer records based on the updated Service Line. The plugin queries for contacts who have the appropriate "Data Approver" Connection Role for the selected Service Line and generates a Document Signer record for each one.

What happens:

  • Finds contacts with matching Data Approver roles
  • Creates a tt_documentsigner record for each approver
  • Links each signer to the Document Approval
Step 8: Log completion

Records the number of signers that were successfully created in the Plugin Trace Log. This helps with debugging and verifying the plugin executed correctly.

What happens: Writes a trace message showing how many signer records were created


Why This Matters

Different departments have different approvers with different permissions and responsibilities. If someone accidentally selects the wrong Service Line when creating a Document Approval, they need to be able to correct it and ensure the right people are assigned to review and sign off.

However, once a document moves past the Draft stage (Awaiting Approval, Signature Required, etc.), we lock down the signers to maintain a clear audit trail. This prevents changes to who can access the approval once it has been sent.


Code

Dependencies

using System;
using Microsoft.Xrm.Sdk;

Full Plugin Code

namespace PracticeGateway.Plugin
{
public class DocumentApprovalDocumentSignersPostOperationUpdate : PluginBase
{
// Entity and field names
private const string ENTITY_DOCUMENT_APPROVAL = "tt_documentapproval";
private const string ATTR_SERVICE_LINE = "tt_serviceline";
private const string ATTR_STATUS_CODE = "statuscode";
private const string MESSAGE_UPDATE = "Update";

// Status value for Draft
private const int STATUS_CODE_DRAFT = 206340002;
private const int MAX_PLUGIN_DEPTH = 10;

public DocumentApprovalDocumentSignersPostOperationUpdate(string unsecure, string secure)
: base(typeof(DocumentApprovalDocumentSignersPostOperationUpdate))
{
}

protected override void ExecuteCdsPlugin(ILocalPluginContext localContext)
{
var tracing = localContext.TracingService;
var context = localContext.PluginExecutionContext;
var service = localContext.CurrentUserService;

tracing.Trace($"[Plugin Start] {context.MessageName}, Stage: {context.Stage}, Depth: {context.Depth}");

try
{
// Helper class for common operations
Utils.Utils utils = new Utils.Utils(service, tracing);

// Get the Document Approval record that was just updated
Entity target = utils.ValidateAndGetTarget(context, ENTITY_DOCUMENT_APPROVAL, MESSAGE_UPDATE, MAX_PLUGIN_DEPTH);

// Stop if Service Line wasn't changed (nothing to do)
if (!target.Contains(ATTR_SERVICE_LINE))
{
tracing.Trace($"[Early Exit] {ATTR_SERVICE_LINE} not in Target - no action required");
return;
}

Guid documentApprovalId = target.Id;
tracing.Trace($"[Processing] Document Approval ID: {documentApprovalId}");

// Retrieve the full Document Approval record to check its status
Entity documentApproval = utils.RetrieveDocumentApproval(documentApprovalId, ATTR_STATUS_CODE);

// Stop if not in Draft status (signers can only be updated in Draft)
if (!utils.IsDocumentApprovalInDraft(documentApproval, STATUS_CODE_DRAFT))
{
tracing.Trace($"[Early Exit] Document Approval is not in Draft status - no action required");
return;
}

tracing.Trace($"[Processing] Status is Draft - proceeding with signer updates");

// Make sure a Service Line is set (required to find the right approvers)
if (!utils.ValidateServiceLine(documentApproval))
{
throw new InvalidPluginExecutionException(
$"Document Approval {documentApprovalId} does not have a valid service line value.");
}

// Get the Contact who needs approval
Guid contactId = utils.GetContactIdFromDocumentApproval(documentApproval);
tracing.Trace($"[Processing] Contact ID: {contactId}");

// Get the new Service Line name (e.g., "Audit", "UK Tax")
string serviceLineLabel = utils.GetServiceLineLabel(documentApproval);
tracing.Trace($"[Processing] Service Line: {serviceLineLabel}");

// Delete old signers and create new ones for the updated Service Line
int signersCreated = utils.UpdateSignersForDocumentApproval(
documentApprovalId,
contactId,
serviceLineLabel,
deleteExisting: true); // Remove old signers since Service Line changed

tracing.Trace($"[Plugin Complete] Successfully updated signer records. Created {signersCreated} new signer(s)");
}
catch (InvalidPluginExecutionException)
{
// Let plugin exceptions pass through without modification
throw;
}
catch (Exception ex)
{
// Log unexpected errors and wrap them
tracing.Trace($"[Error] {ex.GetType().Name}: {ex.Message}");

if (ex.InnerException != null)
{
tracing.Trace($"[Error] Inner exception: {ex.InnerException.Message}");
}

throw new InvalidPluginExecutionException(
$"An unexpected error occurred in DocumentApprovalDocumentSignersPostOperationUpdate: {ex.Message}", ex);
}
}
}
}

Common Issues & Troubleshooting

Problem: Signers aren't updating when I change the Service Line

Possible Causes:

  • The Document Approval status is no longer "Draft" - this plugin only runs in Draft status
  • The Service Line field wasn't actually changed in the update
  • The plugin registration might be missing or disabled

How to Check:

  1. Verify the status code is 206340002 (Draft)
  2. Check the Plugin Trace Log for "[Early Exit]" messages
  3. Confirm the Service Line value actually changed (not just opened and saved)

Problem: Plugin throws "does not have a valid service line value"

Cause: The Service Line field is empty or null after the update

Solution: Ensure the Service Line is populated before saving the Document Approval record. Consider adding client-side validation to make Service Line a required field.


Problem: Wrong approvers are being assigned

Possible Causes:

  • The approver contacts don't have the correct Connection Roles assigned

How to Check: 2. Verify the approver contacts have the appropriate "Data Approver" Connection Roles 3. Check the Plugin Trace Log to see which Service Line label was used


Problem: No signers are created (count is 0)

Possible Causes:

  • No contacts have the required Data Approver Connection Role for this Service Line
  • The Contact associated with the Document Approval might be filtering out potential signers

How to Check:

  1. Verify contacts exist with the correct Data Approver Connection Role
  2. Review the Plugin Trace Log for detailed execution information


Configuration Requirements

This plugin depends on the following configuration:

  1. Service Line Option Set - The tt_serviceline field must have valid option set values
  2. Data Approver Web Roles - Contacts must be assigned to appropriate Data Approver Connection Roles
  3. Plugin Registration - Registered on Update of tt_documentapproval in Post-Operation stage

Testing Checklist

When testing this plugin, verify:

  • Changing Service Line in when the Document Approval is in Draft updates signers correctly
  • Changing Service Line when the Document Approval is NOT in Draft does NOT update signers
  • Updating other fields (not Service Line) does not trigger signer updates
  • Old signers are deleted before new ones are created
  • Plugin handles missing Service Line configuration gracefully
  • Plugin handles missing approver contacts gracefully
  • Error messages are logged clearly in Plugin Trace Log