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?
| Property | Value |
|---|---|
| Entity | Document Approval (tt_documentapproval) |
| Message | Update |
| Stage | Post-Operation (runs after the update is saved) |
| Execution Mode | Synchronous |
| Conditions | Only 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_documentsignerrecord 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:
- Verify the status code is
206340002(Draft) - Check the Plugin Trace Log for "[Early Exit]" messages
- 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:
- Verify contacts exist with the correct Data Approver Connection Role
- Review the Plugin Trace Log for detailed execution information
Related Documentation
- DocumentApprovalDocumentSignersPostOperationCreate - Companion plugin that creates signers when a new Document Approval is created
- Utils.UpdateSignersForDocumentApproval() [To Be Completed] - Utility method that contains the core logic for managing Document Signers
- Service Line Configuration Guide [To Be Completed] - How to configure Service Line to Approver mappings
- Web Roles Overview [To Be Completed] - Understanding Data Approver roles and permissions
Configuration Requirements
This plugin depends on the following configuration:
- Service Line Option Set - The
tt_servicelinefield must have valid option set values - Data Approver Web Roles - Contacts must be assigned to appropriate Data Approver Connection Roles
- Plugin Registration - Registered on Update of
tt_documentapprovalin 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