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:

  1. Retrieve the Auto-Number Configuration Record
  2. Lock the record to prevent conflicts
  3. Increment the current number and update it
  4. 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

Popular posts from this blog

๐Ÿค– Copilot vs Microsoft Copilot vs Copilot Studio: What’s the Difference?

In-Process vs Isolated Process Azure Functions: What’s the Difference?