Tax Form Sections - Post Operation Create
This plugin automatically populates section fields on a Tax Form when it is created, based on configuration stored in Dynamics.
What Does This Do?
When a new UK Tax Return form is created, this plugin automatically loads the appropriate sections (like UK Interest, UK Dividends, Employment Income, etc.) from a web resource and populates them into the Tax Form record's section fields.
For example, when someone creates a UK Tax Return, this plugin will:
- Load the configuration that specifies which sections to include
- Retrieve the section definitions from a JavaScript web resource
- Populate
tt_section1,tt_section2,tt_section3, etc. with the relevant section JSON
Important: This only runs for Tax Forms with Tax Form Type set to "UK Tax Return" (206340000). Other tax form types are ignored.
When Does This Run?
| Property | Value |
|---|---|
| Entity | Tax Form (tt_taxform) |
| Message | Create |
| Stage | Post-Operation (runs after the record is created) |
| Execution Mode | Synchronous |
| Conditions | Only runs when Tax Form Type is UK Tax Return (206340000) |
How It Works
Step 1: Validate the Tax Form Type
The plugin first checks if the Tax Form Type is "UK Tax Return" (206340000). If not, it exits immediately since section population only applies to UK Tax Returns.
Exit condition: Tax Form Type is not UK Tax Return
Step 2: Retrieve the configuration record
Queries the Configuration (tt_configuration) entity to find the active record where:
- Configuration Type = 206340000 (Tax Form)
- Tax Form Type = 206340000 (UK Tax Return)
- State = Active
This configuration contains the web resource name and the list of sections to populate.
Exit condition: No matching configuration record found
Step 3: Parse the sections list
Extracts and parses the tt_sections field from the configuration, which contains a comma or semicolon-separated list of section IDs (e.g., "ukInterest, ukDividends, employment").
What happens: Splits the string and trims whitespace to create an array of section IDs
Exit condition: Sections field is empty or whitespace
Step 4: Get the web resource name
Retrieves the web resource name from the configuration's tt_webresource field and normalizes it by removing any leading slashes or "WebResources/" prefix.
What happens: Ensures the web resource name is in the correct format for querying
Exit condition: Web resource field is empty or whitespace
Step 5: Load the web resource content
Queries the Web Resource (webresource) entity directly using the SDK to retrieve the JavaScript file content. The content is stored as base64-encoded data and is decoded into a string.
What happens:
- Queries for web resource by name
- Decodes base64 content to UTF-8 string
- Returns the JavaScript file content
Exit condition: Throws InvalidPluginExecutionException if web resource is not found or has no content
Step 6: Parse JavaScript to extract JSON
Uses regex to extract the JSON object from the JavaScript file. Expects format: var TaxFormSections = { ... }
What happens: Strips away the JavaScript variable assignment and parses the remaining JSON structure
Exit condition: Throws InvalidPluginExecutionException if JSON cannot be extracted
Step 7: Get the sections array from JSON
Extracts the sections array from the parsed JSON object. This array contains all available section definitions.
What happens: Accesses the "sections" property of the JSON object
Exit condition: No "sections" array found in JSON
Step 8: Match and populate section fields
Iterates through the section IDs from the configuration and searches for matching sections in the JSON array. Each matched section is stored in the corresponding tt_section field (tt_section1, tt_section2, etc.).
What happens:
- Loops through configured section IDs
- Finds matching section in JSON array by ID
- Assigns section JSON to tt_section1, tt_section2, tt_section3, etc.
- Performs a single Update operation with all sections
Step 9: Log completion
Records the number of sections that were successfully populated in the Plugin Trace Log. This helps with debugging and verifying the plugin executed correctly.
What happens: Writes a trace message showing how many section fields were populated
Why This Matters
Tax forms have complex, multi-section structures that change depending on the type of return. Rather than hardcoding section definitions in C# or requiring manual data entry, this plugin:
- Centralizes configuration - Section definitions live in a web resource that can be updated without redeploying code
- Maintains consistency - All UK Tax Returns get the same section structure based on the configuration
- Enables flexibility - Different tax form types can have different configurations
- Reduces errors - Sections are automatically populated correctly, eliminating manual entry mistakes
Code
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Newtonsoft.Json.Linq;
namespace PracticeGateway.Plugin
{
/// <summary>
/// This plugin runs after a Tax Form is created.
/// It loads configuration from a web resource and populates section fields on the Tax Form.
/// </summary>
public class TaxFormSectionsPostOperationCreate : PluginBase
{
// Entity and message names
private const string ENTITY_TAX_FORM = "tt_taxform";
private const string MESSAGE_CREATE = "Create";
private const int UK_TAX_RETURN_VALUE = 206340000;
private const int CONFIGURATION_TYPE_TAX_FORM = 206340000;
public TaxFormSectionsPostOperationCreate(string unsecure, string secure)
: base(typeof(TaxFormSectionsPostOperationCreate))
{
}
protected override void ExecuteCdsPlugin(ILocalPluginContext localContext)
{
if (localContext == null)
throw new InvalidPluginExecutionException(nameof(localContext));
ITracingService tracingService = localContext.TracingService;
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService currentUserService = localContext.CurrentUserService;
try
{
tracingService.Trace("Starting TaxFormSectionsPostOperationCreate");
// Initialize Utils helper
Utils.Utils utils = new Utils.Utils(currentUserService, tracingService);
// Validate context and get target entity
Entity target = utils.ValidateAndGetTarget(context, ENTITY_TAX_FORM, MESSAGE_CREATE);
Guid taxFormId = target.Id;
tracingService.Trace($"Processing Tax Form ID: {taxFormId}");
// Retrieve the full Tax Form record to check Tax Form Type
Entity taxForm = currentUserService.Retrieve(ENTITY_TAX_FORM, taxFormId,
new ColumnSet("tt_taxformtype"));
// Check if Tax Form Type is "UK Tax Return"
if (!IsTaxFormTypeUKTaxReturn(taxForm, tracingService))
{
tracingService.Trace("Tax Form Type is not UK Tax Return - exiting");
return;
}
tracingService.Trace("Tax Form Type is UK Tax Return - proceeding");
// Fetch the configuration record
Entity configuration = utils.GetTaxFormConfiguration(currentUserService, CONFIGURATION_TYPE_TAX_FORM, UK_TAX_RETURN_VALUE);
if (configuration == null)
{
tracingService.Trace("No configuration record found - exiting");
return;
}
tracingService.Trace($"Configuration found: {configuration.GetAttributeValue<string>("tt_name")}");
// Parse sections from configuration
string[] sectionIds = utils.ParseSectionIds(configuration, tracingService);
if (sectionIds.Length == 0)
{
tracingService.Trace("No sections specified in configuration - exiting");
return;
}
tracingService.Trace($"Found {sectionIds.Length} section IDs: {string.Join(", ", sectionIds)}");
// Get web resource name
string webResourceName = utils.GetWebResourceName(configuration, tracingService);
if (string.IsNullOrWhiteSpace(webResourceName))
{
tracingService.Trace("No web resource specified in configuration - exiting");
return;
}
tracingService.Trace($"Web Resource Name: {webResourceName}");
// Load and parse web resource
string webResourceContent = utils.LoadWebResourceContent(webResourceName, currentUserService, tracingService);
tracingService.Trace($"Web resource loaded, length: {webResourceContent.Length} characters");
JObject taxFormSections = utils.ParseJavaScriptToJson(webResourceContent, "TaxFormSections", tracingService);
tracingService.Trace("JSON parsed successfully");
// Get sections array from JSON
JArray sectionsArray = taxFormSections["sections"] as JArray;
if (sectionsArray == null)
{
tracingService.Trace("No 'sections' array found in web resource JSON - exiting");
return;
}
tracingService.Trace($"Found {sectionsArray.Count} sections in web resource");
// Populate section fields on Tax Form
int populatedCount = utils.PopulateTaxFormSections(taxFormId, sectionIds, sectionsArray, currentUserService, tracingService);
tracingService.Trace($"Successfully populated {populatedCount} section field(s)");
tracingService.Trace("Finished TaxFormSectionsPostOperationCreate");
}
catch (Exception ex)
{
tracingService.Trace($"Error in TaxFormSectionsPostOperationCreate: {ex}");
throw new InvalidPluginExecutionException("Plugin execution failed.", ex);
}
}
/// <summary>
/// Checks if the Tax Form Type is UK Tax Return
/// </summary>
private bool IsTaxFormTypeUKTaxReturn(Entity taxForm, ITracingService tracingService)
{
if (!taxForm.Contains("tt_taxformtype") || taxForm["tt_taxformtype"] == null)
{
tracingService.Trace("tt_taxformtype field is missing or null");
return false;
}
OptionSetValue taxFormType = taxForm.GetAttributeValue<OptionSetValue>("tt_taxformtype");
return taxFormType.Value == UK_TAX_RETURN_VALUE;
}
}
}
Common Issues & Troubleshooting
Problem: Sections aren't being populated on new Tax Forms
Possible Causes:
- The Tax Form Type is not set to "UK Tax Return" (206340000)
- No configuration record exists for UK Tax Return
- The configuration record is inactive (statecode ≠ 0)
- The plugin registration might be missing or disabled
How to Check:
- Verify the Tax Form Type is exactly 206340000
- Query Configuration entity for active records with Configuration Type = 206340000 and Tax Form Type = 206340000
- Check the Plugin Trace Log for "[Exit]" or "[Warning]" messages
- Confirm plugin is registered on Create of
tt_taxformin Post-Operation stage
Problem: Plugin throws "No configuration record found"
Cause: The Configuration entity doesn't have a matching record for UK Tax Returns
Solution:
- Create a Configuration record with:
- Configuration Type = 206340000
- Tax Form Type = 206340000
- State = Active
- Populate
tt_webresourceandtt_sectionsfields
- Ensure the record is not deactivated
Problem: Plugin throws "Web resource not found"
Possible Causes:
- The web resource name in the configuration is incorrect
- The web resource doesn't exist in Dynamics
- The web resource name has incorrect formatting (extra slashes, wrong prefix)
How to Check:
- Verify the web resource exists in Dynamics (Settings > Customizations > Web Resources)
- Check the exact name of the web resource matches the
tt_webresourcefield in configuration - Review the Plugin Trace Log to see what name was queried
- Ensure the web resource is published
Problem: Plugin throws "Could not extract JSON from JavaScript content"
Possible Causes:
- The web resource JavaScript doesn't follow the expected format
- The variable name is not "TaxFormSections"
- The JSON structure is malformed
Solution:
- Ensure the web resource contains:
var TaxFormSections = { ... } - Validate the JSON structure is correct (use a JSON validator)
- Check for syntax errors in the JavaScript file
- Ensure the variable name is exactly "TaxFormSections" (case-sensitive)
Problem: Some sections are missing from the Tax Form
Possible Causes:
- The section IDs in the configuration don't match the IDs in the web resource
- The sections array in the JSON doesn't contain the requested sections
- Section IDs have incorrect capitalization or spacing
How to Check:
- Review the
tt_sectionsfield in the configuration (e.g., "ukInterest, ukDividends") - Open the web resource and verify matching section IDs exist in the sections array
- Check the Plugin Trace Log for "Section 'X' not found in web resource" warnings
- Ensure section IDs are spelled exactly the same in both places
Problem: All sections are empty (count is 0)
Possible Causes:
- The
tt_sectionsfield in configuration is empty or whitespace - All section IDs in the configuration are missing from the web resource
How to Check:
- Verify
tt_sectionsfield contains comma or semicolon-separated section IDs - Confirm at least one section ID matches a section in the web resource
- Review the Plugin Trace Log for detailed execution information
Related Documentation
- Utils.GetTaxFormConfiguration() - Utility method that retrieves the configuration record
- Utils.LoadWebResourceContent() - Utility method that loads web resource data from Dynamics
- Utils.ParseJavaScriptToJson() - Utility method that extracts JSON from JavaScript files
- Utils.PopulateTaxFormSections() - Utility method that populates section fields on the Tax Form
- Tax Form Configuration Guide [To Be Completed] - How to configure tax form sections
- Web Resource Structure Guide [To Be Completed] - Format requirements for tax form section web resources
Configuration Requirements
This plugin depends on the following configuration:
-
Configuration Record (
tt_configuration)- Configuration Type = 206340000 (Tax Form)
- Tax Form Type = 206340000 (UK Tax Return)
- State = Active (0)
tt_webresourcefield populated with web resource namett_sectionsfield populated with comma/semicolon-separated section IDs
-
Web Resource (
webresource)- Must contain JavaScript with format:
var TaxFormSections = { sections: [...] } - Each section must have an "id" property matching the IDs in configuration
- Must be published in Dynamics
- Must contain JavaScript with format:
-
Tax Form Entity (
tt_taxform)- Must have fields:
tt_section1,tt_section2,tt_section3, etc. (as many as needed) - Must have
tt_taxformtypefield with option set value 206340000 for UK Tax Return
- Must have fields:
-
Plugin Registration
- Registered on Create of
tt_taxformin Post-Operation stage - Synchronous execution mode
- Registered on Create of
Example Configuration
Configuration Record Example:
Name: UK Tax Return Sections
Configuration Type: 206340000 (Tax Form)
Tax Form Type: 206340000 (UK Tax Return)
Web Resource: tt_taxformsections.js
Sections: ukInterest, ukDividends, employment, propertyIncome, capitalGains
State: Active
Web Resource Example (tt_taxformsections.js):
var TaxFormSections = {
sections: [
{
id: "ukInterest",
title: "UK Interest",
fields: [...],
validation: {...}
},
{
id: "ukDividends",
title: "UK Dividends",
fields: [...],
validation: {...}
},
{
id: "employment",
title: "Employment Income",
fields: [...],
validation: {...}
}
]
};
Testing Checklist
When testing this plugin, verify:
- Creating a Tax Form with Type = UK Tax Return populates sections correctly
- Creating a Tax Form with a different Type does NOT populate sections
- All configured section IDs are matched and populated in order
- Missing sections in web resource are logged as warnings (don't cause failures)
- Plugin handles missing configuration gracefully (exits without error)
- Plugin handles missing web resource with clear error message
- Plugin handles malformed JavaScript/JSON with clear error message
- Section JSON is stored in correct fields (tt_section1, tt_section2, etc.)
- Plugin completes within acceptable performance timeframe
- Error messages are logged clearly in Plugin Trace Log
- Updating the web resource and republishing affects new Tax Forms correctly