Update 24-10-2022: I’ve got word from Microsoft that the issue discussed here has been solved in version v21 (2022 wave 2). The way it has been solved is that now the function Rec.WritePermission() returns false in case of a read-only request. I believe it still makes sense to have a function CurrPage.IsReadOnly like we have in reports. Please vote for the idea here: https://experience.dynamics.com/ideas/idea/?ideaid=c1342c97-994b-ed11-97b2-0003ff45cecf
This twitter message reminded me of an issue that can occur with the sales APIs in Business Central.
Apparently, the PowerBI connector is adding the header Data-Access-Intent: ReadOnly to requests when loading data from Business Central. While this is a good practice in general for GET requests, you may run into an error message with the sales APIs. This applies to salesQuotes, salesOrders and salesInvoices.
Below is an example from the salesOrders API:
If you remove the Data-Access-Intent header, then it works fine. However, after this, the Data-Access-Intent header can be added again and it still works! Below is a video that demonstrates this behavior. After reading without Data-Access-Intent: Readonly, it also works with this header enabled.
You can try this test yourself by following these steps:
- Call the salesOrders API without Data-Access-Intent: ReadOnly.
- Then turn on the header and make a second call. This will work
- Change the quantity on a sales order
- Call the API again (with the header turned on). It will fail.
- Go to step 1 and repeat…
So, what is going on? There is only one conclusion: sometimes the code behind the API wants to write to the database. But not always…
The reason is this little piece of code in the API page:
The field “Recalculate Invoice Disc.” is automatically set to true after every change to a Sales Line that triggers an update of the amounts. It’s in the function UpdateAmounts, and also in the OnValidate trigger for Type and No.
So, after one request without ReadOnly, it works fine until a sales document is modified. Then a request with ReadWrite (the default) is required.
Let’s not dive into the codeunit “Sales – Calc Discount By Type”. It’s quite straightforward what happens there, it calculates discounts and modifies the header and/or the lines. No matter if the new amount equals the existing amount.
Why does the API try to recalculate the invoice discount amount? I can only make an educated guess here. Usually, the recalculation of the invoice discount amount is done when a document is released. When the document is still open, only the flag “Recalculate Invoice Disc.” is set on the lines. But if an open sales document is read through the API, it may contain incorrect invoice discount amounts. Most probably that’s why the API calls the function to recalculate it.
But wait… there is a line if HasWritePermission then before the call. Doesn’t that work in a ReadOnly request? Apparently not… The boolean HasWritePermission is set in the function CheckPermissions in the API:
The method WritePermission checks if a user has permission to write, including the current security filters. It does not take the current data access intent into account.
Report objects do have a method IsReadOnly to get the current report’s data access intent. The page object is currently missing this method. That’s something for Microsoft to fix for us. To help Microsoft prioritize this, I’ve created an idea: https://experience.dynamics.com/ideas/idea/?ideaid=c1342c97-994b-ed11-97b2-0003ff45cecf. Please vote!
The best option we currently have is to change the default AccessIntent of the Sales APIs to ReadWrite. This can be done on the page Database Access Intent List. The setting is not company specific, it applies to the whole environment (or database for on-prem).
Hope this helps and don’t forget to vote for the idea!