Electronic Reporting (ER) Cookbook
The Electronic Reporting module in Dynamics 365 for Operations, previously known as General Electronic Reporting (GER) was one of the coolest application inventions in Dynamics AX7. Developed apparently in my alma mater – the Dynamics development office in Moscow – this “ER” is a surprisingly powerful tool with an own in-built programming language for transformations. A consultant may configure any vendor payment format or any moderately complex GL export (such as the German GDPdU or French FEC) within days with literally no customizations, and test it ad hoc.
Over the last year, I implemented the British BACS18, the Swiss DTA, the Spanish SEPA sub-format and the North American ACH outbound payment format. While the AX wiki was a good start, the best way to learn the tool is practice. Below are some transformation code snippets that may help you along the steep learning curve.
Line counters
In the payment files, there are often record numbers. The ER element “Counter” is intended to be used, but it is incremented on every occurrence in the format. I.e. the counter has a global context, and the same count may not be used more than once in a row.
A total of records in a footer section is a special case: you may enumerate the lines with a Counter, then use a function of the Group by type over the data source to get a total record count, see the screenshot.
With 2 concurrent counters you have no other choice but to use the [real numeric] journal line number, and convert it into an integer:
NUMBERFORMAT(VALUE(model.Payments.PaymentIdentifications.SourceBusinessEntityDescription), "00000000000")
Beware of the major handicap of this trick: deleting payment journal lines creates holes in the numeration.
Simple transformation formula examples
Convert an amount into an amount with fixed 2 decimal places after the commaNUMBERFORMAT(model.Payments.InstructedAmount, "#.00")
Remove the decimal separator, produce an amount in pence / cents, implemented as a subroutine (“transformation”) with a numeric parameterREPLACE(NUMBERFORMAT(parameter,"0.00"), ".", "", false)
Remove line breaks from the postal address, and truncate to 70 charactersLEFT(REPLACE(model.Payments.Debtor.PostalAddress.AddressLine,"\n"," ",true),70)
Remove all non-numeric characters with a regular expressionREPLACE(model.Payments.DebtorAccount.Identification.Number, "[^0-9]", "", true)
Calculate a weighted checksum
The below expression adds the 9th checksum digit to a US ABA routing number, should the user only have entered the 8 significant digits. The RIGHT(NUMBERFORMAT(10 – VALUE(), “#”), 1) is a crude implementation of modulo 10.CASE(LEN(parameter),
9, parameter,
8, parameter&RIGHT(NUMBERFORMAT(10-VALUE(RIGHT(NUMBERFORMAT(
(VALUE(MID(parameter,1,1))+VALUE(MID(parameter,4,1)))+VALUE(MID(parameter,7,1)))*3+
(VALUE(MID(parameter,2,1))+VALUE(MID(parameter,5,1)))+VALUE(MID(parameter,8,1)))*7+
VALUE(MID(parameter,3,1))+VALUE(MID(parameter,6,1))), "#"), 1)),
"#"), 1),
RIGHT(parameter, 9))
Day number in a year (1-366)
The British BACS18 format contained a date format that really made me suffer: for the 1st of February 2016 it expected “ 16032” where 32 was the day count from the 1st of January. The below code took me a while; it used the Gregorian calendar formula and predated the fact that if you start counting months from March, the length of the months oscillates 31-30-31…
The repetitive IF(…) is an implementation of (month + 9) mod 12.(VALUE(DATEFORMAT(parameter, "yyyy"))-
ROUNDDOWN(IF(VALUE(DATEFORMAT(parameter, "MM")) >2,
VALUE(DATEFORMAT(parameter, "MM")) -3,
VALUE(DATEFORMAT(parameter, "MM")) +9)/10, 0)
)*365
+ROUNDDOWN(
(VALUE(DATEFORMAT(parameter, "yyyy"))-
ROUNDDOWN(IF(VALUE(DATEFORMAT(parameter, "MM")) >2,
VALUE(DATEFORMAT(parameter, "MM")) -3,
VALUE(DATEFORMAT(parameter, "MM")) +9)/10, 0)
)/4, 0)
+ROUNDDOWN(
(IF(VALUE(DATEFORMAT(parameter, "MM")) >2,
VALUE(DATEFORMAT(parameter, "MM")) -3,
VALUE(DATEFORMAT(parameter, "MM")) +9) * 306 + 5)/10, 0)
+VALUE(DATEFORMAT(parameter, "dd")) - 1
-( (VALUE(DATEFORMAT(parameter, "yyyy")) - 1) * 365 +
ROUNDDOWN( (VALUE(DATEFORMAT(parameter, "yyyy")) -1)/4, 0) +
305)
This function should work correctly for the next 83 years. We IT people think in shorter terms than the Roman Catholic Church.
Update 02.05.2017: the November 2016 update to Dynamics 365 for Operations features an undocumented function DAYOFYEAR, and the above code reduces to justDAYOFYEAR(parameter)
Data model
To master the file format, you must first learn the data model, the ER’s configurable abstraction layer over the Dynamics database. Outbound vendor payments use the Payment model.
It provides generic payment attributes,
Attribute | Model node |
Payment date (user parm or journal date) | model.Payments.RequestedExecutionDate |
Amount | model.Payments.InstructedAmount |
ISO payment currency (journal line currency) | model.Payments.Currency |
Journal line number (see Line counters) | model.Payments.PaymentIdentifications.SourceBusinessEntityDescription |
Payment / Note | model.Payments.PaymentsNotes |
Voucher+RecId (a unique line ID) | model.Payments.PaymentIdentifications.EndToEndIdentification |
Date of today | model.CreationDateTime |
our bank details,
Attribute | Model node |
Our name (company name) | model.Payments.Debtor.Name |
Our full address (company address) | model.Payments.Debtor.PostalAddress.AddressLine |
Name of our bank | model.Payments.DebtorAgent.Name |
Our bank account number | model.Payments.DebtorAccount.Identification.Number |
Our IBAN | model.Payments.DebtorAccount.Identification.IBAN |
Routing number of our bank | model.Payments.DebtorAgent.RoutingNumber |
BIC of our bank | model.Payments.DebtorAgent.BICFI |
and their (beneficiary) bank details:
Attribute | Model node |
Their name (vendor’s name) | model.Payments.Creditor.Name |
Their full address (vendor’s payment address) | model.Payments.Creditor.PostalAddress.AddressLine |
Name of their bank | model.Payments.CreditorAgent.Name |
Their bank account number | model.Payments.CreditorAccount.Identification.Number |
Their IBAN | model.Payments.CreditorAccount.Identification.IBAN |
Routing number of their bank | model.Payments.CreditorAgent.RoutingNumber |
BIC of their bank | model.Payments.CreditorAgent.BICFI |
Electronic reporting blog series
Further reading:
Z4-Meldung an Bundesbank
Enumerate lines in Configurable Business Documents
D365 Electronic Reporting: JOIN records in pairs
Electronic Reporting (ER) Cookbook 4: References in a model
Electronic reporting for data migration
Electronic Reporting (ER) Cookbook 3: Working with dates
Electronic Reporting (ER) Cookbook 2: new tips from the kitchen
Electronic Reporting (ER) Cookbook
24 Comments
Thanks for sharing.
I need to add the vendor account number (Vendor ID), do you have a hint?
Try model.Payments.Creditor.Identification.’Source ID’
Great job,
Very clear.
Is there an option to separate the address?
As you described, it is possible to fill the complete address.
But I want to have the Country in a separate tag.
The same for the street and number.
And in a separate tag the postcode and city.
Is this possible?
Oh dear 😉 The answer is twofold:
I.e. you don’t have to split the string but just take the model.Payments.Debtor.PostalAddress.Country or model.Payments.Creditor.PostalAddress.Country for the ISO code.
REPLACE(REPLACE(model.'$FirstLineWithPackingSlip'.PackingSlipInfo, ".*: (?=\D)", "", true), " .*", "", true)
HI EUGEN,
Is there any documentation available for using REPLACE function by passing different patterns? Few of them are given in above examples- like (?=\D), “\n” etc.
If you have something around it, please share. It would be very much helpful.
Hello Neeraj, these are the standard Regular Expressions in the .NET flavour: https://en.wikipedia.org/wiki/Regular_expression
I usually test them in a Web interpreter first, than plug them into a ER formula: https://regex101.com/
Hi, I have added Vendor Bank account to my format model, however when the template is only picking up the first value on the Vendor Id List.
Any suggestions on how i can get the Vendor Bank Account for the Vendor ID on the payment Advice?
A screenshot may be beneficial. Why would you add anything to the format model if it contains model.Payments.CreditorAccount.Identification.Number already?
How can I add the creditors Bank Account Type as a string?
Hello, whatever the Bank account type is, it is not bound in the “Payment model mapping 1611”. I.e. you’ll need to derive an own mapping from the standard one and extract the type from the vendor bank account record. Converting an enumeration into a string is a challenge of its own. You can either build a simple CASE statement:
https://community.dynamics.com/365/financeandoperations/f/dynamics-365-for-finance-and-operations-forum/319051/electronic-report-framework-format-designer or a sophisticated chain of formulae and ‘calculated fields’: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/analytics/er-functions-text-getenumvaluebyname
Hi, to display routing number + bankidentification number in tag CdtrAcct > ID i use the formula
model.Payments.CreditorAgent.RoutingNumber & @.CreditorAccount.Identification.Number.
When i run the xml file it seems that for records without routing number in bank setup it gets the routing number of previous record in payment file. e.g bank , what is wrong?
I have my fields mapped, but when I save I keep getting the error ” Path ‘model/Payments/CreditorAccount/Identification/Number’ has no binding to any datasource in using model’s mapping “. Do you have an idea why?
This is strange, but it is a clear sign that this model node is not populated, i.e. not mapped to any backbone D365 table field. Derive you own mapping from the standard one, map the CreditorAccount/Identification/Number to the creditor’s bank account number, then make your derived mapping the default mapping.
Hi,
In my electronic reporting configuration, I would like to add a “%” after the numbers printed out. For example, instead of just showing “25” or “20”, I would like it to be “25%” or “20%”. Do you have a suggestion on how to formulate the formula?
I have tried the following, without succeeding:
Datasource & “%”
Hello, when I was solving a similar problem, I used the NUMBERFORMAT (?) function and used a “p” format string. ER implements the .NET formatting, check https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings and specifically the “p” specifier.
Thank you for a quick reply! It worked with the formula: NUMBERFORMAT(datasource, “#”)&”%”
Knowing that the data model is an abstraction of the actual database design, is there still a most optimal node branch to choose for certain types of data? Does it have much impact other than where you as the analyst find it when doing the mapping work?
For instance, what is the best node to store HcmWorker from Table CustInvoiceLineInterProj for eventual inclusion into an Intercompany Free Text Invoice?
My first try would be in a root node called “Invoice line – item(LineItem)” I would choose this since in Visual studio, looking at references I see that the table CustInvoiceLineInterProj is related to CustInvoiceLine.
I try to stay pragmatic, and choose ANY not yet mapped node in the existing model. There are usually many unused, unmapped fields. The reason is the ease of upgrade: if you start extending the Model and deriving your own sub-version, then any changes made by Microsoft to the standard model remain invisible in your format, unless you “Rebase” the model first, then your mapping, then your format. It’s very hard to maintain.
Thanks Eugen.
Hello Eugene, would you be kind enough to share also the screenshots of the ER config/code of the ACH and Batch headers? kinda like what you did here for the detail records. Thanks a bunch.
Hello, it will be in your mailbox in a few minutes. All the best to Anchorage, I did not know that people live there, like for real :))
Hello Eugene, would you be able to help me provide mapping with line wise VAT amount value in posted invoice.
Sorry, no, this is an exotic setup over here.