Delayed tax calculation: the D365 setting I helped fix, and what it teaches you about importing large journals

In brief

By default D365 Finance recalculates sales tax across a whole journal on every line change, so a 1,000-line import triggers 500,500 calculations — the Delayed tax calculation setting stops that, deferring tax until you click Sales tax or post. Set it on the GL parameter, journal name or journal header (the header wins, and it inherits down the chain). For large general-journal imports it is only half the job: keep Set-based processing on above a few thousand clean, pre-numbered lines but leave it off below a thousand where row-by-row defaulting and validation are worth more, and mind number sequences, high-cardinality dimensions and voucher grouping.

Years ago, before Dr Dynamics, I sat on the other side of the support line at Microsoft.

A customer raised a performance case that has stuck with me ever since. They were importing one journal. One voucher. Thousands of lines. And it crawled.

When we dug in, the cause was almost comical. The system was recalculating sales tax on every single insert.

Line 1 goes in. Calculate tax on line 1.

Line 2 goes in. Recalculate tax on line 1 and line 2.

Line 3 goes in. Recalculate line 1, line 2, line 3.

You can see where this is going.

Import a 1,000-line journal that way and you don’t get 1,000 tax calculations. You get 1 + 2 + 3, all the way up to 1,000. That is 500,500 calculations to post a single voucher. The tax engine was doing half a million passes for a journal a person could read in five minutes.

I escalated it. The fix shipped in a service update a few releases later, around 10.0.17. You know it today as a quiet little checkbox: Delayed tax calculation.

Here is how it actually works.

What the setting does

By default, D365 Finance calculates sales tax on a journal line the moment a tax-related field changes: the sales tax group, the item sales tax group, the amount. Change one line and the system recalculates tax across the whole journal so you see live numbers on screen.

For a 12-line manual journal, that is exactly what you want. You see the tax as you type.

For a 5,000-line imported journal, it is the problem I just described.

Turn on Delayed tax calculation and the behaviour flips. The system stops calculating tax line by line. Nothing recalculates until someone clicks Sales tax or posts the journal. (Validation forces it too, because validation runs the posting routine.) You get the same tax result at posting, with none of the cost during data entry or import.

You can set it at three levels:

  • The legal entity, under General ledger parameters, Sales tax tab
  • The journal name, under Journal setup
  • The individual journal header, under the Setup tab on the journal

The journal header wins. It defaults from the journal name, which defaults from the GL parameter. So you set the parameter once and every new journal name inherits it, but you can still override a single journal on the rare occasion you want live tax on screen.

That is the whole feature. Small setting. Big difference on large journals.

But here is where I watch good teams stop too early.

Delayed tax calculation is not your only lever

If you are importing large general journals, the tax setting fixes one bottleneck. It does nothing for the import itself.

That is where Set-based processing comes in, and where I see the most confusion on real projects.

Does Set-based processing actually help?

Short answer: yes. With conditions.

The General journal data entity ships with Set-based processing turned on by default. The setting lives in the Data management workspace, under Data entities. When it is on, the framework pushes records into the target in bulk instead of inserting them one row at a time with full defaulting and validation on each row. Microsoft’s own guidance is blunt about it: set-based processing speeds up the import, and turning it off makes the General journal import slower.

So the box helps. But “tick it and walk away” is bad advice, because of what set-based processing gives up to go fast.

  • You have to supply the voucher number in your file. Set-based will not generate vouchers per balance for you. If you still want number-sequence vouchers, set Number allocation at posting to Yes on the journal name, put a temporary voucher number in the file, and group your lines by it carefully. Get the grouping wrong and unbalanced vouchers will not post.
  • You lose most of the row-by-row defaulting and validation. The DMF entity does minimal validation on import. Dirty data slips through and surfaces later, usually at posting, usually to someone who did not run the import.
  • It does not cover intercompany. The General journal DMF entity does not handle intercompany transactions. If that is your scenario, set-based is not the answer.

There is a practical threshold the community keeps landing on, and I agree with it. Above several thousand lines, keep set-based on and feed it clean, pre-numbered data. Below a thousand lines, the defaulting and validation you get by leaving it off is often worth more than the speed you would save. The setting is a trade, not a magic switch.

The rest of the large-journal import checklist

If you load big journals regularly, here is what I check beyond those two settings.

Group your lines sensibly. Microsoft’s guidance lands around 10,000 lines per journal batch number, with the physical vouchers kept small, say ten lines or fewer. Enormous single vouchers are their own performance problem.

Mind your number sequences. Do not set the journal batch number sequence to Manual. The General journal entity does not support it. Do not map the voucher number field to Auto-generated either, or the import simply fails.

Watch high-cardinality dimensions. When an import introduces a lot of dimension combinations the system has never seen, it slows down while it creates them. If one dimension is carrying thousands of brand-new values, that is usually your culprit, not the line count.

Use parallel tasks when set-based is off. For entities that do not run set-based, Data management can split the load across threads. Go to Framework parameters, Entity settings, Configure entity execution parameters, then set the import threshold record count and the import task count. One caution: more threads is not always better. Push the task count too high and you saturate the environment and slow down everything else running on it.

Trim validation once your mapping is mature. If a recurring import is well tested, you can dial back per-record business validation on the entity to claw back time. Do it carefully. You are trading a safety net for speed, and you only earn that trade once the data is genuinely clean.

What this really comes down to

Most performance problems I get called into are not really performance problems. They are default settings nobody revisited, doing exactly what they were designed to do, at a scale they were never designed for.

Live tax calculation is a feature, right up until you import 5,000 lines. Row-by-row validation is a safeguard, right up until you need to load 50,000 records before close. The defaults are fine for the keyboard. They fall apart at the file.

The job is not to memorise every switch. It is to understand what each default assumes about how data arrives, then change it when your reality does not match. One stubborn journal import taught me that years ago. It still holds.

Follow for more D365 Finance and AI content. I write about the unglamorous settings that quietly decide whether a finance system flies or crawls.

Want the full large-journal import checklist as a one-pager? It goes out to my newsletter subscribers first.

Get your fastest-path plan

Bring the state of your programme — your Dynamics version, your go-live date and what's worrying you. We'll come back with a one-page fastest-path plan within 48 hours.

The D365 compliance newsletter

Every two weeks: mandates, config fixes, no fluff. Join 750+ finance & ERP leaders.