Skip to main content

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?

PropertyValue
EntityTax Form (tt_taxform)
MessageCreate
StagePost-Operation (runs after the record is created)
Execution ModeSynchronous
ConditionsOnly 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:

  1. Verify the Tax Form Type is exactly 206340000
  2. Query Configuration entity for active records with Configuration Type = 206340000 and Tax Form Type = 206340000
  3. Check the Plugin Trace Log for "[Exit]" or "[Warning]" messages
  4. Confirm plugin is registered on Create of tt_taxform in 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:

  1. Create a Configuration record with:
    • Configuration Type = 206340000
    • Tax Form Type = 206340000
    • State = Active
    • Populate tt_webresource and tt_sections fields
  2. 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:

  1. Verify the web resource exists in Dynamics (Settings > Customizations > Web Resources)
  2. Check the exact name of the web resource matches the tt_webresource field in configuration
  3. Review the Plugin Trace Log to see what name was queried
  4. 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:

  1. Ensure the web resource contains: var TaxFormSections = { ... }
  2. Validate the JSON structure is correct (use a JSON validator)
  3. Check for syntax errors in the JavaScript file
  4. 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:

  1. Review the tt_sections field in the configuration (e.g., "ukInterest, ukDividends")
  2. Open the web resource and verify matching section IDs exist in the sections array
  3. Check the Plugin Trace Log for "Section 'X' not found in web resource" warnings
  4. Ensure section IDs are spelled exactly the same in both places

Problem: All sections are empty (count is 0)

Possible Causes:

  • The tt_sections field in configuration is empty or whitespace
  • All section IDs in the configuration are missing from the web resource

How to Check:

  1. Verify tt_sections field contains comma or semicolon-separated section IDs
  2. Confirm at least one section ID matches a section in the web resource
  3. Review the Plugin Trace Log for detailed execution information


Configuration Requirements

This plugin depends on the following configuration:

  1. Configuration Record (tt_configuration)

    • Configuration Type = 206340000 (Tax Form)
    • Tax Form Type = 206340000 (UK Tax Return)
    • State = Active (0)
    • tt_webresource field populated with web resource name
    • tt_sections field populated with comma/semicolon-separated section IDs
  2. 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
  3. Tax Form Entity (tt_taxform)

    • Must have fields: tt_section1, tt_section2, tt_section3, etc. (as many as needed)
    • Must have tt_taxformtype field with option set value 206340000 for UK Tax Return
  4. Plugin Registration

    • Registered on Create of tt_taxform in Post-Operation stage
    • Synchronous execution mode

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