Automating Unique Number Generation in Dynamics 365 Using Plugins
Introduction
When working with Dynamics 365, generating unique and sequential numbers for records such as invoices, quotes, or orders is a common requirement. Instead of manually assigning these numbers, we can use an Auto-Numbering Plugin to automate the process. In this blog, we’ll break down a C# plugin that implements this functionality in an easy-to-understand way.
Why Do We Need Auto-Numbering?
Manual numbering is prone to errors, duplication, and inefficiency. Automating this ensures:
- Unique numbering for each record
- Improved data consistency
- A structured format (e.g., INV-1001, INV-1002)
How Does the Plugin Work?
The plugin follows a structured approach to generate and assign unique numbers:
- Retrieve the Auto-Number Configuration Record
- Lock the record to prevent conflicts
- Increment the current number and update it
- Assign the new number to the created entity
Let’s break down the code into simple steps.
Understanding the Code
Complete C# Code for Auto-Numbering Plugin
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
public class AutoNumberingPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
// Step 1: Retrieve the Auto-Numbering Record (Locking the row)
QueryExpression query = new QueryExpression("new_AutoNumberConfig") // Your custom entity for auto-numbering
{
ColumnSet = new ColumnSet("new_CurrentNumber", "new_UpdateInProgress")
};
EntityCollection autoNumberRecords = service.RetrieveMultiple(query);
if (autoNumberRecords.Entities.Count == 0)
{
throw new InvalidPluginExecutionException("Auto-numbering configuration record not found.");
}
Entity autoNumberRecord = autoNumberRecords.Entities[0];
// Step 2: Apply a Pre-lock by setting "UpdateInProgress" field
autoNumberRecord["new_UpdateInProgress"] = true;
service.Update(autoNumberRecord); // This locks the row
// Step 3: Lock and Increment the Number
int currentNumber = autoNumberRecord.Contains("new_CurrentNumber")
? (int)autoNumberRecord["new_CurrentNumber"]
: 1000; // Default start value if none exists
int newNumber = currentNumber + 1;
autoNumberRecord["new_CurrentNumber"] = newNumber;
autoNumberRecord["new_UpdateInProgress"] = false; // Unlocking
service.Update(autoNumberRecord);
// Step 4: Assign the New Number to the Target Entity
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity targetEntity)
{
targetEntity["new_AutoNumber"] = newNumber.ToString("INV-0000"); // Format as INV-1001, INV-1002, etc.
service.Update(targetEntity);
}
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException($"Error in Auto Numbering: {ex.Message}");
}
}
}
Breaking Down the Code
Step 1: Retrieve the Auto-Numbering Record
QueryExpression query = new QueryExpression("new_AutoNumberConfig") // Custom entity storing numbers
{
ColumnSet = new ColumnSet("new_CurrentNumber", "new_UpdateInProgress")
};
EntityCollection autoNumberRecords = service.RetrieveMultiple(query);
- The plugin looks for a record that keeps track of the last assigned number.
- If no such record is found, an error is thrown.
Step 2: Apply a Pre-Lock to Prevent Conflicts
autoNumberRecord["new_UpdateInProgress"] = true;
service.Update(autoNumberRecord);
- Before changing the number, we lock the record by updating the
UpdateInProgress
field. - This prevents multiple processes from generating the same number at the same time.
Step 3: Increment and Save the New Number
int currentNumber = autoNumberRecord.Contains("new_CurrentNumber")
? (int)autoNumberRecord["new_CurrentNumber"]
: 1000; // Default starting number
int newNumber = currentNumber + 1;
autoNumberRecord["new_CurrentNumber"] = newNumber;
autoNumberRecord["new_UpdateInProgress"] = false; // Unlocking
service.Update(autoNumberRecord);
- The plugin retrieves the current number.
- If no number exists, it starts from 1000.
- The number is incremented and updated back to the record.
- The lock is released by setting
UpdateInProgress
to false.
Step 4: Assign the New Number to the Created Record
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity
Comments
Post a Comment