Tested on:Dynamics 365 version 8.2.2 and 9.0.2 (web client)
I recently ran into an interesting problem where several identical and near identical invoices were found in Dynamics 365 Project Service Automation. Not only were there duplicate invoices but deleting them led to a situation where Actuals that were already on a confirmed invoice found their way back to the system to be picked up on new invoices. All these issues eventually come down to Batch Jobs and the processes behind them but before we jump to the conclusion, let's investigate each individual issue.
When a Time Entry is approved, the Billing status ID (msdyn_billingstatus) of an Unbilled Sales Actual has no value. When this Actuals is pulled on an invoice the Billing status ID changes to Customer Invoice Created. When the invoice is confirmed the Billing status ID changes to Customer Invoice Posted.
If there are duplicate PSA invoices in Dynamics 365 CE with identical Invoice Line Details on each invoice, it's important to verify if some of these invoices have been confirmed or not. There are two scenarios that can happen when deleting a duplicate invoice. These are based on what the Project Invoice Status (msdyn_projectinvoicestatus) of the invoice is. Before we examine these scenarios it's important to understand that when an invoice with a Project Invoice Status of Draft is removed, the related Actuals are released and are available to be picked up on new invoices. The Billing status ID on these Actuals is set to no value and the related Transaction Connections linking Invoice Line Details to Actuals are deleted.
1. Multiple invoices have a Project Invoice Status of Draft
In this case there are no Confirmed duplicate invoices in Dynamics 365 CE. For the sake of example let's say we have 2 duplicates. Deleting these duplicate draft invoices is safe and as there are no Confirmed invoices, there are no Actuals that have a Billing status ID other than no value. Deleting the invoices sets a Billing status ID of no value to no value and no harm is done.
2. Some invoices have a Project Invoice Status of Draft while others have a status of Confirmed
Let's say we have 2 duplicate invoices in the system. One is Draft and the other is Confirmed. In this scenario we run into issues with Actuals. As the Billing status ID of the Actuals is Customer Invoice Posted (we have a confirmed invoice, remember), deleting the draft invoice sets the Billing status ID of the related Unbilled Sales Actuals back to no value. This naturally means that the specific Actuals are pulled on a new invoice when one is created. This scenario is illustrated in the photos below.
Confirmed invoice and its Invoice Line Detail.
The Unbilled Sales Actual related to the Invoice Line Detail.
Changing the Billing status ID of the Unbilled Sales Actual to no value.
A new invoice is created from the Project Contract's context.
ILDs on the new invoice. The Actual has found its way on the invoice as the Billing status ID was changed to no value.
The Billing status ID of the Actual is Customer Invoice Created as the Actual is on an unconfirmed invoice.
As the photos show, in this scenario we may accidentally invoice the Actual twice. If there are duplicate invoices in Dynamics 365 CE, the correct procedure is to delete the duplicate invoice and then set the Billing status ID of the related Unbilled Sales Actuals to Customer Invoice Posted. This way we are able to mitigate duplicate invoices without compromising invoicing.
When investigating the cause of duplicate invoices I looked at Batch Jobs more closely. If you have read my article on Batch Jobs you will know that the workflows behind them are called ProcessRunner and ProcessRunCaller. In short they work in the following manner:
Fire off ProcessRunner once to create an infinite loop of the ProcessRunner workflow.
This was called and fired off by ProcessRunner in step 2.
As the ProcessRunner workflow creates invoices I thought I would filter all Batch Jobs under System Jobs to see what I have in the system. As the image below shows, there are 5 ProcessRunCallers waiting. This means that on June 30th and July 1st at 14.06 there will be 5 ProcessRunners running concurrently if there are available resources for the async service to pick them up. What will happen if 5 ProcessRunners run in parallel?
I let ProcessRunCaller and ProcessRunner do their job and came back to Dynamics 365 Project Service Automation two days later. As we can see from the photos below, ProcessRunner created 5 invoices against my Project Contract "Batch job demo" on the 30th and 5 invoices on the 1st. It's interesting to see that on the 30th there is one invoice with a value of 200 (this is correct) while the rest are for a value of 0. Then again on the 1st all invoices have been created with a value of 350. This means we now have duplicate invoices with duplicate Invoice Line Details in the system.
Why did we have 5 ProcessRunCallers in the system in the first place? The answer to this is user error in all its simplicity. It is possible to fire off ProcessRunner manually as many times as one desires. A user may easily choose to run ProcessRunner when invoicing is at hand and invoices are to be created for further processing. This is actually quite understandable as a company's invoicing process most likely will not look at when Dynamics 365 CE creates invoices and instead there is a business need for creating invoices on-demand when it's time to send them to the customer.
Based on the findings we can summarize that there can only be a single ProcessRunner / ProcessRunCaller queued in Dynamics 365 CE. Multiple processes may lead to multiple duplicate invoices and cause additional problems when duplicate invoices are removed.
While the OOTB processes should not be edited or deleted to guarantee compatibility in future PSA updates, the OOTB workflows don't need to be used to run Batch Jobs. If there's a need to run Batch Jobs on-demand, a valid approach is to build a new workflow to accomplish this. Below is an example of a workflow that can be used to run Batch Jobs on-demand.
Custom Batch Job workflow
Running the custom Batch Job workflow on-demand
Resulting invoice has correct Invoice Line Details
Resulting Actuals are correct
I hope this article helps you use Batch Jobs correctly. Keep in mind that you should only have 1 ProcessRunner / ProcessRunCaller waiting in Dynamics 365 CE. If you need to run Batch Jobs on-demand, build a new workflow and use that instead on ProcessRunner. Huge thanks to the people at Microsoft who helped me diagnose and solve this mystery!
Disclaimer:All my blog posts reflect my personal opinions and findings unless otherwise stated.