Post

Avoiding Duplicate Document Numbers in Job Queues: Use ClearStateAndGetNextNo

Avoiding Duplicate Document Numbers in Job Queues: Use ClearStateAndGetNextNo

When working with number series in Dynamics 365 Business Central, you’ve probably used NoSeriesMgt.GetNextNo more times than you’ve had coffee breaks. But if you’ve ever used it inside a job queue, you might have noticed something funky — like getting the same document number reused. And no, it’s not déjà vu — it’s state retention biting back.


🧨 The Problem: “Why am I getting the same Document No. again?!”

In job queues, NoSeriesMgt.GetNextNo keeps an internal memory of the last fetched document number beacuse of it’s global variable not being cleared. This is normally fine for page-bound transactions, but it’s a trap when you run the NoSeriesMgt instance in a job queue run.

codeunit 50000 "My Codeunit"
{
    var
        GenJnlLine: Record "Gen. Journal Line";
        NoSeriesMgt: Codeunit NoSeriesManagement;

    procedure ProcessMytable(Mytable: Record "Mytable")
    begin
        CreateJournal(Mytable);
        PostJournal(GenJnlLine);
    end;

    procedure CreateJournal(Mytable)
    begin
      <!-- Other Code -->
      GenJnlLine.Validate("Document No.", NoSeriesMgt.GetNextNo(Mytable."No. Series"));
      <!-- Other Code -->
    end;

    procedure PostJournal(GenJnlLine)
    begin
      <!-- Post the journal -->
    end;

}

What’s going wrong?

  • The last number fetched is remembered internally.
  • If you’re looping, retrying, or processing in a long-running session, that number might get reused.
  • Result? ❌ Duplicate document numbers, failed journal lines, angry accountants, and awkward post-mortem calls.

Possible Solutions

Simple Solution

codeunit 50000 "My Codeunit"
{
    var
        GenJnlLine: Record "Gen. Journal Line";
        NoSeriesMgt: Codeunit NoSeriesManagement;

    procedure ProcessMytable(Mytable: Record "Mytable")
    begin
        CreateJournal(Mytable);
        PostJournal(GenJnlLine);
    end;

    procedure CreateJournal(Mytable)
    begin
      <!-- Other Code -->
      Clear(NoSeriesMgt);
      GenJnlLine.Validate("Document No.", NoSeriesMgt.GetNextNo(Mytable."No. Series"));
      <!-- Other Code -->
    end;

    procedure PostJournal(GenJnlLine)
    begin
      <!-- Post the journal -->
    end;

}

🧯 The Solution: Use ClearStateAndGetNextNo

To break out of this Groundhog Day loop, use the following method for BC version less than 24: ClearStateAndGetNextNo.

GenJnlLine.Validate("Document No.", NoSeriesMgt.ClearStateAndGetNextNo(GenJnlBatch."No. Series"));

✅ Why it works:

  • 🧽 Clears the internal state (no lingering “last number”)
  • 🔄 Ensures a new number is fetched from the series every time
  • 🚀 Perfect for job queues, background services, and loops
  • 🧘‍♂️ Eliminates side effects caused by retained memory

🔬 Side-by-Side Comparison

MethodRetains StateSafe for Job Queues?Behavior
GetNextNo✅ Yes❌ NoReuses last fetched number unless reset manually
ClearStateAndGetNextNo❌ No✅ YesAlways fetches a fresh number, clearing any cached state

🧠 Best Practices

  • 🔁 Always use ClearStateAndGetNextNo in:
    • Job queues
    • Loops processing multiple documents
    • Web service integrations
    • Background tasks
  • 🧑‍💻 Use GetNextNo only when working with UI-triggered or single-use synchronous processes.

🔍 Real-World Use Case

GenJnlLine.Validate("Document No.",
    NoSeriesMgt.ClearStateAndGetNextNo(GenJnlBatch."No. Series")
);

In this example, we ensure every journal line created from a job queue run gets a unique document number, no matter how many entries we’re looping through.


🧷 Conclusion

Using ClearStateAndGetNextNo isn’t just a “better” choice for job queues — it’s the only sane one. If you’re still using GetNextNo in your async code, you’re practically inviting a duplicate-doc-no party.

🎯 Be smart. Be stateless. Use ClearStateAndGetNextNo.


This post is licensed under CC BY 4.0 by the author.