Quantcast
Channel: Waldo's Blog Microsoft Dynamics NAV & Business Central
Viewing all 336 articles
Browse latest View live

NAVUG European Congress 2016

$
0
0

There is a new congress in town. Or at least – in Stuttgart :-). NAVUG is coming to Europe for the very first time this year!

As NAVUG is very strongly focused on the User, this is really another type of congress that we’re actually used to (in Europe). Now, NAVUG is all about education, networking and best practices. If you look at the schedule, you see that that is exactly what they are focusing on for their target group: IT/Developers, Finance people, Data Analysts and Project Managers.

This is how NAVUG describes the congress themselves:

NAVUG European Congress is dedicated to helping Dynamics NAV technical and finance users maximize the return on their Dynamics investment through instructional sessions led by experienced users and partners focusing on past and current versions of Dynamics NAV. Dynamics NAV users will receive practical advice that drives innovation and positive business outcomes.

And here is a glimpse on the schedule:

  • Dynamics NAV today, tomorrow, and beyond
  • Best Practices for Dynamics NAV administration and security
  • Data Visualisation Best Practices
  • Strategies for continuous upgrades
  • Maintaining target system performance when upgrading and migrating from on-premise to a virtual environment
  • What is C/AL and why should I care?
  • Converting your classic client objects to the latest Dynamics NAV version
  • The time is now to get started with Microsoft Power BI
  • Upgrading? Don’t forget about your team
  • Global vs. Local: Localizations and cultural aspects
  • Integration with Microsoft Dynamics CRM “out of the box”
  • Upgrading your reports from Dynamics NAV 2009 ClassicMicrosoft Dynamics NAV Support Desk
  • Sponsor Reception & Networking (Expo open)
  • Automated testing in Dynamics NAV
  • Reporting solutions examined: Jet Express
  • World-class product handling and customer service at Nature’s Pride
  • Data integration today and tomorrow
  • How we went from Dynamics NAV 3.7 to NAV 2015 in four weeks
  • Power BI in action
  • A whole new world: Role Centers in Dynamics NAV 2016
  • Migrating from on premise to the cloud
  • The Big Deal about Big Data
  • Achieving perfection with Dynamics NAV
  • Connecting Dynamics NAV with add-on solutions
  • The pros and cons of working with upgrade centres
  • Approaches to managing your historical data
  • How we use Dynamics NAV Project Management at Frederiksberg Forsyning
  • Recruit graduates with Microsoft Dynamics experience

Date and Location

9-10 May, 2016

International Congress Centre

Stuttgart, German

Some more resources

Here are some more blogs about the congress:

Too bad I’m not able to join – already traveling too much – but certainly recommended!


Project “Madeira” Public Preview

$
0
0

As of today, Project “Madeira” is in public preview. Question is .. What is Project “Madeira”? In short: It’s a new offering in the cloud, in fact “NAV-in-the-cloud”, strongly integrated with Office 365. But the focus of Project “Madeira” isn’t really “NAV” – it would never say “NAV-in-the-cloud” – but it’s a “Business Solution” in the cloud :-). But now you know: it’s based on NAV :p.

But then again – what do I know. :-). This is all just very new (announced today), and I think – at this point – it’s better for me to give you some info to get you more informed, instead of giving my personal view, opinion and feel on it.

And I think the best link of an overview of what Project “Madeira” really is, is this blogpost from Marko Perisic, the General Manager for Dynamics SMB. Marko talks about four pillars:

  • A new level of productivity
  • Cloud first
  • Mobile first
  • Built for growth

As I have a development-oriented background (and blog) – let me try to put it in my own words – as far as a developer is able to understand the Marketing Fluff ;-):

  • It is strongly integrated with cloud, like a strong integration with Office 365 Outlook. The software recognizes customer, showing customer data, or quotes and such, and without switching between applications.. . Or like Marko says: “If you know how to use Office, then you know how to use Project “Madeira””. I wasn’t able to see this just yet .. but will make new attemps in the near future ;-).
  • It’s NAV on azure, with O365, hosted by Microsoft, sold by Partners – at least, when it gets released (and how I understand). All benefits that comes with cloud – comes with project “Madeira”: pricing, scalability, quickly up-and-running, .. .
  • With any client, you can work with project “Madeira”, on a web browser, or an app on Windows, iOS or Android.
  • Microsoft focuses on companies with 10-100 employees, where project “Madeira” offers a fully powered business management solution in the cloud.
  • These are the functionalities it covers:
    • Financials
    • Dimensions
    • sales and purchase management
    • Inventory
    • CRM (opportunity management)
    • multi-currency
    • business insight
  • And last but not least – interesting for us – developers – we can customize it. We – as “independent software vendors” (aka ISVs) can build rich extensions to make available on a Marketplace (how I understand it: an app-store for project “Madeira” customers).

Interested? Well – if you’re a US company, you can sign up for preview here:

(yep, only US :( )

And get more info here http://aka.ms/MadeiraFAQ– though, when I tried this link, lots of sub-links didn’t seem to work – I guess this will be fixed soon.

One last thing I should mention: attend Directions to get first-hand info. One might expect that around the time of these conventions, the final release of project “Madeira” takes place (though, that’s only my expectation, no release date known just yet …). You can find more information on Directions here:

Some screenshots

Signing up was not easy for me as this thing is only available in the US – and I’m from beautiful Belgium (yes, Trump, Belgium is still beautiful, and you’re still an idiot :p). At about all my mail addresses I got the message: not available for your country. But hey .. Apparently, I have enough of them, so here are the first screenshots of Project “Madeira” public preview.

Getting excited during setup :-):

(this screen took a long time … MUCH more than “less than a minute” – but that’s ok. Probably I just started this thing before the preview was actually available in the first place :-) ).

Anyway, let’s look a some things that I noticed during my first walk around:

Project “Madeira” seems to be available like an app in Office365:

First time you open the client, you get a very familiar screen – but clearly tailored to (let’s quote Erik Hougaard) a “Cloud first, Mobile first” world :-).

Let’s have a look at the “Extension Management”. Here you see which Extensions are available, and if they are installed. I can only imagine that in a later release, you can “buy” new extensions from ISV’s in a yet-to-be-released market place.

Also cool on this list are display buttons: it’s seems to become possible to display a list in different ways, like “normal list, pictures (like above) and bricks”.

In the Role Center, you’ll find an “Assisted Setup & Tasks” action, which I really like.

Basically, it guides you through the setup of your new system, with wizards and statuses and all, like this list (in Bricks this time – just to show you):

And a wizard-setup, like in this case, for setting up Email:

When I opened the Item list, this is what it looked like :-):

I wasn’t able (yet) to set up the Office 365 integration, as my Office365 is a Belgian one – and I couldn’t start project “Madeira” for it – but I’ll find a way in the near future :-).

To conclude, I spent some time in looking up some resources (some other blogs / articles) for you to read up:

Seems that I was late with my blog :-) .. but hey, it was Champions League night .. and glad I watched because Kevin scored! :-)

Setting up Project “Madeira” Preview (also when you’re not from the US)

$
0
0

The last few days (between the soup and potatoes (Flemish expression ;-)), I have been trying to set up a full blown environment on Project “Madeira”. Not easy, as I am not from the US, and this preview only got released in the US. And apparently, Microsoft is serious about that – like when I try to download the Windows App, I get the error message that the app is not available in my country …

Now, important to mention: this blogpost is based on the comments I got on my previous post (from Jacques Bolduc) – which put me in a direction that is available for any Microsoft Dynamics NAV Partner – to the best of my knowledge at least. Just wanted

What do you mean with “full blown”

Well, probably I exaggerate a bit .. but I mainly wanted to be able to test:

  • The Exchange-integration in Office 365 Outlook (I only tested it online)
  • The Universal App (or should I say: the Windows/Android/iOS app “Project Madeira”)
  • The single-sing-on with O365
  • The Web Client and Project Madeira functionality

May be, I’m forgetting something – or a lot – but I just wanted to get that working.

And here is how I did it (not saying it’s the only way to make it happen) :-).

First: set up Office365

As a Microsoft Partner, you should have access to its demo-portal, where you can set up demo-tenants for Office365. I didn’t know it existed (thanks, Jacques :-)), but it does, and it worked for me just like it has existed for years. Just sign in with your Microsoft account to: https://demos.microsoft.com.

Create a new tenant (which in my understanding is a temporary (hence: demo) Office365 account) and you’re good to go. I created the “Sales Productivity and Intelligent Customer Service” tenant (seemed interesting to me to have CRM in there as well ;-))

Done! You have an Office365 account within one minute. You can test it out by browsing to https://portal.office.com/ and logging in with the credentials you just got back from the demo-system.

Second: Sing up for Project “Madeira”

From now on (and for the rest of the steps), I strongly recommend to work in a “InPrivate” browser session. I have got some problems by not doing that – because I’m having multiple Microsoft Accounts .. which apparently “confuses” the process. Just use an “InPrivate” window can save you this mess.

So .. open an InPrivate window, and browse to https://madeira.microsoft.com/. There, you can click the “Try the preview“:

Time to fill in the account that you created in the previous step. Like:

Just click “Sign Up” – log in if necessary (you should be logged in still) – and click “Start” to initiate the provisioning of Madeira:

Wait for this to finish to continue for the next step. (takes more than a minute, by the way :p – but it sounds great, doesn’t it ;))

When finished, Madeira is already there for you, and you are able to log into your tenant with the newly created O365 account. In fact – it will log in automagically, and show you this:

You are free to enjoy the wonderful click through experience (I wonder how this has been created – interesting :-)), but I clicked “Not Now”, and continued with the next step.

Third: Configure Email Integration

Let’s go straight into the configuration of the mail-addin in Office 365. Just go to Actions, and click the “Assisted Setup” button:

You’ll get this:

In the list, click “Set Up Office Add-Ins” (I won’t go into the other settings – it’s quite obvious, isn’t it?).

During the wizard, make sure you Set it up for “My Organization”:

And use your Office365 account – which is an admin, so should be fine!

Also, send the sample email messages .. which make a lot of sense to try out the Outlook Integration :-).

When it shows “Completed” test it out by navigating to your mail (you can do this from your Madeira):

You’ll see a mail from (for example) “Andy Teal”, and when you browse to it, you’ll see the Madeira plugin:

Start enjoying it! It’s amazing!

Fourth: Download App from client

Now, downloading the “Universal” client – the app – the thing that works on all devices (how do you kids call it these days .. ). You can initiate the download from Madeira (notice that your menu contains a Madeira “app”):

In Madeira, just start the “Replay Getting Started”

In the wizard, we’re interested in the “One Last Thing” part – so press “next” until you reach this:

And click “Show me how”

You’ll see you get this link: http://aka.ms/GetMadeira (you can use this link on any device!) and a QR Code to get you to the app.

Also here, there is country-check. I’m not able to test on all devices .. but to get it to work on windows if you’re not from the US, is simply change your country-settings to “United States”

Just install the app now, and switch it back to the original value. Once installed, you own the app, and you’re good to go and use it :-).

Open the app, provide your activation code, sign in .. and enjoy!

I have an iPhone as well – but must admit, I didn’t get it to work (yet) because of the country-checks. I guess I need to change the settings of my AppleID (as it always tries to download the client from the Belgian Store – and isn’t available there) – though, when I tried that, I was not able to change the country (apparently, according to Apple, I can’t move to another country ;-)). I’ll try some more in the next few days..

That’s it .. If there is anything more to add – please leave a comment! :-)

The Future of Dynamics NAV Extensions

$
0
0

Last week, there was a call from Microsoft (a so called “Directions Webinar”) all partners could attend. It was a public call, with about 400 people – which kind of means: the information is considered to be public.

Now, I’m not good in expressing my opinion. I never felt that it was my job nor obligation to tell Microsoft what they have to do, or what not. May be for some details, but not for major strategic decisions. Let’s just say I’m a sailor, not a boat-manufacturer, nor am I the wind ;-). So I’ll drive my boat to wherever the wind takes me – and I’ll updated my boat as much as possible whenever new features come around .. and learn how to use them .. en even think of best practices on how to use them ;-). How’s that for a metaphor :p?

So this blog is not going to be about my opinion on Extensions. It’s merely to share the content of the Microsoft’s webinar about Extensions. The webinar is already shared on the Dynamics Learning Portal here.

The webinar was mostly on “the future of Dynamics NAV Extensions” (hence the title :-)) lead by Kurt Juvyns.

One of the first statements was quite powerful:

Extensions in Microsoft Dynamics NAV are quickly becoming more capable. It should be a fundamental part of your go to market strategy. It is your way to enhance NAV and to differentiate.

Well, hold on, Microsoft. Let’s be honest. There just isn’t enough possible with Extensions just yet to just drop everything that we’re used to, and start doing all with Extensions. The development capabilities are just not there yet.

True .. all that Microsoft tries to say here is that you should start diving into it – because they are moving fast, and before you know it, extensions ARE fully capable, and you might not be …

Business Apps Marketplace for Dynamics NAV Extensions
This is very premature information, and Microsoft is still working on a lot of details. So there are a lot of questions and details to fill in. But one thing is for sure: Microsoft is working on a Marketplace for NAV Extensions.

You all have read about Project “Madeira”, right? There has been a LOT of posts about that, like my two blogs:

When you think of it, some kind of Marketplace, where customers of Project “Madeira” can buy small additions to their software, makes a lot of sense. And it’s exactly this what they (Microsoft) are working on.. . And we are going to be able to develop Extensions to put in the Marketplace .. oh yes we are :-). Talking about new opportunities…

Can anyone just upload stuff to the Marketplace?

Well, no. There is going to be a validation process. Microsoft will guarantee that only Extensions with a certain quality on numerous areas. It’s not a CfMD process, but “something that happens before you upload”. Let’s call it a “quality assurance”. Something customers will want before they install your Extension ;-). So we need to be serious, people…

When you look at Project “Madeira”, you can see a page “Extension Management” which Kurt said that at a certain point would evolve in a “Business Apps Marketplace”. So my guess is that we will be able to see and add Extensions from within NAV .. but that’s only my personal assumption (an you know what is said about “assumptions”).

Licensing

I can imagine you have loads of questions about licensing. Well – no details there yet, as far as I know. All we know, is that extensions need a completely new licensing model .. and Microsoft is still “whiteboarding” to make it what you like it to be ;-). All we know for now is that the Licensing model (if i can call it like that) for Project “Madeira” is going to be “CSP”: Cloud Solution Provider. You should already get yourself familiar with it .. and you can do that here: https://partner.microsoft.com/en-US/Solutions/cloud-reseller-overview

Extension in Microsoft Dynamics NAV are quickly becoming more capable
Well, that was what that powerful statement above was saying. But what does that mean? Let me answer very easy with a slide from the webinar. Here, you can see what NAV Extensions can do with NAV2016, and what the capabilities will be in the fall – with project “Madeira”:

That’s quite complete, right? Sure, we need to wait a bit for it .. but it’s coming nevertheless! We got a demo of it, where Microsoft showed an Extensions that facilitated a “Rewards program” (something like air miles or whatever). They showed that it was possible to add an Add-In, reports, queries, an ODATA service based on the query, translation fils .. all stuff that isn’t possible today with NAV2016, but stuff we need for Extension to make sense..

Here some screenshots on the AppFiles that needs to be included in the extension, like obviously the delta files .. but also an Addin.zip, translationfile.txt, queryWebservice.xml, … and more:

So .. NAV is getting all over the place. We have NAV on premise, we have NAV in the cloud on some kind of private SaaS either on Managed Services or our own hosted platform .. and we will have some kind of public SaaS offering from Microsoft with Project “Madeira”. You might wonder how Extensions come in to all of these offerings .. .

Well, do know that the Marketplace will first be available for US customers only, as there is quite a big focus for Project “Madeira” now, which will be released in the US first. But that doesn’t mean that Extensions are not interesting for the other offerings. Let’s review this slide:

I have been thinking for my own company on what we are going to do with Extensions. And for this moment, we don’t do anything with it in production. As mentioned, NAV 2016 is just not capable at this point. But we ARE:

  • Making ourselves aware
  • Exploring
  • creating best practices
  • deep diving
  • sketching design patterns
  • improving what we have now to facilitate extension later

And so should you! And here is how you can start making yourself familiar: http://aka.ms/navextensions .

So, this is all I have to share about the “Directions Webinar” on Microsoft Dynamics NAV Extensions. Again, you can watch the webinar here. Great job, Kurt Juvyns. :-)

NAV 2016: All Published Events

$
0
0

Blogging has been low lately. I have been insanely busy with providing content for the Cloud SureStep for Product Development on the DLP, which means: less free time :(.

But ..

Together with a brilliant colleague, I’m also working on an internal project: A code analyzer in PowerShell. Why in PowerShell .. well .. because in my opinion, it belongs there: I just don’t know what I want to analyze just yet  and when I do know, I want to be able to create it within minutes, and put it in my test-library as some kind of automated test or part of my build server or wherever I want it .. .

The tool is something that understands NAV Code (C/AL, C/SIDE Objects, …) to be able to analyze them.. . And we’re getting quite far with it. Unfortunately, this is not something that I can put up for free .. but I can put up some “output” of the tests that I have been doing with it. Today, my first “output”:

All information on the published events of NAV2016

You might remember my blog “NAV 2016 Eventing: All published Integration and Business events“. I provided a script that showed all the Events in NAV. Well .. I wanted to know more. Much more! I wanted to know:

  • Which events are there (ok, this I already had)
  • How many times are they raised
  • Where are they declared
  • Where are they raised

So all information on the publishers that I could find.

At the end of this post, you can find the result. On top, you first have a table with two columns: “the number of times the publisher was raised”, and “the key of the element that identifies the publisher”. I think the key is readable enough ;-). You can use this key to search further in the output, for all the places it was raised. I mark the line in code of where it was raised.. .

Call for Feedback / Ideas

This was actually just an example for me to test the tool, but the result is quite interesting. If you have any feedback on this output, if you want to show it in some other way, or if you would like to know even more on publishers .. Please provide a comment and let me know.

Or even better: if you’re interested in any other kind of analysis on NAV, again, please let me know – and I might just do it, and put it online as well ;-).

I’m currently thinking of:

  • Analyzing COMMITs (how many, where, …)
  • Cyclomatic Complexity
  • Duplicate Control Ids (although this is not interesting in default NAV)

Waldo, finally show me the output

Here it is:

Number of times a publisher was raised: 
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterAutoFormatTranslate>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterCaptionClassTranslate>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterCompanyClose>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterCompanyOpen>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterFindPrinter>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetApplicationVersion>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetDatabaseTableTriggerSetup>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetDefaultRoleCenter>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetGlobalTableTriggerMask>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetSystemIndicator>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeCodeFilter>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeDateFilter>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeDateText>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeDateTimeFilter>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeText>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeTextFilter>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeTimeFilter>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeTimeText>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseDelete>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseInsert>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseModify>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseRename>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalDelete>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalInsert>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalModify>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalRename>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnBeforeCompanyClose>
1 : Codeunit<ApplicationManagement>.PROCEDURE<OnBeforeCompanyOpen>
1 : Codeunit<Gen. Jnl.-Check Line>.PROCEDURE<OnAfterCheckGenJnlLine>
1 : Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnAfterInitGLRegister>
1 : Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnAfterInsertGlobalGLEntry>
1 : Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnBeforeInsertGLEntryBuffer>
1 : Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnBeforePostGenJnlLine>
1 : Codeunit<Release Incoming Document>.PROCEDURE<OnAfterCreateDocFromIncomingDocFail>
1 : Codeunit<Release Incoming Document>.PROCEDURE<OnAfterCreateDocFromIncomingDocSuccess>
1 : Codeunit<Release Incoming Document>.PROCEDURE<OnAfterReleaseIncomingDoc>
1 : Codeunit<Send Incoming Document to OCR>.PROCEDURE<OnAfterIncomingDocReadyForOCR>
2 : Codeunit<Send Incoming Document to OCR>.PROCEDURE<OnAfterIncomingDocReceivedFromOCR>
1 : Codeunit<Send Incoming Document to OCR>.PROCEDURE<OnAfterIncomingDocSentToOCR>
1 : Codeunit<Doc. Exch. Service Mgt.>.PROCEDURE<OnAfterIncomingDocReceivedFromDocExch>
1 : Codeunit<Workflow Setup>.PROCEDURE<OnAddWorkflowCategoriesToLibrary>
1 : Codeunit<Workflow Event Handling>.PROCEDURE<OnAddWorkflowEventPredecessorsToLibrary>
1 : Codeunit<Workflow Event Handling>.PROCEDURE<OnAddWorkflowEventsToLibrary>
1 : Codeunit<Workflow Event Handling>.PROCEDURE<OnAddWorkflowTableRelationsToLibrary>
1 : Codeunit<Workflow Response Handling>.PROCEDURE<OnAddWorkflowResponsePredecessorsToLibrary>
1 : Codeunit<Workflow Response Handling>.PROCEDURE<OnAddWorkflowResponsesToLibrary>
1 : Codeunit<Workflow Response Handling>.PROCEDURE<OnExecuteWorkflowResponse>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnApproveApprovalRequest>
3 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelCustomerApprovalRequest>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelGeneralJournalBatchApprovalRequest>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelGeneralJournalLineApprovalRequest>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelIncomingDocApprovalRequest>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelItemApprovalRequest>
12 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelPurchaseApprovalRequest>
12 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelSalesApprovalRequest>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelVendorApprovalRequest>
1 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnDelegateApprovalRequest>
1 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnRejectApprovalRequest>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendCustomerForApproval>
1 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendGeneralJournalBatchForApproval>
1 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendGeneralJournalLineForApproval>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendIncomingDocForApproval>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendItemForApproval>
12 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendPurchaseDocForApproval>
12 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendSalesDocForApproval>
2 : Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendVendorForApproval>
1 : Codeunit<Item Jnl.-Check Line>.PROCEDURE<OnAfterCheckItemJnlLine>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInitItemLedgEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertCorrItemLedgEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertCorrValueEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertItemLedgEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertValueEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterPostItemJnlLine>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertCorrItemLedgEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertCorrValueEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertTransferEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertValueEntry>
1 : Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforePostItemJnlLine>
1 : Codeunit<Release Sales Document>.PROCEDURE<OnAfterReleaseSalesDoc>
1 : Codeunit<Release Sales Document>.PROCEDURE<OnAfterReopenSalesDoc>
1 : Codeunit<Release Sales Document>.PROCEDURE<OnBeforeReleaseSalesDoc>
1 : Codeunit<Release Sales Document>.PROCEDURE<OnBeforeReopenSalesDoc>
1 : Codeunit<Release Purchase Document>.PROCEDURE<OnAfterReleasePurchaseDoc>
1 : Codeunit<Release Purchase Document>.PROCEDURE<OnAfterReopenPurchaseDoc>
1 : Codeunit<Release Purchase Document>.PROCEDURE<OnBeforeReleasePurchaseDoc>
1 : Codeunit<Release Purchase Document>.PROCEDURE<OnBeforeReopenPurchaseDoc>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterApplyRecordTemplate>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterInsertRecord>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterModifyRecord>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterTransferRecordFields>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeApplyRecordTemplate>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeInsertRecord>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeModifyRecord>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeTransferRecordFields>
1 : Codeunit<Integration Table Synch.>.PROCEDURE<OnFindUncoupledDestinationRecord>
2 : Codeunit<CRM Integration Table Synch.>.PROCEDURE<OnQueryPostFilterIgnoreRecord>
1 : Codeunit<Sales-Calc. Discount>.PROCEDURE<OnAfterCalcSalesDiscount>
1 : Codeunit<Sales-Calc. Discount>.PROCEDURE<OnBeforeCalcSalesDiscount>
1 : Codeunit<Purch.-Calc.Discount>.PROCEDURE<OnAfterCalcPurchaseDiscount>
1 : Codeunit<Purch.-Calc.Discount>.PROCEDURE<OnBeforeCalcPurchaseDiscount>
1 : Codeunit<Sales-Post>.PROCEDURE<OnAfterPostSalesDoc>
1 : Codeunit<Sales-Post>.PROCEDURE<OnBeforePostCommitSalesDoc>
1 : Codeunit<Sales-Post>.PROCEDURE<OnBeforePostSalesDoc>
2 : Codeunit<Purch.-Post>.PROCEDURE<OnAfterPostPurchaseDoc>
1 : Codeunit<Purch.-Post>.PROCEDURE<OnBeforePostCommitPurchaseDoc>
1 : Codeunit<Purch.-Post>.PROCEDURE<OnBeforePostPurchaseDoc>
1 : Report<Send Overdue Appr. Notif.>.PROCEDURE<OnSendOverdueNotifications>
1 : Table<Incoming Document>.PROCEDURE<OnCheckIncomingDocCreateDocRestrictions>
1 : Table<Incoming Document>.PROCEDURE<OnCheckIncomingDocReleaseRestrictions>
1 : Table<Incoming Document>.PROCEDURE<OnCheckIncomingDocSetForOCRRestrictions>
1 : Table<Incoming Document Attachment>.PROCEDURE<OnAttachBinaryFile>
4 : Table<Service Connection>.PROCEDURE<OnRegisterServiceConnection>
1 : Table<G/L Entry>.PROCEDURE<OnAfterCopyGLEntryFromGenJnlLine>
1 : Table<Cust. Ledger Entry>.PROCEDURE<OnAfterCopyCustLedgerEntryFromGenJnlLine>
1 : Table<Gen. Journal Batch>.PROCEDURE<OnCheckGenJournalLineExportRestrictions>
1 : Table<Gen. Journal Batch>.PROCEDURE<OnGeneralJournalBatchBalanced>
1 : Table<Gen. Journal Batch>.PROCEDURE<OnGeneralJournalBatchNotBalanced>
1 : Table<Gen. Journal Batch>.PROCEDURE<OnMoveGenJournalBatch>
1 : Table<Vendor Ledger Entry>.PROCEDURE<OnAfterCopyVendLedgerEntryFromGenJnlLine>
3 : Table<Sales Header>.PROCEDURE<OnCheckSalesPostRestrictions>
1 : Table<Sales Header>.PROCEDURE<OnCheckSalesReleaseRestrictions>
3 : Table<Sales Header>.PROCEDURE<OnCustomerCreditLimitExceeded>
3 : Table<Sales Header>.PROCEDURE<OnCustomerCreditLimitNotExceeded>
3 : Table<Purchase Header>.PROCEDURE<OnCheckPurchasePostRestrictions>
1 : Table<Purchase Header>.PROCEDURE<OnCheckPurchaseReleaseRestrictions>
1 : Table<Gen. Journal Line>.PROCEDURE<OnCheckGenJournalLinePostRestrictions>
1 : Table<Gen. Journal Line>.PROCEDURE<OnCheckGenJournalLinePrintCheckRestrictions>
6 : Table<Gen. Journal Line>.PROCEDURE<OnMoveGenJournalLine>


DETAILS:
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterAutoFormatTranslate>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<AutoFormatTranslate>
    Code lines:
    AutoFormatTranslation := AutoFormatManagement.AutoFormatTranslate(AutoFormatType,AutoFormatExpr);
    OnAfterAutoFormatTranslate(AutoFormatType,AutoFormatExpr,AutoFormatTranslation); <=====================
    EXIT(AutoFormatTranslation);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterCaptionClassTranslate>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<CaptionClassTranslate>
    Code lines:
    Caption := CaptionManagement.CaptionClassTranslate(Language,CaptionExpr);
    OnAfterCaptionClassTranslate(Language,CaptionExpr,Caption); <=====================
    EXIT(Caption);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterCompanyClose>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<CompanyClose>
    Code lines:
    OnBeforeCompanyClose;
    LogInManagement.CompanyClose;
    OnAfterCompanyClose; <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterCompanyOpen>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<CompanyOpen>
    Code lines:
    OnBeforeCompanyOpen;
    LogInManagement.CompanyOpen;
    OnAfterCompanyOpen; <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterFindPrinter>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<FindPrinter>
    Code lines:
    ...
    IF NOT PrinterSelection.GET(USERID,ReportID) THEN
      IF NOT PrinterSelection.GET('',ReportID) THEN
        IF NOT PrinterSelection.GET(USERID,0) THEN
          IF PrinterSelection.GET('',0) THEN;
    PrinterName := PrinterSelection."Printer Name";
    OnAfterFindPrinter(ReportID,PrinterName); <=====================
    EXIT(PrinterName);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetApplicationVersion>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<ApplicationVersion>
    Code lines:
    AppVersion := CustomApplicationVersion('BE Dynamics NAV 9.0');
    OnAfterGetApplicationVersion(AppVersion); <=====================
    EXIT(AppVersion);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetDatabaseTableTriggerSetup>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<GetDatabaseTableTriggerSetup>
    Code lines:
    ChangeLogMgt.GetDatabaseTableTriggerSetup(TableId,OnDatabaseInsert,OnDatabaseModify,OnDatabaseDelete,OnDatabaseRename);
    IntegrationManagement.GetDatabaseTableTriggerSetup(TableId,OnDatabaseInsert,OnDatabaseModify,OnDatabaseDelete,OnDatabaseRename);
    OnAfterGetDatabaseTableTriggerSetup(TableId,OnDatabaseInsert,OnDatabaseModify,OnDatabaseDelete,OnDatabaseRename); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetDefaultRoleCenter>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<DefaultRoleCenter>
    Code lines:
    DefaultRoleCenterID := ConfPersMgt.DefaultRoleCenterID;
    OnAfterGetDefaultRoleCenter(DefaultRoleCenterID); <=====================
    EXIT(DefaultRoleCenterID);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetGlobalTableTriggerMask>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<GetGlobalTableTriggerMask>
    Code lines:
    // Replaced by GetDatabaseTableTriggerSetup
    OnAfterGetGlobalTableTriggerMask(TableID,TableTriggerMask); <=====================
    EXIT(TableTriggerMask);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterGetSystemIndicator>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<GetSystemIndicator>
    Code lines:
    IF CompanyInformation.GET THEN
      CompanyInformation.GetSystemIndicator(Text,Style);
    OnAfterGetSystemIndicator(Text,Style); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeCodeFilter>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeCodeFilter>
    Code lines:
    Position := TextManagement.MakeTextFilter(TextFilterText);
    OnAfterMakeCodeFilter(Position,TextFilterText); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeDateFilter>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeDateFilter>
    Code lines:
    Position := TextManagement.MakeDateFilter(DateFilterText);
    OnAfterMakeDateFilter(Position,DateFilterText); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeDateText>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeDateText>
    Code lines:
    Position := TextManagement.MakeDateText(DateText);
    OnAfterMakeDateText(Position,DateText); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeDateTimeFilter>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeDateTimeFilter>
    Code lines:
    Position := TextManagement.MakeDateTimeFilter(DateTimeFilterText);
    OnAfterMakeDateTimeFilter(Position,DateTimeFilterText); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeText>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeText>
    Code lines:
    Position := TextManagement.MakeText(Text);
    OnAfterMakeText(Position,Text); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeTextFilter>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeTextFilter>
    Code lines:
    Position := TextManagement.MakeTextFilter(TextFilterText);
    OnAfterMakeTextFilter(Position,TextFilterText); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeTimeFilter>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeTimeFilter>
    Code lines:
    Position := TextManagement.MakeTimeFilter(TimeFilterText);
    OnAfterMakeTimeFilter(Position,TimeFilterText); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterMakeTimeText>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<MakeTimeText>
    Code lines:
    Position := TextManagement.MakeTimeText(TimeText);
    OnAfterMakeTimeText(Position,TimeText); <=====================
    EXIT(Position);
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseDelete>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnDatabaseDelete>
    Code lines:
    ChangeLogMgt.LogDeletion(RecRef);
    IntegrationManagement.OnDatabaseDelete(RecRef);
    OnAfterOnDatabaseDelete(RecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseInsert>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnDatabaseInsert>
    Code lines:
    ChangeLogMgt.LogInsertion(RecRef);
    IntegrationManagement.OnDatabaseInsert(RecRef);
    OnAfterOnDatabaseInsert(RecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseModify>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnDatabaseModify>
    Code lines:
    ChangeLogMgt.LogModification(RecRef);
    IntegrationManagement.OnDatabaseModify(RecRef);
    OnAfterOnDatabaseModify(RecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnDatabaseRename>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnDatabaseRename>
    Code lines:
    ChangeLogMgt.LogRename(RecRef,xRecRef);
    IntegrationManagement.OnDatabaseRename(RecRef,xRecRef);
    OnAfterOnDatabaseRename(RecRef,xRecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalDelete>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnGlobalDelete>
    Code lines:
    // Replaced by OnDataBaseDelete. This trigger is only called from pages.
    OnAfterOnGlobalDelete(RecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalInsert>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnGlobalInsert>
    Code lines:
    // Replaced by OnDataBaseInsert. This trigger is only called from pages.
    OnAfterOnGlobalInsert(RecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalModify>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnGlobalModify>
    Code lines:
    // Replaced by OnDataBaseModify. This trigger is only called from pages.
    OnAfterOnGlobalModify(RecRef,xRecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnAfterOnGlobalRename>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<OnGlobalRename>
    Code lines:
    // Replaced by OnDataBaseRename. This trigger is only called from pages.
    OnAfterOnGlobalRename(RecRef,xRecRef); <=====================
Codeunit<ApplicationManagement>.PROCEDURE<OnBeforeCompanyClose>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<CompanyClose>
    Code lines:
    OnBeforeCompanyClose; <=====================
    LogInManagement.CompanyClose;
    OnAfterCompanyClose;
Codeunit<ApplicationManagement>.PROCEDURE<OnBeforeCompanyOpen>
  Raised in: Codeunit<ApplicationManagement>.PROCEDURE<CompanyOpen>
    Code lines:
    OnBeforeCompanyOpen; <=====================
    LogInManagement.CompanyOpen;
    OnAfterCompanyOpen;
Codeunit<Gen. Jnl.-Check Line>.PROCEDURE<OnAfterCheckGenJnlLine>
  Raised in: Codeunit<Gen. Jnl.-Check Line>.PROCEDURE<RunCheck>
    Code lines:
    ...
    END;
    IF CostAccSetup.GET THEN
      CostAccMgt.CheckValidCCAndCOInGLEntry(GenJnlLine."Dimension Set ID");
    OnAfterCheckGenJnlLine(GenJnlLine); <=====================
Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnAfterInitGLRegister>
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<StartPosting>
    Code lines:
    ...
      GLReg."Source Code" := "Source Code";
      GLReg."Journal Batch Name" := "Journal Batch Name";
      GLReg."User ID" := USERID;
      GLReg."Journal Template Name" := GenJnlTemplate.Name;
      OnAfterInitGLRegister(GLReg,GenJnlLine); <=====================
      GetCurrencyExchRate(GenJnlLine);
      TempGLEntryBuf.DELETEALL;
      CalculateCurrentBalance(
    ...
Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnAfterInsertGlobalGLEntry>
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<FinishPosting>
    Code lines:
    ...
          GlobalGLEntry."Add.-Currency Debit Amount" := 0;
          GlobalGLEntry."Add.-Currency Credit Amount" := 0;
        END;
        GlobalGLEntry."Prior-Year Entry" := GlobalGLEntry."Posting Date" < FiscalYearStartDate;
        GlobalGLEntry.INSERT(TRUE);
        OnAfterInsertGlobalGLEntry(GlobalGLEntry); <=====================
      UNTIL TempGLEntryBuf.NEXT = 0;
      GLReg."To VAT Entry No." := NextVATEntryNo - 1;
      IF GLReg."To Entry No." = 0 THEN BEGIN
    ...
Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnBeforeInsertGLEntryBuffer>
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<InsertGLEntry>
    Code lines:
    ...
      UpdateDebitCredit(GenJnlLine.Correction);
    END;
    TempGLEntryBuf := GLEntry;
    OnBeforeInsertGLEntryBuffer(TempGLEntryBuf,GenJnlLine); <=====================
    TempGLEntryBuf.INSERT;
    IF FirstEntryNo = 0 THEN
    ...
Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<OnBeforePostGenJnlLine>
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<StartPosting>
    Code lines:
    OnBeforePostGenJnlLine(GenJnlLine); <=====================
    WITH GenJnlLine DO BEGIN
      GlobalGLEntry.LOCKTABLE;
      IF GlobalGLEntry.FINDLAST THEN BEGIN
        NextEntryNo := GlobalGLEntry."Entry No." + 1;
        NextTransactionNo := GlobalGLEntry."Transaction No." + 1;
      END ELSE BEGIN
        NextEntryNo := 1;
        NextTransactionNo := 1;
    ...
Codeunit<Release Incoming Document>.PROCEDURE<OnAfterCreateDocFromIncomingDocFail>
  Raised in: Codeunit<Release Incoming Document>.PROCEDURE<Fail>
    Code lines:
    ...
      Status := Status::Failed;
      MODIFY(TRUE);
      COMMIT;
      OnAfterCreateDocFromIncomingDocFail(IncomingDocument); <=====================
    END;
Codeunit<Release Incoming Document>.PROCEDURE<OnAfterCreateDocFromIncomingDocSuccess>
  Raised in: Codeunit<Release Incoming Document>.PROCEDURE<Create>
    Code lines:
    ...
      Status := Status::Created;
      MODIFY(TRUE);
      COMMIT;
      OnAfterCreateDocFromIncomingDocSuccess(IncomingDocument); <=====================
    END;
Codeunit<Release Incoming Document>.PROCEDURE<OnAfterReleaseIncomingDoc>
  Raised in: Codeunit<Release Incoming Document>.<OnRun>
    Code lines:
    ..."Released Date-Time" := CURRENTDATETIME;"Released By User ID" := USERSECURITYID;
    MODIFY(TRUE);
    OnAfterReleaseIncomingDoc(Rec); <=====================
Codeunit<Send Incoming Document to OCR>.PROCEDURE<OnAfterIncomingDocReadyForOCR>
  Raised in: Codeunit<Send Incoming Document to OCR>.PROCEDURE<SendToJobQueue>
    Code lines:
    ...
    IncomingDocument."OCR Status" := IncomingDocument."OCR Status"::Ready;
    IncomingDocument.MODIFY;
    CODEUNIT.RUN(CODEUNIT::"Release Incoming Document",IncomingDocument);
    ShowMessage(DocumentHasBeenScheduledTxt);
    OnAfterIncomingDocReadyForOCR(IncomingDocument); <=====================
Codeunit<Send Incoming Document to OCR>.PROCEDURE<OnAfterIncomingDocReceivedFromOCR>
  Raised in: Codeunit<Send Incoming Document to OCR>.PROCEDURE<SetStatusToReceived>
    Code lines:
    ..."OCR Status" := "OCR Status"::Success;"OCR Process Finished" := TRUE;
      MODIFY;
      COMMIT;
      OnAfterIncomingDocReceivedFromOCR(IncomingDocument); <=====================
    END;
  Raised in: Codeunit<Send Incoming Document to OCR>.PROCEDURE<SetStatusToFailed>
    Code lines:
    ..."OCR Status" := "OCR Status"::Error;"OCR Process Finished" := TRUE;
      MODIFY;
      COMMIT;
      OnAfterIncomingDocReceivedFromOCR(IncomingDocument); <=====================
    END;
Codeunit<Send Incoming Document to OCR>.PROCEDURE<OnAfterIncomingDocSentToOCR>
  Raised in: Codeunit<Send Incoming Document to OCR>.PROCEDURE<SendToOCR>
    Code lines:
    ...
      IncomingDocumentAttachment.SendToOCR;"OCR Status" := "OCR Status"::Sent;
      MODIFY;
      COMMIT;
      OnAfterIncomingDocSentToOCR(IncomingDocument); <=====================
    END;
Codeunit<Doc. Exch. Service Mgt.>.PROCEDURE<OnAfterIncomingDocReceivedFromDocExch>
  Raised in: Codeunit<Doc. Exch. Service Mgt.>.PROCEDURE<ProcessReceivedDocs>
    Code lines:
    ...
        LogActivityFailed(ContextRecordID,MarkBusinessProcessedTxt,'');
      END;
      COMMIT;
      IncomingDocument.FIND;
      OnAfterIncomingDocReceivedFromDocExch(IncomingDocument); <=====================
    END;
Codeunit<Workflow Setup>.PROCEDURE<OnAddWorkflowCategoriesToLibrary>
  Raised in: Codeunit<Workflow Setup>.PROCEDURE<InsertWorkflowCategories>
    Code lines:
    ...
    InsertWorkflowCategory(PurchDocCategoryTxt,PurchDocCategoryDescTxt);
    InsertWorkflowCategory(SalesDocCategoryTxt,SalesDocCategoryDescTxt);
    InsertWorkflowCategory(AdminCategoryTxt,AdminCategoryDescTxt);
    InsertWorkflowCategory(FinCategoryTxt,FinCategoryDescTxt);
    OnAddWorkflowCategoriesToLibrary; <=====================
Codeunit<Workflow Event Handling>.PROCEDURE<OnAddWorkflowEventPredecessorsToLibrary>
  Raised in: Codeunit<Workflow Event Handling>.PROCEDURE<AddEventPredecessors>
    Code lines:
    ...
        AddEventPredecessor(RunWorkflowOnGeneralJournalBatchBalancedCode,RunWorkflowOnSendGeneralJournalBatchForApprovalCode);
      RunWorkflowOnGeneralJournalBatchNotBalancedCode:
        AddEventPredecessor(RunWorkflowOnGeneralJournalBatchNotBalancedCode,RunWorkflowOnSendGeneralJournalBatchForApprovalCode);
    END;
    OnAddWorkflowEventPredecessorsToLibrary(EventFunctionName); <=====================
Codeunit<Workflow Event Handling>.PROCEDURE<OnAddWorkflowEventsToLibrary>
  Raised in: Codeunit<Workflow Event Handling>.PROCEDURE<CreateEventsLibrary>
    Code lines:
    ...
    AddEventToLibrary(RunWorkflowOnCustomerChangedCode,DATABASE::Customer,CustChangedTxt,0,TRUE);
    AddEventToLibrary(RunWorkflowOnVendorChangedCode,DATABASE::Vendor,VendChangedTxt,0,TRUE);
    AddEventToLibrary(RunWorkflowOnItemChangedCode,DATABASE::Item,ItemChangedTxt,0,TRUE);
    OnAddWorkflowEventsToLibrary; <=====================
    OnAddWorkflowTableRelationsToLibrary;
Codeunit<Workflow Event Handling>.PROCEDURE<OnAddWorkflowTableRelationsToLibrary>
  Raised in: Codeunit<Workflow Event Handling>.PROCEDURE<CreateEventsLibrary>
    Code lines:
    ...
    AddEventToLibrary(RunWorkflowOnCustomerChangedCode,DATABASE::Customer,CustChangedTxt,0,TRUE);
    AddEventToLibrary(RunWorkflowOnVendorChangedCode,DATABASE::Vendor,VendChangedTxt,0,TRUE);
    AddEventToLibrary(RunWorkflowOnItemChangedCode,DATABASE::Item,ItemChangedTxt,0,TRUE);
    OnAddWorkflowEventsToLibrary;
    OnAddWorkflowTableRelationsToLibrary; <=====================
Codeunit<Workflow Response Handling>.PROCEDURE<OnAddWorkflowResponsePredecessorsToLibrary>
  Raised in: Codeunit<Workflow Response Handling>.PROCEDURE<AddResponsePredecessors>
    Code lines:
    ...
      GetSendToOCRCode:
        AddResponsePredecessor(GetSendToOCRCode,WorkflowEventHandling.RunWorkflowOnAfterReadyForOCRIncomingDocCode);
      GetSendToOCRAsyncCode:
        AddResponsePredecessor(GetSendToOCRAsyncCode,WorkflowEventHandling.RunWorkflowOnAfterReadyForOCRIncomingDocCode);
    END;
    OnAddWorkflowResponsePredecessorsToLibrary(ResponseFunctionName); <=====================
Codeunit<Workflow Response Handling>.PROCEDURE<OnAddWorkflowResponsesToLibrary>
  Raised in: Codeunit<Workflow Response Handling>.PROCEDURE<CreateResponsesLibrary>
    Code lines:
    ...
    AddResponseToLibrary(RevertValueForFieldCode,0,RevertRecordValueTxt,'GROUP 6');
    AddResponseToLibrary(ApplyNewValuesCode,0,ApplyNewValuesTxt,'GROUP 7');
    AddResponseToLibrary(DiscardNewValuesCode,0,DiscardNewValuesTxt,'GROUP 0');
    OnAddWorkflowResponsesToLibrary; <=====================
Codeunit<Workflow Response Handling>.PROCEDURE<OnExecuteWorkflowResponse>
  Raised in: Codeunit<Workflow Response Handling>.PROCEDURE<ExecuteResponse>
    Code lines:
    ...
        ApplyNewValuesCode:
          WorkflowChangeRecMgt.ApplyNewValues(Variant,ResponseWorkflowStepInstance);
        DiscardNewValuesCode:
          WorkflowChangeRecMgt.DiscardNewValues(Variant,ResponseWorkflowStepInstance);
        ELSE BEGIN
          OnExecuteWorkflowResponse(ResponseExecuted,Variant,xVariant,ResponseWorkflowStepInstance); <=====================
          IF NOT ResponseExecuted THEN
            ERROR(NotSupportedResponseErr,WorkflowResponse."Function Name");
        END;
      END;
Codeunit<Approvals Mgmt.>.PROCEDURE<OnApproveApprovalRequest>
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<ApproveSelectedApprovalRequest>
    Code lines:
    IF ApprovalEntry.Status <> ApprovalEntry.Status::Open THEN
      ERROR(ApproveOnlyOpenRequestsErr);
    ApprovalEntry.VALIDATE(Status,ApprovalEntry.Status::Approved);
    ApprovalEntry.MODIFY(TRUE);
    OnApproveApprovalRequest(ApprovalEntry); <=====================
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<SendApprovalRequestFromRecord>
    Code lines:
    ...
      EXIT;
    END;
    ApprovalEntry.SETRANGE(Status,ApprovalEntry.Status::Approved);
    IF ApprovalEntry.FINDLAST THEN
      OnApproveApprovalRequest(ApprovalEntry) <=====================
    ELSE
      ERROR(NoApprovalRequestsFoundErr);
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelCustomerApprovalRequest>
  Raised in: Page<Customer List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelCustomerApprovalRequest(Rec); <=====================
  Raised in: Page<Customer Card>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelCustomerApprovalRequest(Rec); <=====================
  Raised in: Table<Customer>.<OnDelete>
    Code lines:
    ApprovalsMgmt.OnCancelCustomerApprovalRequest(Rec); <=====================
    IF DOPaymentCreditCard.FINDFIRST THEN
      DOPaymentCreditCard.DeleteByCustomer(Rec);
    ServiceItem.SETRANGE("Customer No.","No.");
    IF ServiceItem.FINDFIRST THEN
      IF CONFIRM(
           Text008,
           FALSE,
    ...
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelGeneralJournalBatchApprovalRequest>
  Raised in: Table<Gen. Journal Batch>.<OnDelete>
    Code lines:
    ApprovalsMgmt.OnCancelGeneralJournalBatchApprovalRequest(Rec); <=====================
    GenJnlAlloc.SETRANGE("Journal Template Name","Journal Template Name");
    GenJnlAlloc.SETRANGE("Journal Batch Name",Name);
    GenJnlAlloc.DELETEALL;
    GenJnlLine.SETRANGE("Journal Template Name","Journal Template Name");
    GenJnlLine.SETRANGE("Journal Batch Name",Name);
    GenJnlLine.DELETEALL(TRUE);
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<TryCancelJournalBatchApprovalRequest>
    Code lines:
    GetGeneralJournalBatch(GenJournalBatch,GenJournalLine);
    OnCancelGeneralJournalBatchApprovalRequest(GenJournalBatch); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelGeneralJournalLineApprovalRequest>
  Raised in: Table<Gen. Journal Line>.<OnDelete>
    Code lines:
    ApprovalsMgmt.OnCancelGeneralJournalLineApprovalRequest(Rec); <=====================
    CheckNoCardTransactEntryExist(Rec);
    TESTFIELD("Check Printed",FALSE);
    ClearCustVendApplnEntry;
    ClearAppliedGenJnlLine;
    DeletePaymentFileErrors;
    ClearDataExchangeEntries(FALSE);
    ...
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<TryCancelJournalLineApprovalRequests>
    Code lines:
    REPEAT
      IF HasOpenApprovalEntries(GenJournalLine.RECORDID) THEN
        OnCancelGeneralJournalLineApprovalRequest(GenJournalLine); <=====================
    UNTIL GenJournalLine.NEXT = 0;
    MESSAGE(ApprovalReqCanceledForSelectedLinesMsg);
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelIncomingDocApprovalRequest>
  Raised in: Page<Incoming Documents>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelIncomingDocApprovalRequest(Rec); <=====================
  Raised in: Page<Incoming Document>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelIncomingDocApprovalRequest(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelItemApprovalRequest>
  Raised in: Page<Item List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelItemApprovalRequest(Rec); <=====================
  Raised in: Page<Item Card>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelItemApprovalRequest(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelPurchaseApprovalRequest>
  Raised in: Page<Blanket Purchase Order>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Credit Memo>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Invoice>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Order>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Quote>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Return Order List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Blanket Purchase Orders>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Credit Memos>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Invoices>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Order List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Quotes>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
  Raised in: Page<Purchase Return Order>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelPurchaseApprovalRequest(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelSalesApprovalRequest>
  Raised in: Page<Blanket Sales Order>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Credit Memo>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Invoice>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Order>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Quote>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Order List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Return Order List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Blanket Sales Orders>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Credit Memos>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Invoice List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Quotes>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
  Raised in: Page<Sales Return Order>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelSalesApprovalRequest(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnCancelVendorApprovalRequest>
  Raised in: Page<Vendor Card>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelVendorApprovalRequest(Rec); <=====================
  Raised in: Page<Vendor List>.Action<CancelApprovalRequest>.<OnAction>
    Code lines:
    ApprovalsMgmt.OnCancelVendorApprovalRequest(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnDelegateApprovalRequest>
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<SubstituteUserIdForApprovalEntry>
    Code lines:
    ...
    ELSE
      UserSetup.GET(UserSetup.Substitute);
    ApprovalEntry."Approver ID" := UserSetup."User ID";
    ApprovalEntry.MODIFY(TRUE);
    OnDelegateApprovalRequest(ApprovalEntry); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnRejectApprovalRequest>
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<RejectSelectedApprovalRequest>
    Code lines:
    IF ApprovalEntry.Status <> ApprovalEntry.Status::Open THEN
      ERROR(RejectOnlyOpenRequestsErr);
    ApprovalEntry.VALIDATE(Status,ApprovalEntry.Status::Rejected);
    ApprovalEntry.MODIFY(TRUE);
    OnRejectApprovalRequest(ApprovalEntry); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendCustomerForApproval>
  Raised in: Page<Customer List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckCustomerApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendCustomerForApproval(Rec); <=====================
  Raised in: Page<Customer Card>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckCustomerApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendCustomerForApproval(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendGeneralJournalBatchForApproval>
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<TrySendJournalBatchApprovalRequest>
    Code lines:
    ...
    CheckGeneralJournalBatchApprovalsWorkflowEnabled(GenJournalBatch);
    IF HasOpenApprovalEntries(GenJournalBatch.RECORDID) OR
       HasAnyOpenJournalLineApprovalEntries(GenJournalBatch."Journal Template Name",GenJournalBatch.Name)
    THEN
      ERROR(PendingJournalBatchApprovalExistsErr);
    OnSendGeneralJournalBatchForApproval(GenJournalBatch); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendGeneralJournalLineForApproval>
  Raised in: Codeunit<Approvals Mgmt.>.PROCEDURE<TrySendJournalLineApprovalRequests>
    Code lines:
    IF GenJournalLine.COUNT = 1 THEN
      CheckGeneralJournalLineApprovalsWorkflowEnabled(GenJournalLine);
    REPEAT
      IF WorkflowManagement.CanExecuteWorkflow(GenJournalLine,
           WorkflowEventHandling.RunWorkflowOnSendGeneralJournalLineForApprovalCode) AND <=====================
         NOT HasOpenApprovalEntries(GenJournalLine.RECORDID)
      THEN BEGIN
        OnSendGeneralJournalLineForApproval(GenJournalLine); <=====================
        LinesSent += 1;
    ...
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendIncomingDocForApproval>
  Raised in: Page<Incoming Documents>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckIncomingDocApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendIncomingDocForApproval(Rec); <=====================
  Raised in: Page<Incoming Document>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckIncomingDocApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendIncomingDocForApproval(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendItemForApproval>
  Raised in: Page<Item List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckItemApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendItemForApproval(Rec); <=====================
  Raised in: Page<Item Card>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckItemApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendItemForApproval(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendPurchaseDocForApproval>
  Raised in: Page<Blanket Purchase Order>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Credit Memo>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Invoice>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Order>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Quote>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF  ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Return Order List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Blanket Purchase Orders>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Credit Memos>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Invoices>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Order List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Quotes>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
  Raised in: Page<Purchase Return Order>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckPurchaseApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendPurchaseDocForApproval(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendSalesDocForApproval>
  Raised in: Page<Blanket Sales Order>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Credit Memo>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Invoice>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Order>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Quote>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Order List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Return Order List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Blanket Sales Orders>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Credit Memos>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Invoice List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Quotes>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
  Raised in: Page<Sales Return Order>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckSalesApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendSalesDocForApproval(Rec); <=====================
Codeunit<Approvals Mgmt.>.PROCEDURE<OnSendVendorForApproval>
  Raised in: Page<Vendor Card>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckVendorApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendVendorForApproval(Rec); <=====================
  Raised in: Page<Vendor List>.Action<SendApprovalRequest>.<OnAction>
    Code lines:
    IF ApprovalsMgmt.CheckVendorApprovalsWorkflowEnabled(Rec) THEN
      ApprovalsMgmt.OnSendVendorForApproval(Rec); <=====================
Codeunit<Item Jnl.-Check Line>.PROCEDURE<OnAfterCheckItemJnlLine>
  Raised in: Codeunit<Item Jnl.-Check Line>.PROCEDURE<RunCheck>
    Code lines:
    ...
          TESTFIELD("Applies-to Entry");
      CheckDimensions(ItemJnlLine);
    END;
    OnAfterCheckItemJnlLine(ItemJnlLine); <=====================
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInitItemLedgEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InitItemLedgEntry>
    Code lines:
    ...
      END;
      IF (ItemLedgEntry.Quantity < 0) AND ("Entry Type" <> "Entry Type"::Transfer) THEN
        ItemLedgEntry."Shipped Qty. Not Returned" := ItemLedgEntry.Quantity;
    END;
    OnAfterInitItemLedgEntry(ItemLedgEntry,ItemJnlLine); <=====================
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertCorrItemLedgEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InitCorrItemLedgEntry>
    Code lines:
    ...
    OnBeforeInsertCorrItemLedgEntry(NewItemLedgEntry,OldItemLedgEntry,ItemJnlLine);
    NewItemLedgEntry.INSERT;
    OnAfterInsertCorrItemLedgEntry(NewItemLedgEntry,ItemJnlLine); <=====================
    IF NewItemLedgEntry."Item Tracking" <> NewItemLedgEntry."Item Tracking"::None THEN
      ItemTrackingMgt.ExistingExpirationDate(
        NewItemLedgEntry."Item No.",
    ...
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertCorrValueEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InsertCorrValueEntry>
    Code lines:
    ...
    IF NewValueEntry.Inventoriable THEN
      PostInventoryToGL(NewValueEntry);
    NewValueEntry.INSERT;
    OnAfterInsertCorrValueEntry(NewValueEntry,ItemJnlLine); <=====================
    ItemApplnEntry.SetOutboundsNotUpdated(ItemLedgEntry);
    UpdateAdjmtProp(NewValueEntry,ItemLedgEntry."Posting Date");
    ...
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertItemLedgEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InsertItemLedgEntry>
    Code lines:
    ...
      ItemLedgEntry.UpdateItemTracking;
      ItemLedgEntry.INSERT(TRUE);
      OnAfterInsertItemLedgEntry(ItemLedgEntry,ItemJnlLine); <=====================
      InsertItemReg(ItemLedgEntry."Entry No.",0,0,0);
    END;
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterInsertValueEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InsertValueEntry>
    Code lines:
    ...
      IF ValueEntry.Inventoriable THEN
        PostInventoryToGL(ValueEntry);
      ValueEntry.INSERT;
      OnAfterInsertValueEntry(ValueEntry,ItemJnlLine); <=====================
      ItemApplnEntry.SetOutboundsNotUpdated(ItemLedgEntry);
      UpdateAdjmtProp(ValueEntry,ItemLedgEntry."Posting Date");
    ...
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnAfterPostItemJnlLine>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<Code>
    Code lines:
    ..."Item Shpt. Entry No." := CapLedgEntryNo
      ELSE"Item Shpt. Entry No." := GlobalItemLedgEntry."Entry No.";
    END;
    OnAfterPostItemJnlLine(ItemJnlLine); <=====================
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertCorrItemLedgEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InitCorrItemLedgEntry>
    Code lines:
    ...
    IF OldItemLedgEntry.Positive THEN
      NewItemLedgEntry."Applies-to Entry" := OldItemLedgEntry."Entry No."
    ELSE
      NewItemLedgEntry."Applies-to Entry" := 0;
    OnBeforeInsertCorrItemLedgEntry(NewItemLedgEntry,OldItemLedgEntry,ItemJnlLine); <=====================
    NewItemLedgEntry.INSERT;
    OnAfterInsertCorrItemLedgEntry(NewItemLedgEntry,ItemJnlLine);
    ...
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertCorrValueEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InsertCorrValueEntry>
    Code lines:
    ...
    NewValueEntry."Cost Posted to G/L" := 0;
    NewValueEntry."Cost Posted to G/L (ACY)" := 0;
    NewValueEntry."Expected Cost Posted to G/L" := 0;
    NewValueEntry."Exp. Cost Posted to G/L (ACY)" := 0;
    OnBeforeInsertCorrValueEntry(NewValueEntry,OldValueEntry,ItemJnlLine); <=====================
    IF NewValueEntry.Inventoriable THEN
      PostInventoryToGL(NewValueEntry);
    ...
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertTransferEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InsertTransferEntry>
    Code lines:
    ...
          NewItemLedgEntry."Remaining Quantity",TRUE);
      ApplyItemLedgEntry(NewItemLedgEntry,ItemLedgEntry2,NewValueEntry,TRUE);
      AutoTrack(NewItemLedgEntry);
      OnBeforeInsertTransferEntry(NewItemLedgEntry,OldItemLedgEntry,ItemJnlLine); <=====================
      InsertItemLedgEntry(NewItemLedgEntry,TRUE);
      InsertValueEntry(NewValueEntry,NewItemLedgEntry,TRUE);
    ...
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforeInsertValueEntry>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<InsertValueEntry>
    Code lines:
    ...
          ValueEntry."Sales Amount (Expected)",
          ValueEntry."Purchase Amount (Expected)",
          ItemLedgEntry.Quantity = ItemLedgEntry."Invoiced Quantity");
      END;
      OnBeforeInsertValueEntry(ValueEntry,ItemJnlLine); <=====================
      IF ValueEntry.Inventoriable THEN
        PostInventoryToGL(ValueEntry);
    ...
Codeunit<Item Jnl.-Post Line>.PROCEDURE<OnBeforePostItemJnlLine>
  Raised in: Codeunit<Item Jnl.-Post Line>.PROCEDURE<Code>
    Code lines:
    OnBeforePostItemJnlLine(ItemJnlLine); <=====================
    WITH ItemJnlLine DO BEGIN
      IF EmptyLine AND NOT Correction AND NOT Adjustment THEN
        IF NOT IsValueEntryForDeletedItem THEN
          EXIT;
      ItemJnlCheckLine.SetCalledFromInvtPutawayPick(CalledFromInvtPutawayPick);
      ItemJnlCheckLine.SetCalledFromAdjustment(CalledFromAdjustment);
    ...
Codeunit<Release Sales Document>.PROCEDURE<OnAfterReleaseSalesDoc>
  Raised in: Codeunit<Release Sales Document>.<OnRun>
    Code lines:
    ...
    IF NotOnlyDropShipment THEN
      IF "Document Type" IN ["Document Type"::Order,"Document Type"::"Return Order"] THEN
        WhseSalesRelease.Release(Rec);
    OnAfterReleaseSalesDoc(Rec); <=====================
Codeunit<Release Sales Document>.PROCEDURE<OnAfterReopenSalesDoc>
  Raised in: Codeunit<Release Sales Document>.PROCEDURE<Reopen>
    Code lines:
    ...
      MODIFY(TRUE);
      IF "Document Type" IN ["Document Type"::Order,"Document Type"::"Return Order"] THEN
        WhseSalesRelease.Reopen(SalesHeader);
    END;
    OnAfterReopenSalesDoc(SalesHeader); <=====================
Codeunit<Release Sales Document>.PROCEDURE<OnBeforeReleaseSalesDoc>
  Raised in: Codeunit<Release Sales Document>.<OnRun>
    Code lines:
    IF Status = Status::Released THEN
      EXIT;
    OnBeforeReleaseSalesDoc(Rec); <=====================
    OnCheckSalesReleaseRestrictions;
    IF "Document Type" = "Document Type"::Quote THEN
      IF CheckCustomerCreated(TRUE) THEN
        GET("Document Type"::Quote,"No.")
      ELSE
    ...
Codeunit<Release Sales Document>.PROCEDURE<OnBeforeReopenSalesDoc>
  Raised in: Codeunit<Release Sales Document>.PROCEDURE<Reopen>
    Code lines:
    OnBeforeReopenSalesDoc(SalesHeader); <=====================
    WITH SalesHeader DO BEGIN
      IF Status = Status::Open THEN
        EXIT;
      Status := Status::Open;
      IF "Document Type" <> "Document Type"::Order THEN
        ReopenATOs(SalesHeader);
    ...
Codeunit<Release Purchase Document>.PROCEDURE<OnAfterReleasePurchaseDoc>
  Raised in: Codeunit<Release Purchase Document>.<OnRun>
    Code lines:
    ...
    IF NotOnlyDropShipment THEN
      IF "Document Type" IN ["Document Type"::Order,"Document Type"::"Return Order"] THEN
        WhsePurchRelease.Release(Rec);
    OnAfterReleasePurchaseDoc(Rec); <=====================
Codeunit<Release Purchase Document>.PROCEDURE<OnAfterReopenPurchaseDoc>
  Raised in: Codeunit<Release Purchase Document>.PROCEDURE<Reopen>
    Code lines:
    ...
      Status := Status::Open;
      MODIFY(TRUE);
    END;
    OnAfterReopenPurchaseDoc(PurchHeader); <=====================
Codeunit<Release Purchase Document>.PROCEDURE<OnBeforeReleasePurchaseDoc>
  Raised in: Codeunit<Release Purchase Document>.<OnRun>
    Code lines:
    IF Status = Status::Released THEN
      EXIT;
    OnBeforeReleasePurchaseDoc(Rec); <=====================
    OnCheckPurchaseReleaseRestrictions;
    TESTFIELD("Buy-from Vendor No.");
    PurchLine.SETRANGE("Document Type","Document Type");
    PurchLine.SETRANGE("Document No.","No.");
    ...
Codeunit<Release Purchase Document>.PROCEDURE<OnBeforeReopenPurchaseDoc>
  Raised in: Codeunit<Release Purchase Document>.PROCEDURE<Reopen>
    Code lines:
    OnBeforeReopenPurchaseDoc(PurchHeader); <=====================
    WITH PurchHeader DO BEGIN
      IF Status = Status::Open THEN
        EXIT;
      IF "Document Type" IN ["Document Type"::Order,"Document Type"::"Return Order"] THEN
        WhsePurchRelease.Reopen(PurchHeader);
      Status := Status::Open;
      MODIFY(TRUE);
    ...
Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterApplyRecordTemplate>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<ApplyConfigTemplate>
    Code lines:
    ...
    IF NOT ConfigTemplateHeader.GET(ConfigTemplateCode) THEN
      ERROR(ConfigurationTemplateNotFoundErr,ConfigTemplateHeader.TABLECAPTION,ConfigTemplateCode);
    ConfigTemplateManagement.UpdateRecord(ConfigTemplateHeader,DestinationRecordRef);
    IF NOT OnAfterApplyRecordTemplate(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef) THEN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterInsertRecord>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<InsertRecord>
    Code lines:
    ...
    IF SynchAction <> SynchActionType::Insert THEN
      EXIT;
    UpdateIntegrationRecordCoupling(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef);
    IF NOT OnAfterInsertRecord(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef) THEN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
    COMMIT;
Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterModifyRecord>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<ModifyRecord>
    Code lines:
    ...
      EXIT;
    END;
    UpdateIntegrationRecordCoupling(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef);
    IF NOT OnAfterModifyRecord(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef) THEN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
    COMMIT;
Codeunit<Integration Table Synch.>.PROCEDURE<OnAfterTransferRecordFields>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<SynchRecord>
    Code lines:
    ...
    IF SourceWasChanged OR (SynchAction = SynchActionType::ForceModify) THEN
      TransferFields(IntegrationRecordSynch,SourceRecordRef,DestinationRecordRef,SynchAction);
    AdditionalFieldsModified := FALSE;
    IF NOT OnAfterTransferRecordFields(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef,AdditionalFieldsModified) THEN BEGIN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
      SynchAction := SynchActionType::Fail;
      EXIT;
    END;
    ...
Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeApplyRecordTemplate>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<ApplyConfigTemplate>
    Code lines:
    IF DestinationRecordRef.NUMBER = IntegrationTableMapping."Integration Table ID" THEN
      ConfigTemplateCode := IntegrationTableMapping."Int. Tbl. Config Template Code"
    ELSE
      ConfigTemplateCode := IntegrationTableMapping."Table Config Template Code";
    IF NOT OnBeforeApplyRecordTemplate(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef,ConfigTemplateCode) THEN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
    IF ConfigTemplateCode = '' THEN
      EXIT;
    ...
Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeInsertRecord>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<InsertRecord>
    Code lines:
    // Callbak to as if we should cancel insert
    IF NOT OnBeforeInsertRecord(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef) THEN BEGIN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
      SynchAction := SynchActionType::Fail;
      COMMIT;
      EXIT;
    END;
    IF SynchAction <> SynchActionType::Insert THEN
      EXIT;
    ...
Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeModifyRecord>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<ModifyRecord>
    Code lines:
    IF NOT OnBeforeModifyRecord(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef) THEN BEGIN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
      SynchAction := SynchActionType::Fail;
      COMMIT;
      EXIT;
    END;
    IF NOT TryModify(DestinationRecordRef) THEN BEGIN
      SynchAction := SynchActionType::Fail;
      LogSynchError(
    ...
Codeunit<Integration Table Synch.>.PROCEDURE<OnBeforeTransferRecordFields>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<SynchRecord>
    Code lines:
    ...
            STRSUBSTNO(DestinationRecordIsNewerThanSourceErr,SourceRecordRef.CAPTION,DestinationRecordRef.CAPTION));
        EXIT;
      END
    END;
    IF NOT OnBeforeTransferRecordFields(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef) THEN BEGIN <=====================
      LogSynchError(SourceRecordRef,DestinationRecordRef,GETLASTERRORTEXT);
      SynchAction := SynchActionType::Fail
    END;
    ...
Codeunit<Integration Table Synch.>.PROCEDURE<OnFindUncoupledDestinationRecord>
  Raised in: Codeunit<Integration Table Synch.>.PROCEDURE<FindAndCoupleDestinationRecord>
    Code lines:
    OnFindUncoupledDestinationRecord( <=====================
      IntegrationTableMapping,SourceRecordRef,DestinationRecordRef,DestinationIsDeleted,DestinationFound);
    IF DestinationFound THEN
      UpdateIntegrationRecordCoupling(IntegrationTableMapping,SourceRecordRef,DestinationRecordRef);
Codeunit<CRM Integration Table Synch.>.PROCEDURE<OnQueryPostFilterIgnoreRecord>
  Raised in: Codeunit<CRM Integration Table Synch.>.PROCEDURE<PerformScheduledSynchToIntegrationTable>
    Code lines:
    ...
            // Verify record is in the source lookup filtered view
            Found := FALSE;
            IF SourceLookupRecordRef.FINDSET THEN
              REPEAT
                IF SourceLookupRecordRef.RECORDID = SourceRecordRef.RECORDID THEN BEGIN
                  OnQueryPostFilterIgnoreRecord(IntegrationTableMapping,SourceRecordRef,IgnoreRecord); <=====================
                  IF NOT IgnoreRecord THEN
                    Found := TRUE;
                END;
              UNTIL (Found OR IgnoreRecord OR (SourceLookupRecordRef.NEXT = 0))
    ...
  Raised in: Codeunit<CRM Integration Table Synch.>.PROCEDURE<PerformScheduledSynchFromIntegrationTable>
    Code lines:
    ...
    LatestModifiedOn := 0DT;
    IF SourceRecordRef.FINDSET THEN
      REPEAT
        DoIgnoreRecord := FALSE;
        OnQueryPostFilterIgnoreRecord(IntegrationTableMapping,SourceRecordRef,DoIgnoreRecord); <=====================
        IF NOT DoIgnoreRecord THEN BEGIN
          SourceFieldRef := SourceRecordRef.FIELD(IntegrationTableMapping."Integration Table UID Fld. No.");
          TempCRMIntegrationRecord.RESET;
          TempCRMIntegrationRecord.SETRANGE("CRM ID",FORMAT(SourceFieldRef.VALUE));
    ...
Codeunit<Sales-Calc. Discount>.PROCEDURE<OnAfterCalcSalesDiscount>
  Raised in: Codeunit<Sales-Calc. Discount>.PROCEDURE<CalculateInvoiceDiscount>
    Code lines:
    ...
        SalesLine2.SetSalesHeader(SalesHeader);
        SalesLine2.UpdateVATOnLines(0,SalesHeader,SalesLine2,TempVATAmountLine);
      END;
    END;
    OnAfterCalcSalesDiscount(SalesHeader); <=====================
Codeunit<Sales-Calc. Discount>.PROCEDURE<OnBeforeCalcSalesDiscount>
  Raised in: Codeunit<Sales-Calc. Discount>.PROCEDURE<CalculateInvoiceDiscount>
    Code lines:
    SalesSetup.GET;
    OnBeforeCalcSalesDiscount(SalesHeader); <=====================
    WITH SalesLine DO BEGIN
      LOCKTABLE;
      SalesHeader.TESTFIELD("Customer Posting Group");
      CustPostingGr.GET(SalesHeader."Customer Posting Group");
      SalesLine2.RESET;
    ...
Codeunit<Purch.-Calc.Discount>.PROCEDURE<OnAfterCalcPurchaseDiscount>
  Raised in: Codeunit<Purch.-Calc.Discount>.PROCEDURE<CalculateInvoiceDiscount>
    Code lines:
    ...
        PurchLine2.UpdateVATOnLines(0,PurchHeader,PurchLine2,TempVATAmountLine);
      END;
    END;
    OnAfterCalcPurchaseDiscount(PurchHeader); <=====================
Codeunit<Purch.-Calc.Discount>.PROCEDURE<OnBeforeCalcPurchaseDiscount>
  Raised in: Codeunit<Purch.-Calc.Discount>.PROCEDURE<CalculateInvoiceDiscount>
    Code lines:
    PurchSetup.GET;
    OnBeforeCalcPurchaseDiscount(PurchHeader); <=====================
    WITH PurchLine DO BEGIN
      LOCKTABLE;
      PurchHeader.TESTFIELD("Vendor Posting Group");
      VendPostingGr.GET(PurchHeader."Vendor Posting Group");
      PurchLine2.RESET;
    ...
Codeunit<Sales-Post>.PROCEDURE<OnAfterPostSalesDoc>
  Raised in: Codeunit<Sales-Post>.<OnRun>
    Code lines:
    ...
        GenJnlLineDocType,GenJnlLineDocNo,GenJnlLineExtDocNo,SrcCode);
    CRMIntegrationManagement.AddPostedSalesDocumentToCRMAccountWall(SalesHeader);
    CRMIntegrationManagement.SetCRMSalesOrderStatusToInvoiced(SalesHeader);
    OnAfterPostSalesDoc(Rec,GenJnlPostLine,SalesShptHeader."No.",ReturnRcptHeader."No.",SalesInvHeader."No.", <=====================
      SalesCrMemoHeader."No.");
Codeunit<Sales-Post>.PROCEDURE<OnBeforePostCommitSalesDoc>
  Raised in: Codeunit<Sales-Post>.<OnRun>
    Code lines:
    ...
              END;
            UNTIL SalesLine.NEXT = 0;
          END;
        END;
      END;
      OnBeforePostCommitSalesDoc(Rec,GenJnlPostLine,PreviewMode,ModifyHeader); <=====================
      IF NOT PreviewMode AND ModifyHeader THEN BEGIN
        MODIFY;
        COMMIT;
      END;
    ...
Codeunit<Sales-Post>.PROCEDURE<OnBeforePostSalesDoc>
  Raised in: Codeunit<Sales-Post>.<OnRun>
    Code lines:
    OnBeforePostSalesDoc(Rec); <=====================
    IF PostingDateExists AND (ReplacePostingDate OR ("Posting Date" = 0D)) THEN BEGIN"Posting Date" := PostingDate;
      VALIDATE("Currency Code");
    END;
    IF PostingDateExists AND (ReplaceDocumentDate OR ("Document Date" = 0D)) THEN
      VALIDATE("Document Date",PostingDate);
    ...
Codeunit<Purch.-Post>.PROCEDURE<OnAfterPostPurchaseDoc>
  Raised in: Table<Purchase Header>.PROCEDURE<TriggerOnAfterPostPurchaseDoc>
    Code lines:
    PurchPost.OnAfterPostPurchaseDoc(Rec,GenJnlPostLine,PurchRcpHdrNo,RetShptHdrNo,PurchInvHdrNo,PurchCrMemoHdrNo); <=====================
  Raised in: Codeunit<Purch.-Post>.<OnRun>
    Code lines:
    ...
      COMMIT;
      UpdateAnalysisView.UpdateAll(0,TRUE);
      UpdateItemAnalysisView.UpdateAll(0,TRUE);
    END;
    OnAfterPostPurchaseDoc(Rec,GenJnlPostLine,PurchRcptHeader."No.",ReturnShptHeader."No.",PurchInvHeader."No.", <=====================
      PurchCrMemoHeader."No.");
Codeunit<Purch.-Post>.PROCEDURE<OnBeforePostCommitPurchaseDoc>
  Raised in: Codeunit<Purch.-Post>.<OnRun>
    Code lines:
    ...
                END;
              END;
            UNTIL PurchLine.NEXT = 0;
        END;
      END;
      OnBeforePostCommitPurchaseDoc(Rec,GenJnlPostLine,PreviewMode,ModifyHeader); <=====================
      IF NOT PreviewMode AND ModifyHeader THEN BEGIN
        MODIFY;
        COMMIT;
      END;
    ...
Codeunit<Purch.-Post>.PROCEDURE<OnBeforePostPurchaseDoc>
  Raised in: Codeunit<Purch.-Post>.<OnRun>
    Code lines:
    OnBeforePostPurchaseDoc(Rec); <=====================
    ValidatePostingAndDocumentDate(Rec);
    IF PreviewMode THEN BEGIN
      CLEARALL;
      PreviewMode := TRUE;
    END ELSE
      CLEARALL;
    ...
Report<Send Overdue Appr. Notif.>.PROCEDURE<OnSendOverdueNotifications>
  Raised in: Report<Send Overdue Appr. Notif.>.<OnPreReport>
    Code lines:
    IF NOT ApprovalsMgmt.IsOverdueNotificationsWorkflowEnabled THEN
      ERROR(NoWorkflowEnabledErr);
    OnSendOverdueNotifications; <=====================
Table<Incoming Document>.PROCEDURE<OnCheckIncomingDocCreateDocRestrictions>
  Raised in: Table<Incoming Document>.PROCEDURE<CreateWithDataExchange>
    Code lines:
    FIND;
    IF ApprovalsMgmt.IsIncomingDocApprovalsWorkflowEnabled(Rec) AND (Status = Status::New) THEN
      ERROR(DocWhenApprovalIsCompleteErr);
    OnCheckIncomingDocCreateDocRestrictions; <=====================
    IF "Data Exchange Type" = '' THEN
      ERROR(DataExchangeTypeEmptyErr);
    ...
Table<Incoming Document>.PROCEDURE<OnCheckIncomingDocReleaseRestrictions>
  Raised in: Codeunit<Release Incoming Document>.<OnRun>
    Code lines:
    IF Status = Status::Released THEN
      EXIT;
    IF Status IN [Status::Created,Status::Posted] THEN
      ERROR(STRSUBSTNO(CanReleasedIfStatusErr,Status::"Pending Approval",Status::New,Status::Failed));
    OnCheckIncomingDocReleaseRestrictions; <=====================
    TESTFIELD(Posted,FALSE);
    IncomingDocumentAttachment.SETRANGE("Incoming Document Entry No.","Entry No.");
    ...
Table<Incoming Document>.PROCEDURE<OnCheckIncomingDocSetForOCRRestrictions>
  Raised in: Codeunit<Send Incoming Document to OCR>.PROCEDURE<VerifySendToOCR>
    Code lines:
    ...
      IF "OCR Status" IN ["OCR Status"::Sent,"OCR Status"::Success,"OCR Status"::"Awaiting Verification"] THEN BEGIN
        ShowMessage(STRSUBSTNO(ErrorMessage,FORMAT("OCR Status")));
        EXIT(FALSE);
      END;
      OnCheckIncomingDocSetForOCRRestrictions; <=====================
      IF ApprovalsMgmt.IsIncomingDocApprovalsWorkflowEnabled(IncomingDocument) AND (Status = Status::New) THEN
        ERROR(OCRWhenApprovalIsCompleteErr);
    ...
Table<Incoming Document Attachment>.PROCEDURE<OnAttachBinaryFile>
  Raised in: Codeunit<Import Attachment - Inc. Doc.>.PROCEDURE<ImportAttachment>
    Code lines:
    ...
      END;
      INSERT(TRUE);
      IF Type IN [Type::Image,Type::PDF] THEN
        OnAttachBinaryFile; <=====================
    END;
    EXIT(TRUE);
Table<Service Connection>.PROCEDURE<OnRegisterServiceConnection>
  Raised in: Page<Service Connections>.<OnOpenPage>
    Code lines:
    OnRegisterServiceConnection(Rec); <=====================
  Raised in: Page<Service Connections>.PROCEDURE<CallSetup>
    Code lines:
    ...
      EXIT;
    RecordRef.GET("Record ID");
    RecordRefVariant := RecordRef;
    PAGE.RUNMODAL("Page ID",RecordRefVariant);
    DELETE;
    OnRegisterServiceConnection(Rec); <=====================
    IF GET(xRec."No.") THEN;
    CurrPage.UPDATE(FALSE);
  Raised in: Page<Service Connections Part>.<OnOpenPage>
    Code lines:
    OnRegisterServiceConnection(Rec); <=====================
  Raised in: Page<Service Connections Part>.PROCEDURE<CallSetup>
    Code lines:
    ...
      EXIT;
    RecordRef.GET("Record ID");
    RecordRefVariant := RecordRef;
    PAGE.RUNMODAL("Page ID",RecordRefVariant);
    DELETE;
    OnRegisterServiceConnection(Rec); <=====================
    IF GET(xRec."No.") THEN;
    CurrPage.UPDATE(FALSE);
Table<G/L Entry>.PROCEDURE<OnAfterCopyGLEntryFromGenJnlLine>
  Raised in: Table<G/L Entry>.PROCEDURE<CopyFromGenJnlLine>
    Code lines:
    ..."User ID" := USERID;"No. Series" := GenJnlLine."Posting No. Series";"IC Partner Code" := GenJnlLine."IC Partner Code";"Journal Template Name" := GenJnlLine."Journal Template Name";
    OnAfterCopyGLEntryFromGenJnlLine(Rec,GenJnlLine); <=====================
Table<Cust. Ledger Entry>.PROCEDURE<OnAfterCopyCustLedgerEntryFromGenJnlLine>
  Raised in: Table<Cust. Ledger Entry>.PROCEDURE<CopyFromGenJnlLine>
    Code lines:
    ..."Applies-to Ext. Doc. No." := GenJnlLine."Applies-to Ext. Doc. No.";"Payment Method Code" := GenJnlLine."Payment Method Code";"Exported to Payment File" := GenJnlLine."Exported to Payment File";"Journal Template Name" := GenJnlLine."Journal Template Name";
    OnAfterCopyCustLedgerEntryFromGenJnlLine(Rec,GenJnlLine); <=====================
Table<Gen. Journal Batch>.PROCEDURE<OnCheckGenJournalLineExportRestrictions>
  Raised in: Codeunit<Payment Export Gen. Jnl Check>.<OnRun>
    Code lines:
    DeletePaymentFileErrors;
    GenJnlBatch.GET("Journal Template Name","Journal Batch Name");
    GenJnlBatch.OnCheckGenJournalLineExportRestrictions; <=====================
    IF NOT GenJnlBatch."Allow Payment Export" THEN
      AddBatchEmptyError(Rec,GenJnlBatch.FIELDCAPTION("Allow Payment Export"),'');
    IF GenJnlBatch."Bal. Account Type" <> GenJnlBatch."Bal. Account Type"::"Bank Account" THEN
      AddBatchEmptyError(Rec,GenJnlBatch.FIELDCAPTION("Bal. Account Type"),GenJnlBatch."Bal. Account Type");
    ...
Table<Gen. Journal Batch>.PROCEDURE<OnGeneralJournalBatchBalanced>
  Raised in: Table<Gen. Journal Batch>.PROCEDURE<CheckBalance>
    Code lines:
    Balance := GetBalance;
    IF Balance = 0 THEN
      OnGeneralJournalBatchBalanced <=====================
    ELSE
      OnGeneralJournalBatchNotBalanced;
Table<Gen. Journal Batch>.PROCEDURE<OnGeneralJournalBatchNotBalanced>
  Raised in: Table<Gen. Journal Batch>.PROCEDURE<CheckBalance>
    Code lines:
    Balance := GetBalance;
    IF Balance = 0 THEN
      OnGeneralJournalBatchBalanced
    ELSE
      OnGeneralJournalBatchNotBalanced; <=====================
Table<Gen. Journal Batch>.PROCEDURE<OnMoveGenJournalBatch>
  Raised in: Codeunit<Gen. Jnl.-Post Batch>.PROCEDURE<Code>
    Code lines:
    ...
      CLEAR(GenJnlCheckLine);
      CLEAR(GenJnlPostLine);
      CLEARMARKS;
    END;
    UpdateAnalysisView.UpdateAll(0,TRUE);
    GenJnlBatch.OnMoveGenJournalBatch(GLReg.RECORDID); <=====================
    COMMIT;
Table<Vendor Ledger Entry>.PROCEDURE<OnAfterCopyVendLedgerEntryFromGenJnlLine>
  Raised in: Table<Vendor Ledger Entry>.PROCEDURE<CopyFromGenJnlLine>
    Code lines:
    ..."Payment Reference" := GenJnlLine."Payment Reference";"Payment Method Code" := GenJnlLine."Payment Method Code";"Exported to Payment File" := GenJnlLine."Exported to Payment File";"Journal Template Name" := GenJnlLine."Journal Template Name";
    OnAfterCopyVendLedgerEntryFromGenJnlLine(Rec,GenJnlLine); <=====================
Table<Sales Header>.PROCEDURE<OnCheckSalesPostRestrictions>
  Raised in: Codeunit<Sales-Post Prepayments>.PROCEDURE<Code>
    Code lines:
    ...
      IF NOT CheckOpenPrepaymentLines(SalesHeader,DocumentType) THEN
        ERROR(Text001);
      ValidatePaymentMethod(SalesHeader);
      CheckDim(SalesHeader);
      OnCheckSalesPostRestrictions; <=====================
      Cust.GET("Sell-to Customer No.");
      Cust.CheckBlockedCustOnDocs(Cust,PrepmtDocTypeToDocType("Document Type"),FALSE,TRUE);
      IF "Bill-to Customer No." <> "Sell-to Customer No." THEN BEGIN
        Cust.GET("Bill-to Customer No.");
    ...
  Raised in: Codeunit<Sales-Post>.PROCEDURE<CheckPostRestrictions>
    Code lines:
    SalesHeader.OnCheckSalesPostRestrictions; <=====================
    CheckCustBlockage(SalesHeader."Sell-to Customer No.",TRUE);
    IF SalesHeader."Bill-to Customer No." <> SalesHeader."Sell-to Customer No." THEN
      CheckCustBlockage(SalesHeader."Bill-to Customer No.",FALSE);
  Raised in: Codeunit<Sales-Quote to Order>.<OnRun>
    Code lines:
    TESTFIELD("Document Type","Document Type"::Quote);
    ShouldRedistributeInvoiceAmount := SalesCalcDiscountByType.ShouldRedistributeInvoiceDiscountAmount(Rec);
    OnCheckSalesPostRestrictions; <=====================
    Cust.GET("Sell-to Customer No.");
    Cust.CheckBlockedCustOnDocs(Cust,"Document Type"::Order,TRUE,FALSE);
    CALCFIELDS("Amount Including VAT");
    SalesOrderHeader := Rec;
    IF GUIALLOWED AND NOT HideValidationDialog THEN
    ...
Table<Sales Header>.PROCEDURE<OnCheckSalesReleaseRestrictions>
  Raised in: Codeunit<Release Sales Document>.<OnRun>
    Code lines:
    IF Status = Status::Released THEN
      EXIT;
    OnBeforeReleaseSalesDoc(Rec);
    OnCheckSalesReleaseRestrictions; <=====================
    IF "Document Type" = "Document Type"::Quote THEN
      IF CheckCustomerCreated(TRUE) THEN
        GET("Document Type"::Quote,"No.")
      ELSE
    ...
Table<Sales Header>.PROCEDURE<OnCustomerCreditLimitExceeded>
  Raised in: Table<Sales Header>.PROCEDURE<CheckAvailableCreditLimit>
    Code lines:
    ...
      Customer.GET("Sell-to Customer No.");
    AvailableCreditLimit := Customer.CalcAvailableCredit;
    IF AvailableCreditLimit < 0 THEN
      OnCustomerCreditLimitExceeded <=====================
    ELSE
      OnCustomerCreditLimitNotExceeded;
    EXIT(AvailableCreditLimit);
  Raised in: Codeunit<Cust-Check Cr. Limit>.PROCEDURE<SalesHeaderCheck>
    Code lines:
    ...
    ELSE BEGIN
      CreditLimitExceeded := TRUE;
      OK := CustCheckCreditLimit.RUNMODAL = ACTION::Yes;
      CLEAR(CustCheckCreditLimit);
      IF OK THEN
        SalesHeader.OnCustomerCreditLimitExceeded <=====================
      ELSE
        ERROR(Text000);
    END;
  Raised in: Codeunit<Cust-Check Cr. Limit>.PROCEDURE<SalesLineCheck>
    Code lines:
    ...
      SalesHeader.OnCustomerCreditLimitNotExceeded
    ELSE BEGIN
      OK := CustCheckCreditLimit.RUNMODAL = ACTION::Yes;
      CLEAR(CustCheckCreditLimit);
      IF OK THEN
        SalesHeader.OnCustomerCreditLimitExceeded <=====================
      ELSE
        ERROR(Text000);
    END;
Table<Sales Header>.PROCEDURE<OnCustomerCreditLimitNotExceeded>
  Raised in: Table<Sales Header>.PROCEDURE<CheckAvailableCreditLimit>
    Code lines:
    ...
    AvailableCreditLimit := Customer.CalcAvailableCredit;
    IF AvailableCreditLimit < 0 THEN
      OnCustomerCreditLimitExceeded
    ELSE
      OnCustomerCreditLimitNotExceeded; <=====================
    EXIT(AvailableCreditLimit);
  Raised in: Codeunit<Cust-Check Cr. Limit>.PROCEDURE<SalesHeaderCheck>
    Code lines:
    IF NOT GUIALLOWED THEN
      EXIT;
    IF NOT CustCheckCreditLimit.SalesHeaderShowWarning(SalesHeader) THEN
      SalesHeader.OnCustomerCreditLimitNotExceeded <=====================
    ELSE BEGIN
      CreditLimitExceeded := TRUE;
      OK := CustCheckCreditLimit.RUNMODAL = ACTION::Yes;
      CLEAR(CustCheckCreditLimit);
      IF OK THEN
    ...
  Raised in: Codeunit<Cust-Check Cr. Limit>.PROCEDURE<SalesLineCheck>
    Code lines:
    ...
      EXIT;
    SalesHeader.GET(SalesLine."Document Type",SalesLine."Document No.");
    IF NOT CustCheckCreditLimit.SalesLineShowWarning(SalesLine) THEN
      SalesHeader.OnCustomerCreditLimitNotExceeded <=====================
    ELSE BEGIN
      OK := CustCheckCreditLimit.RUNMODAL = ACTION::Yes;
      CLEAR(CustCheckCreditLimit);
      IF OK THEN
    ...
Table<Purchase Header>.PROCEDURE<OnCheckPurchasePostRestrictions>
  Raised in: Codeunit<Purchase-Post Prepayments>.PROCEDURE<Code>
    Code lines:
    ...
        FIELDERROR("Posting Date",Text000);
      IF NOT CheckOpenPrepaymentLines(PurchHeader,DocumentType) THEN
        ERROR(Text001);
      CheckDim(PurchHeader);
      OnCheckPurchasePostRestrictions; <=====================
      Vend.GET("Buy-from Vendor No.");
      Vend.CheckBlockedVendOnDocs(Vend,TRUE);
      IF "Pay-to Vendor No." <> "Buy-from Vendor No." THEN BEGIN
        Vend.GET("Pay-to Vendor No.");
    ...
  Raised in: Codeunit<Purch.-Quote to Order>.<OnRun>
    Code lines:
    TESTFIELD("Document Type","Document Type"::Quote);
    ShouldRedistributeInvoiceAmount := PurchCalcDiscByType.ShouldRedistributeInvoiceDiscountAmount(Rec);
    OnCheckPurchasePostRestrictions; <=====================
    Vend.GET("Buy-from Vendor No.");
    Vend.CheckBlockedVendOnDocs(Vend,FALSE);
    PurchOrderHeader := Rec;
    PurchOrderHeader."Document Type" := PurchOrderHeader."Document Type"::Order;
    ...
  Raised in: Codeunit<Purch.-Post>.PROCEDURE<CheckPostRestrictions>
    Code lines:
    PurchaseHeader.OnCheckPurchasePostRestrictions; <=====================
    Vendor.GET(PurchaseHeader."Buy-from Vendor No.");
    Vendor.CheckBlockedVendOnDocs(Vendor,TRUE);
    IF PurchaseHeader."Pay-to Vendor No." <> PurchaseHeader."Buy-from Vendor No." THEN BEGIN
      Vendor.GET(PurchaseHeader."Pay-to Vendor No.");
      Vendor.CheckBlockedVendOnDocs(Vendor,TRUE);
    END;
Table<Purchase Header>.PROCEDURE<OnCheckPurchaseReleaseRestrictions>
  Raised in: Codeunit<Release Purchase Document>.<OnRun>
    Code lines:
    IF Status = Status::Released THEN
      EXIT;
    OnBeforeReleasePurchaseDoc(Rec);
    OnCheckPurchaseReleaseRestrictions; <=====================
    TESTFIELD("Buy-from Vendor No.");
    PurchLine.SETRANGE("Document Type","Document Type");
    PurchLine.SETRANGE("Document No.","No.");
    ...
Table<Gen. Journal Line>.PROCEDURE<OnCheckGenJournalLinePostRestrictions>
  Raised in: Codeunit<Gen. Jnl.-Post Batch>.PROCEDURE<CheckRestrictions>
    Code lines:
    IF NOT PreviewMode THEN
      GenJournalLine.OnCheckGenJournalLinePostRestrictions; <=====================
Table<Gen. Journal Line>.PROCEDURE<OnCheckGenJournalLinePrintCheckRestrictions>
  Raised in: Codeunit<Document-Print>.PROCEDURE<PrintCheck>
    Code lines:
    GenJnlLine.COPY(NewGenJnlLine);
    GenJnlLine.OnCheckGenJournalLinePrintCheckRestrictions; <=====================
    ReportSelection.SETRANGE(Usage,ReportSelection.Usage::"B.Check");
    ReportSelection.SETFILTER("Report ID",'<>0');
    ReportSelection.FIND('-');
    REPEAT
      REPORT.RUNMODAL(ReportSelection."Report ID",TRUE,FALSE,GenJnlLine);
    UNTIL ReportSelection.NEXT = 0;
Table<Gen. Journal Line>.PROCEDURE<OnMoveGenJournalLine>
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<PostGLAcc>
    Code lines:
    ...
      InitVAT(GenJnlLine,GLEntry,VATPostingSetup);
      InsertGLEntry(GenJnlLine,GLEntry,TRUE);
      PostJob(GenJnlLine,GLEntry);
      PostVAT(GenJnlLine,GLEntry,VATPostingSetup);
      DeferralPosting("Deferral Code","Source Code","Account No.",GenJnlLine,Balancing);
      OnMoveGenJournalLine(GLEntry.RECORDID); <=====================
    END;
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<PostCust>
    Code lines:
    ...
        IF IsTempGLEntryBufEmpty THEN
          DtldCustLedgEntry.SetZeroTransNo(NextTransactionNo);
      UpdateDOPaymentTransactEntry(GenJnlLine,CustLedgEntry."Entry No.");
      DeferralPosting("Deferral Code","Source Code",ReceivablesAccount,GenJnlLine,Balancing);
      OnMoveGenJournalLine(CustLedgEntry.RECORDID); <=====================
    END;
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<PostVend>
    Code lines:
    ...
      IF DtldLedgEntryInserted THEN
        IF IsTempGLEntryBufEmpty THEN
          DtldVendLedgEntry.SetZeroTransNo(NextTransactionNo);
      DeferralPosting("Deferral Code","Source Code",PayablesAccount,GenJnlLine,Balancing);
      OnMoveGenJournalLine(VendLedgEntry.RECORDID); <=====================
    END;
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<PostBankAcc>
    Code lines:
    ...
      BankAccPostingGr.TESTFIELD("G/L Bank Account No.");
      CreateGLEntryBalAcc(
        GenJnlLine,BankAccPostingGr."G/L Bank Account No.","Amount (LCY)","Source Currency Amount","Bal. Account Type","Bal. Account No.");
      DeferralPosting("Deferral Code","Source Code",BankAccPostingGr."G/L Bank Account No.",GenJnlLine,Balancing);
      OnMoveGenJournalLine(BankAccLedgEntry.RECORDID); <=====================
    END;
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<PostFixedAsset>
    Code lines:
    ...
    GLEntry := GLEntry2;
    TempGLEntryBuf."Entry No." := GLEntry."Entry No."; // Used later in InsertVAT(): GLEntryVATEntryLink.InsertLink(TempGLEntryBuf."Entry No.",VATEntry."Entry No.")
    PostVAT(GenJnlLine,GLEntry,VATPostingSetup);
    FAJnlPostLine.UpdateRegNo(GLReg."No.");
    GenJnlLine.OnMoveGenJournalLine(GLEntry.RECORDID); <=====================
  Raised in: Codeunit<Gen. Jnl.-Post Line>.PROCEDURE<CreateGLEntryBalAcc>
    Code lines:
    InitGLEntry(GenJnlLine,GLEntry,AccNo,Amount,AmountAddCurr,TRUE,TRUE);
    GLEntry."Bal. Account Type" := BalAccType;
    GLEntry."Bal. Account No." := BalAccNo;
    InsertGLEntry(GenJnlLine,GLEntry,TRUE);
    GenJnlLine.OnMoveGenJournalLine(GLEntry.RECORDID); <=====================

 

 

Disclaimer(s)

This result was compiled by a tool that is still in alpha-stage.

This result is based on a BE database, not a W1. I did not have a W1 version and don’t expect any changes between the two versions regarding “Event Publishers”.

This output is based on Cumulative Update 7.

NAV2016 PartnerSource Marketing and Sales material

$
0
0

I admit .. this is somewhat late in the year to come with this information. But I was searching for marketing material on NAV2016, and came across a list of links to (mainly) PartnerSource that might be useful for you as well, so I decided to write up a quick blogpost.

As such, these are “official” resources for NAV 2016, regarding three topics:

Awareness

Obviously, there is a lot out there already regarding NAV 2016 awareness .. including (non-)MVP blogs, Microsoft blogs and such. I’m just referencing the official links I got from Microsoft, which is:

Consideration

This category, is leaning more and more to the “marketing-fluff” ;-). Here we have:

Decision

Regarding decision-making, there are these links that can help you:

That’s all .. enjoy!

Invoke-NAVSQL: Execute SQL queries on NAV databases with PowerShell

$
0
0

Just another CmdLet to bother you with (no really, it’s quite an interesting one – try to keep awake … ;-)).

The more you use PowerShell .. and the more you use it for NAV .. you just realize that you have to call out to SQL Server quite regularly. Just a few examples:

  • Backup and restore – Get the default restore directories
  • Update/select the UidOffSet
  • Unlocking all objects while upgrading
  • Granting service user db-owner access
  • … (and a lot more…)

Some time ago, I have been spending time in making the call to an NAV database a little bit simpler from PowerShell.

Goals

Simplicity is key here – I just want to provide a ServerInstance, and the function needs to figure out how to connect to the database. Next, if I had some kind of “select” statement, it needed to be simple to get to the data. As you know, PowerShell returns Objects .. so in case of this new function, columns needed to be properties, and records needed to be elements in my resulting object collection.

Invoke-NAVSQL

I decided to call the function “Invoke-NAVSQL” – according to a similar existing function part of the SQLPS module. You can find the function on my github.

A few major things that stand out in the script:

  • First of all, it is going to get all details from the Server Instance, which it needs to figure out on which database details it has to execute the SQL command. I do this with Get-NAVServerInstanceDetails (also part of my github module).
  • I use quite a default way (.Net) to connect to the database and execute the query to get back a dataset. In my understanding, this way of working with SQL Server is not dependent of any modules, but it just going to use the libraries in .Net – which makes the function somewhat more independent. But I could be wrong (comments always appreciated ;-)).
  • At the end, I’m converting the dataset to a collection of objects, where all fields are properties.

So, if the SQLCommand was a SELECT, this function returns you a clear and user friendly object model.

Some examples

To have a look at the UidOffSet property of our database that is attached to server instance “DynamicsNAV90”:

$dbproperties = Invoke-NAVSql -ServerInstance 'dynamicsNAV90' -SQLCommand 'Select * From [$ndo$dbproperty]'
$dbproperties.uidoffset

Get all companies, and get the customers of the first company:

$Mycompanies = Invoke-NAVSql -ServerInstance 'dynamicsNAV90' -SQLCommand 'Select * From Company'

$Customers = $Mycompanies |
                  select -First 1 |
                        foreach{ Invoke-NAVSQL -ServerInstance 'dynamicsNAV90' -SQLCommand "select * from [$($_.Name)$('$Customer')]" -ShowWriteHost }

$Customers | select 'No_', Name, 'Credit Limit (LCY)' | Format-Table -AutoSize

Assumptions

I know .. “Assumption is the mother of all fuckups“. But simplicity often comes with a number of assumptions, doesn’t it? ;-). My big assumption is the security-part. Namely, I assume the PowerShell user (Windows Authentication) has got access to the SQL Server, and obviously enough permissions to perform the query.

NAV Upgrade: Import and Compile crashes when NST is running

$
0
0

This is just a small blogpost to mention a few things you need to take into account when you’re working with objects on a large scale – I mean importing, exporting, upgrading, compiling, .. whatever.

My scenario

I was upgrading a customer from CU3 to CU8. Sure, you might wonder why it took me so long :-). Any reason is the wrong reason – I agree ;-). But anyway .. . I always use my scripts that are also available on my github here, which I have done exactly 37 times before – I counted them. So why change – this is a 10 minute job – let’s quickly do this. Basically, the script is following three major steps:

  • Merge the objects
  • Create a fob
    • Sandbox db
    • Import merged txt
    • Compile
    • Export fob
  • Perform upgrade on customer db

I noticed that the import-step took far too long.. . No errors, nothing. During (quite some) investigation, I noticed that it was “hanging” on a very specific object. And always the same object.  When I stopped the NST, there was nothing hanging anymore – but obviously, this cannot be the case.. .  Even more, when importing, it automatically tried to start the NST again (???).  Strange behaviour if you ask me..

Missing DLL

It appeared to be some kind of object that used some custom dll … . I can only assume that – even when importing txt – even when importing without synchronizing schema changes – it was still trying to connect to the NST to get to the dll.

Well, on my machine, which is a general upgrade-VM I always use for all my upgrades, that specific dll was not in my Add-ins folder, so no way for the NST to find it ..and – I assume – it hangs. No error. No message. No progress .. Nothing. I noticed that when I shut down the NST, all was importing fine.

Failing objects

But then again, when compiling, I had the same issue as above. No message, it just hangs and wastes your time. Apparently, when I finally figured out it was the dll .. and copied it in the Add-Ins folder, all went fine. But a next issue came into play. During the merge, I already noticed some customer objects that were failing. Failing objects means: completely ignored during the merge-process – which means: totally not part of the result. Obviously, this does not have positive consequences to the “Compile All” routine, as there are missing objects.

In my case, apparently there ware functions and local variables without a name. Apparently, it is possible to create this in the DEV environment, only the PowerShell tools does not accept this, and completely ignores the object.

I left it as is – was going to fix it later on in the resulting database – but it made the upgrade going completely bad – at the end, I was not able to even sync my schema-changes because of (probably) too many uncompiled objects (?).. . When I decided to first handle these failed objects, and include them in my merge, all went fine again.

So .. what have I learned today?

When you upgrade, always make sure you deal with the failed objects first, before you continue in creating fob or performing the final database-upgrade

When you upgrade, always make sure that you have your custom .Net libraries ready in your Add Ins folder.

All sounds very obvious, I admit. I was just not aware at first of any custom dlls .. and I definitely wasn’t aware that it would actually going to crash and hang my process without any notice. This doesn’t really co-exist with my “only-fix-when-error”-mentality


Delete all records in table with C/AL – Truncate

$
0
0

Did you ever have to empty a certain table entirely in NAV? Well, I admit, you don’t need to do this too often. And definitely not in a repetitive way.

Well, I came across a situation where I needed to delete all millions of records from a table every single day. So I didn’t only need a way to fill the table with millions of records in a fast way (that’s a whole other story), but I also needed a fast way to remove all the records.

Don’t use DELETEALL

First thing you think about while using C/AL is obviously the DELETEALL statement, which is generating (if you’re lucky) a “DELETE FROM table” in SQL. The outcome is slow, and it’s locking the entire table all the time.. . You don’t want this – believe you me .. . Definitely not for something repetitive, for a process that needs to be AFAP (as fast as possible ;-)).

When you start googling it, you can find that it’s advisable to not delete all records in one transaction – to not lock all the time.

In fact, you don’t have to search long to find another way: by using the “TRUNCATE <table>” statement (which removes all records without adding anything to the transaction log – be aware of this!).

Hey, altering data in SQL by not using C/AL code is not done because of the NST cache, dude

That’s a problem indeed. If I would call the TRUNCATE-statement in C/AL by simply call the statement with .Net Interop, then my NST Cache will not be notified of any transaction, which means: possibly, the content of that truncated table is still in cache. If I would read the content of the truncated table, I would still get the cached records – while in reality on SQL server they are gone.

You don’t want this! Your NST cache should be up-to-date at all time.

So, how can I delete all records very fast, and still have an updated NST cache?

Well, first, I just call the truncate, by using .Net Interop. Here is part of the code:

As you see, a simple SQLConnection and SQLCommand to execute the “truncate table” statement.

To update the NST cache, I tried two options which all seemed to work just fine. I first liked this one:

Which basically meant: try to delete all the records. Since the table is empty after the truncate, this statement will be fast. The only disadvantage which I didn’t test out is that I didn’t know how it would react when I would try this on a table which has an activated “OnDatabaseDelete” trigger. By default, it would generate a delete statement per record (to be able to call the logic OnDatabaseDelete) – but if the NST would use cache for it, I would be .. screwed.

That’s why I decided to go for this one:

Which basically forces the NST to a new read on SQL. This seemed to work as well, and I’m at least sure it is not going to start any hidden loops.. .

Disclaimer

Well, use at your own risk, obviously. And know the risks!

Personally, I’m doing this on a table which I can always rebuild with business logic, based on other tables – it’s some kind of “rainbow table” which I use to speed up searches – as said: I can rebuild it if necessary. So if it’s not part of any transaction log – I don’t really care. The rebuild should be as fast as possible.

Microsoft Dynamics 365 & Microsoft AppSource

$
0
0

Big day yesterdag – but also a confusing one.

A while ago, Microsoft announced Project “Madeira” as a “Public SaaS” offering from Microsoft, clearly based on our beloved NAV (but unspoken). There was a lot of fuss .. lots of people blogged about it (including me). But when you look at the name – Project “Madeira” – one could imaging this was not the final name .. right? If it is final, well .. then it would suck (only my opinion  ).

Microsoft announced yesterday “Microsoft Dynamics 365” and “Microsoft AppSource”. My first reaction was: ah, the final name of Project “Madeira”. But apparently, when reading into it, that only seems a wrong assumption..

Let me try to get you AS confused as I am at this moment:-)

And let’s start with the official Microsoft landing page for Microsoft Dynamics 365. There, you find the official blogpost that introduces this new product (?). It introduces Microsoft Dynamics 365 and Microsoft AppSource.

I guess, this says quite a lot:

Microsoft Dynamics 365 evolves our current CRM and ERP cloud solutions into one cloud service with new purpose-built apps to help manage specific business functions, including: Financials, Field Service, Sales, Operations, Marketing, Project Service Automation and Customer Service.

Without stating which actual product is behind the scenes – it mentions “current ERP cloud solutions and CRM” .. and the fact it’s going to be a cloud service. Well, not only that, even with apps (Extensions?) to help specific business.

Until now, it smells a lot like Project “Madeira”, but also may be some kind of AX version of it as well.. .

But then again, when you read further in the blog, things like “Cortana Intelligence” that will be natively embedded, or “Project Service Automation”, IoT, big data, … will be included .. does make me wonder if they are “only” talking about Madeira. I guess not. Then again – does it really matter?

My guess is that Project “Madeira” is just part of a much bigger picture, called “Dynamics 365” (the name makes a lot of sense, by the way), where there is a strong focus on making ERP available as a service in the cloud (just like Office has become a cloud service), and where customers can customize their cloud service with apps .. . Whether it is NAV or AX or CRM in the background, that doesn’t really matter – you want a solution and it’s going to be Dynamics 365 – depending on the solution you want, it will give you the product in the background (whatever it is) – making available the apps that is part of that particular service… . Something like that. The only thing I know for sure: time will tell :-).

I strongly recommend you reading the blog yourself to make up your own mind.

What is clear at this point

There is going to be a Microsoft ERP offering called Dynamics 365, and you will be able to easily choose, download and install apps from the AppSource.

What is this “Microsoft AppSource” all about?

Let’s start with the youtube video about it:

And then, just browse to the URL (https://appsource.microsoft.com/en-us/) and travel around in the AppSource. As such, you can even find apps (Extension) for NAV .. so I assume this is the “app store” we have been wanting for a while :-). It looks a lot like the Store: you are able to find apps (Extensions) for different products. I don’t see “Dynamics 365” as a separate product, but I assume it’s going to be either part of the products, or either a family name.. .

When is this available?

The blog mentions this fall – which happens to be the same time frame as Project “Madeira”.. .

Is that all? Because it is still foggy …

Yes, it’s quite foggy .. also for me. Probably I’m missing the big picture here .. sorry for that. But at least let me give you the resources I could find for now:

I look forward to the next couple of weeks, where all this probably will become very clear!

Dynamics365 & NAV2017

$
0
0

I could go and write an extensive article on it here. But I’m not gonna! I’m going to make it super duper easy on me.

Microsoft Dynamics365

This week, my fellow MVP and friend James Crowter went to WPC (World Partner Conference, aka, THE biggest reason for managers to spend loads of money to be away and party – oh yeah – and to learn about Microsoft’s focus for the upcoming year  ).

And guess what. James took the time to guide us through what Dynamics365 really is all about – or at least what it is going to be about. He wrote an article called “Q&A on Dynamics365” and I can only strongly advise you to carefully read through this and get yourself informed.

The main thing that I was confused about in my previous post, has been answered: Project “Madeira” IS going to be part of Dynamcis365 – it’s going to be the “Business Edition”, which is targeted at SMB.

NAV 2017

And that’s not all. It was also announced that for the on premise version – or how do you kids call it these days (classic?)? And apparently (as the title already suggested) it’s going to be NAV 2017.

I must say, I’m not too happy with that. And that is because of the current CfMD certification process. ISVs need to certify their product, and the certification needs to be on the current version, or one below. At this rate (every year there is a new major version (2015-2016-2017)), we need to certify every 2 years. And if you ever did the CfMD process – this is something you don’t want to do every two bloody years… .

Resources

As said, I did not visit WPC, so I’m going to make it easy on me and end this post with some other resources from people that got their info first hand (at least I assume):

New Extension Videos for NAV2016

$
0
0

As we could see during WPC, NAV2017 is around the corner. But that doesn’t mean Microsoft is not working on readiness material for NAV2016. Especially for the things that have been brand new, and need some attention. You know: Extensions. :-)

A few months ago, I had the opportunity to work together with Microsoft on some new NAV2016 videos on Extensions. And since this month (July 5th) they were published on all media (MSDN, but also YouTube). These videos were created by Vjeko (voice) and myself (content). Hope you like them :-).

How Do I Get Started with Building Microsoft Dynamics NAV Extensions on the Azure Demo

This is the first interesting video that handles on how to use the Extension Development Shell on the Azure Demo VM. I blogged about it here .. and now there is a video as well:

How Do I Build My Own Development Environment for Starting to Develop Dynamics NAV Extensions

If you are willing to build your own development environment for building Extensions (something that I do and would recommend as well) – then we created a video to help you on this. So, I suggest start with this one .. as the next videos are based on this development environment.

How Do I Build a Basic Microsoft Dynamics NAV Extension

When you have your development environment in place, you can start looking into building a basic extension by watching this video:

How Do I Include a Permission Set in a Microsoft Dynamics NAV Extension

The next step is to include permission sets into your Extension. We made a separate video for that here:

How Do I Set Prerequisites and Dependencies with Microsoft Dynamics NAV Extensions

And last but not least, there is a video on how to work with Prerequisites and Dependencies. They are both totally different things, but often confused. This video explains what can be expected, and how you do it with NAV2016 Extensions:

There is one more on the agenda: debugging. A small video with tips how to debug Extensions. I’ll ping you when it’s finished!

Microsoft Dynamics NAV 2016 CU10 – buggy CustomSettings.config

$
0
0

Just a small blogpost before I leave on a well-deserved (if I may say so myself) holiday :-). And it’s going to be a REALLY small one .. with one small tiny warning that my colleague came with earlier this week.

The new CU has a wrong default CustomSettings.config file in the server folder (DVD:\ServiceTier\program files\Microsoft Dynamics NAV\90\Service):

You see it has port number 7047 on both SOAP and Odata. Obviously this doesn’t work.

You shouldn’t be impacted that often though, as when you install NAV, you usually overwrite these settings. Like I personally use my install-script on my GitHub, which always uses a settings file like this:

But there are scenarios where you would copy service folders to new folders and set up serverinstances like that. We do that on our development servers to maintain multiple builds and versions. In that case, you will be affected, and you need to deal with it.

That’s it! Enjoy summer!

Directions US: my sessions

$
0
0

It’s almost there .. Directions US is around the corner. My first conference of this year’s “conference season”. And one might expect that NAV 2017 will be released there! Who knows

Well, also this year, the conference is going to be quite busy for me. One of the reasons I haven’t been blogging too much: I have been preparing and preparing – and getting myself somewhat familiar with the new version of Microsoft Dynamics NAV.

My Sessions about Extensions

As you might have expected, Extensions is going to be quite a hot topic. Nothing much I can share just yet – just that you should get yourself informed. And I’m going to try to help you with that :-). Because my sessions are going mainly going to be about just that: Extensions! At least 3 out of 4 will be ;-).

Session 1: Dynamics NAV 2017, Extensions, Best Practices

Monday, September 26, 2016| From: 1:30 PM to 2:30 PM | Room: Kave (Two)

Well, the title says it all: it’s going to be about some Best Practices that I think are important for you to develop Extensions. There will be some PowerShell (knowing me… ), but also some development tips and tricks, things to look out for and things to take into account. I don’t know I will be able to address all – because I have had a lot of topics. Both from my own experience, but also from input by Johannes Wikman (known by most of you probably ;-)) and by “the Tecman Team” (aka James Crowter and his team ;-)).

Session 2: Bad Habits of NAV Developers

Tuesday, September 27, 2016| From: 5:15 PM to 6:15 PM | Room: Komatke (A)

Not about Extensions, but about development. Together with Vjeko and Gary, I will address some bad habits (or at least what we believe to be bad habits) – and will try to break them by providing good alternatives .. . This is going to be fun! :-)

Session 3: My First Extension

Wednesday, September 28, 2016| From: 11:15 AM to 12:15 PM | Room: Komatke (A)

It may sound weird, but on the last day – one of the very last sessions – I will give a brief overview on what it takes to build your very first extension. And introduction, if you will.. . This session is obviously not for the more experienced people, but more like for the people that really never built an extension, and would like to see what it’s all about.. .

What else?

Well, one more. But one I can’t really prepare. I like that ;-): Dynamics NAV 2017, Extensions – An Interactive Discussion together with Arend-Jan Kauffman on Monday, September 26, 2016| From: 3:30 PM to 4:30 PM | Room: Komatke (C).

And that’s it, I’m afraid. Like they say in Dutch: it’s a “big sandwich” .. so I know what I’ll be doing until the event starts ;-). See you all there!

Microsoft Dynamics NAV 2017 – what’s new

$
0
0

Well .. There it is .. It’s out in the open .. :-).

During the Directions call today, Kurt Juvyns introduced the upcoming conferences: Directions US and Directions EMEA. By announcing which sessions there are going to be during the conferences .. and the track (or was it called “path”?) “what’s new” he also announced what is actually going to be new in the product.

So, let’s not focus this small blogpost on what is going to be on the agenda at Directions (I strongly recommend you to review the conference websites), but focus on what is actually going to be new in Microsoft Dynamics NAV 2017. And, as one picture says more than a 1000 words .. :

I don’t want to go too deep into it .. just because I can’t … but I’ll still, let’s try to see what’s coming to us. Microsoft has been focusing on three pillars:

Bringing two worlds together

I guess this is clear: a much deeper Office 365 integration with NAV. I’m thinking of the stuff I talked about in one of my previous posts: a tight integration of NAV and Outlook.

But also: embedded Power BI. How cool is that? I just hope this is going to be easy to implement (didn’t have a look at it just yet…)

Enhancing the core application and platform

In this part, you see they have been working on quite some application improvements in different areas:

  • Jobs
  • CRM
  • Finance
  • Items

And also the platform has been improved. That can mean a number of things: both technically behind the screens, or things like screen rending on the client (remember the screenshots which I showed here with for example a new way to show lists (more like “bricks”)).

Creating new opportunities

Kind of a general title .. but when you look at what’s behind – that’s amazing. I’m really looking forward to dive into the Cortana Intelligence, but also PowerApps& Flow.

But I think it’s not a secret: Extensions is going to be the *** (pardon my language..)! Whether you like it or not, Microsoft is committed to Extensions, and making Extensions as important as can be in the near future. NAV2017 is another big step in that direction! Let’s say a lot of capabilities were added .. which make creating Extensions a lot more feasible then it was for NAV2016.


Directions US 2016 – Final Thoughts

$
0
0

What an exciting couple of weeks it has been .. . I was in the States for 8 days for Directions US. The first half, we stayed in Flagstaff at our fellow-MVP and good friend Daniel Rimmelzwaan. That dude lives at an awesome place: Sedona is nearby, as well as Grand Canyon, … and lots of other great stuff. So besides the usual work, we needed to check these places out – so we got to Sedona and the Grand Canyon .. and that literally takes your breath away. Just look at this (behind us, I mean :-))

But .. there was obviously a business-reason for this trip: Directions US.

The Venue

This year, the convention was hosted in the (very hot) Sonoran desert in the conference center of a beautiful place called “The Wild Horse Pass Resort” in Chandler near Phoenix, Arizona. A special place .. especially when you think about rattle snakes that manage to get into the hotel’s lobby, run trails where you need to watch out for coyote’s, mountain lions and – again – rattle snakes, golf courses in the middle of the desert, … and a bunch of NAV guys (probably the most special thing of all).

Some stats

It was quite a bunch .. In fact: 707 registered attendees managed to get its way to the rattle snakes! Most ever, I was told! And the conference had an immense amount of 57 sponsors and exhibitors. That’s a lot – I don’t think that many are at NAVTechDays, while NAVTechDays hosts many more people.. .

The agenda was quite interesting as well. There were:

  • 91 breakout sessions
  • 35 ISV sessions
  • 20 Microsoft and ISV workshops
  • 15 Townhall meetings (aka “chalk-and-talk”)

That’s massive! Many concurrent sessions, which makes it always a challenge to choose where to go to. And also there was a solution: there were repeats :-). And I used one of them – I was happy to be able to attend the session on the new developer environment. Which brings us at the topic about “sessions”…

The sessions & the content

Well, let’s be brief on the content: Microsoft Dynamics NAV 2017 and Dynamics 365. Doesn’t say much, does it? :-). Well, I’ll try to do a separate blogpost on that in the near future .. as there is much to share. Lots of good stuff!

The sessions were very good. I didn’t attend one single bad session. Not one. Then again .. I haven’t attended all I would have wanted .. simply by the fact that I had my own sessions (4) and had to finish up on the preparation of those as well. Which means I did spend quite some time in the “Speaker’s Room”.

First of all I attended a session on PowerShell. Actually because I wanted to learn new stuff from different people. Thing was that the guy (don’t remember his name) was using my scripts :-). That was cool to see they are actually being used – but unfortunately nothing new … .

Next was a session that was called “Extensions & Events – practical examples“. For me, that was an interesting session to attend, because of my own session about “Extension Best Practices”. Now, it turned out that Michael Hammond and Adam Trukawka made it into a “Best Practices” session themselves :-/. Didn’t see that coming .. . I was lucky that there are enough “best practices” .. only a few overlapping topics .. so no harm done :-).

Another session I didn’t want to miss, was Vjeko’s session on “Polymorphic Event Patterns“. Well, that guy did it again. Mind boggling new ideas to do true polymorphism in the current development environment, by combining multiple already existing patterns, like:

  • Façade Pattern
  • Handled Pattern
  • Variant Façade Pattern
  • Discovery Event Pattern

He is already blogging about it, so pay attention to his blog! These are already out:

Then the session by Stuart Glasson and Esben Nyhuus Kristoffersen about “The new development experience“. Well, it’s not going to be there at the release of NAV2017 – there is still quite some work to do – but it is coming. And it is going fast! In the next blogpost, I’ll try to explain what it is. But for now: in the future, we will be coding in VSCode! And this is what it looks like:

I attended both Key notes. This is not of my habit as I usually don’t like all this marketing and business fluff. I just want to dive into the tech stuff. But as a lot is changing, and new markets are opening – I guess it makes a lot of sense to attend these, and see what the messaging of Microsoft is all about. And hope (I repeat: HOPE) that I understand some of it ;-). Well, it was clear to me that “On Premise” is not dying .. . That’s good. There are a lot of people in that business! And it is also clear that new markets are opening: Dynamics 365. And for partners that are there .. new opportunities are opening: AppSource. And a lot a lot more. But for now, let’s share this slide:

Yes indeed: the release date for NAV 2017 is October 24th 2016! Dynamics 365 will follow shortly after .. and the bloody sexy new development environment is not yet to be released, but we’ll get a preview by Christmas this year (2016 that is, right Stuart? :-))! How’s that for a Christmas present? :-).

This year, there was no MVP track.  That’s too bad.  I don’t really know why it was cancelled, but I do hope to see it back next year!

My Sessions

As you could read in one of my previous posts, I had planned to do 3 session. The truth is I did 4 .. . I do admit, I was quite nervous as all I want to do is just do a good job. That means, be thorough enough, clear enough, all that. And when I need to do that amount of sessions, I just don’t feel comfortable enough. Well – it’s over now – and to quote Gary: now I’m happy to have done all of these. It was fun, the audience was great, interacting, attentive, .. good stuff! These were the ones I did:

  • NAV2017, Extensions, Best Practices“. The hardest one. I was not particularly happy with the demos .. so I’m going to work on that for Directions EMEA. And for NAVTechDays, I’m going to add a lot more stuff :-).
  • Bad Habits of NAV Developers“. One of the most fun sessions ever that I had to prepare and deliver. It was a session with Vjeko. My first actually with Vjeko. And you all know him .. standing next to an icon like that .. well .. I was proud :-). We prepared the session together in one day .. the same day we had to deliver it. Pressure, but fun – and also quite confronting! I’m not going to share too much just yet – because I have a feeling that this was not the last time we did this (#NAVTechDays ;-)).
  • My First Extension“. I actually only prepared this session on the evening before. Just because I was quite comfortable doing this, as I have been doing it for all PowerShots for NAV 2016. This session was very well received. I had people coming up to me saying things like: “I have been here all convention, following sessions about Extension – but now, I finally get it” :-).
  • Town Hall on Extension“. Town halls are unprepared sessions where attendees can ask away, and the speakers pretend to know what they are talking about ;-). I attended a few of them myself, and co-hosted this particular one.
  • Workshop: Build Extensions“. Last one. I didn’t need to host this, because it was concurrent to one of my sessions, but I did need to prepare the complete workshop. The reason for this is that the speaker was not able to attend Directions, I was not able to host it, and Arend-Jan Kauffman (who hosted the session) was not able to prepare it. Well .. we all try to help each other out, so there you go :-). I was happy that all turned out ok! I just hope the attendees liked the example, the scripts and I hope they learned something.

The last day, a guy came up to me and said: “thanks so much for doing so many sessions, man .. I enjoyed each and every one of them”. Well .. that’s the feedback that makes we want to do this :-). Thanks so much.

The overall organization

Well .. I was quite pissed at the moment I found out that the committee didn’t foresee enough rooms for speakers. First year I wasn’t at the conference hotel. You know, during the conference, you are so busy that all you want to do is just not loose time in taxis and all that. I ended up in a crappy place 15 minutes from the conference center. A good thing about it, it didn’t cost all that much

But that’s about all the comments I have .. the rest of the convention was very well organized. All you expect was present. The infrastructure for speakers was very good, very big screens, comfortable chairs (well, you still can’t beat NAVTechDays), good content, great partner celebration party (I had a “ball”! :-)), .. .

Good stuff, I’ll be there next year (if you still want me ;-))!

Talking about next year .. Directions US 2017 will again be hosted in Orlando (I was hoping for something else .. but then again .. Orlando is not that bad ;-)).

See y’all there oh’y’all!

Microsoft Dynamics NAV 2017 – what’s really new?

$
0
0

Hello from beautiful Prague at the second day of Directions EMEA.

A while ago, I blogged about this slide, without giving too much deeper information:

Well – let’s go deeper!

But first a disclaimer.  While reading everything below, keep this in mind: a lot is still to be dived into… :-).  Why?  Well, I wrote this blog a while ago, just after Directions US, where I wasn’t able to attend all the sessions that I wanted to (which I will make up for at Directions EMEA this week ;-)).  And there is a lot .. .  But for now, I already wanted to share as much as possible.  And it’s a lot! :-)

So, let’s get going, by diving into the three pillars that Microsoft uses as well…

Bringing two worlds together

Well, this is obvious, right? Although I think the right title would be “bringing three worlds together”, because we are talking about bringing Office 365, Power BI even closer to Microsoft Dynamics NAV. Microsoft has done this by adding things like:

  • The ability to invoice your customers, based on entries in your calendar. There is a Dynamics NAV Add-In in the calendar appointment to give you this ability.
  • In the email section, there are buttons that integrate NAV with your emails. You’re able to see the contacts information in NAV, and such things. I already blogged about that here.
  • From the contacts list, you can manually synch your NAV contacts to O365 people. The contacts will sync back to NAV as well (filters can be applied).

  • I don’t have any documentation of it, but there is also something that is called “Embedded PowerBI“. I don’t know the details, and have no screenshots – but basically it’s having decent PowerBI charts embedded in the (any) NAV client. On Role Centers, Fact Boxes, you name it. Basically, calling out to your PowerBI from within NAV:

    I did find some information on DLP (Dynamics Learning Portal), like a walkthrough.  Go check it out!  I know I will :-).  I think this is a very important new feature!

Enhancing the core application and platform

A lot has been done on the “Setup and Configuration” part of the product. A few things, really, but most important is the “Assisted Setup” kind of thing, and the “notifications” (which I will talk about in a minute). Assisted Setup has been handled in this blogpost, was probably created mainly for Dynamics365, but is very interesting for any implementation, in my opinion. Basically, you can plug your own setup into this page:

and up you go with your wizards:

The way these wizards are created needs some coding skills (quite some coding on the page). But you’ll get used to it :-).

Speaking about simplified setup: it’s also simpler to setup Dynamics CRM from inside Dynamics NAV.

On top of that, there is a simplified opportunity management and CRM functionality– so even with Dynamics CRM, there is an improved CRM experience in NAV. Here are a few improvements:

  • The wizard pages have become card pages, to be able to use them in the web client
  • Worksheet pages have been replaced by list pages, so you can use it in browsers
  • The contact card has been simplified (by turning some fields into “additional” fields)
  • Mail merge have been replaced with word reporting
  • A new role center (Sales & Relationship Manager)
  • A new wizard for setting up email logging (you have seen that in the screenshot above about the “Assisted Setup”)

Also, there are “richer incoming documents” in the product. Now, I’m not an expert on application level, but in short, I know it’s this:

  • The list of incoming document is filtered, which reduces the amount of entries (filtered on “processed” field)
  • OCR support for document lines. In my opinion, if this really works, that’s quite advanced!
  • Visually validate and train the OCR service. As such, it’s just using the new online experience that Lexmark has put online to visually train and validate the OCR processing of PDF and image files.

Microsoft has also worked on the Items. First of all, they introduce “Item Attributes“.  Now, in my company, we have our own “attributes” (which we call “specifications”) which is – I must admit – more advanced .. which is good, because then we have a reason to keep it :-). But then again, many people will be happy, because finally you can have a dynamic set of attributes which you can use to enter Item “specification”.

And even more .. you can filter the Item list on it as well!

Basically: you’re able to set a filter on the Item table, based on values of a sub table .. . I didn’t see this in the product just yet (or I’m missing something…). We did build it our own (quite more generic), and I was happy to see Microsoft did it quite the same way: building piped filter, based on the “No.”-field in the Item table. I am curious how they handle the cases where the filter include over 2000 Items though .. :-) (SQL doesn’t really like that..)

On top of that, you’re now able to better categorize your Items as well, by the ability to create an hierarchy of item categories and assign attributes to each item category.

Next, it’s possible to cancel posted credit memos (sales & purchase) and you have package Tracking No. and Shipping Agent Code on sales invoices. Let’s just say we have “smarter sales and purchase documents” ;-).

And also accounting got a little bit smarter, as in NAV2017, we can categorize our accounts, like you see here:

Next: Jobs! A Project Manager Role Center, and a new fact box on the Job Card that displays the job’s cost information. On top of these, some more small changes were added, like:

  • A “My Jobs” list
  • Project manager field
  • Create job Sales Invoice
  • Contract is changed to “billable” throughout the module
  • New Job Quote report – and it’s a Word Template

Also, some small additions to Fixed Assets were made. It’s now easier to set up with default setups (which you can modify) and you are able to register purchases of fixed assets through a special fixed asset G/L journal where you can also dispose of fixed assets.

Smart notifications. Well, this must be one of my favorites. I must dive into it technically (and I will handle it shortly in a minute..), but in a way, it’s possible to give the user in-context notifications, and assign an action that the user can execute along with it. This is just a great user experience right under the fingertips of the developer. It’s intended to give the user advice and recommendations – it’s up to the user to react on them or ignore them (so, it’s not a confirm at all!).

Another nice one: there is now a “Brick view” (small and big) on lists.  I already showed it in one of my previous blogs.  it works like we would expect: field group “Brick” and adding a field with “MediaSet”-datatype to it (as I was told :-)).  This is a screenshot of a list in “Brick view”:

041216_2220_ProjectMade9.png

Creating new opportunities

Microsoft creates Extensions as well .. namely, Microsoft Dynamics NAV 2017 comes with already packed Extensions. It’s still a question whether we are going to be able to change the code of that particular Extension or not (by creating a dependent extension if necessary).  It doesn’t look like it. On the product DVD (of the beta), you can only find the .navx files:

Who knows, It might be there in the RTM afterall .. but it’s not that big of a deal. The whole intention about Extensions is “not having to change them” anyway.. and if so, create a dependent extension (but we do need code for that..). Let’s just hope they fulfill 100% our needs ;-). Microsoft has foreseen these Extensions:

  • Ceridian Payroll
  • Dynamics GP Data Migration
  • Envestnet Yodlee Bank Feeds
  • PayPal Payments Standard (Marko blogged about it here)
  • QuickBooks Data Migration
  • Sales and Inventory Forecast

For developers

Well, for developers, there is quite a lot to mention. First of all: the capabilities of Extensions have been immensely improved. All object types are available now .. and more. In short:

  • Additional Object Types:
    • Reports
    • XMLPorts
    • Queries
    • Custom report templates
  • Default and starting data
  • Multilanguage captions
  • Support for .Net Framework Add-Ins
    • .NET interop types executed on the server
    • Client-side JavaScript
    • WinForms extensibility control add-ins
  • Support for web services
  • Restore and backup data in extensions
    • NAVAPP.RESTOREARCHIVEDATA
    • NAVAPP.DELETEARCHIVEDATA
  • Updated PowerShell to publish Extensions
    • Publish to SQL db on Azure SQL
  • Installing Extensions
    • Users can install/uninstall from Extensions Mgt page.

Next, there is now the possibility to publish Web Services per tenant. This was very necessary to combine with extensions. In my opinion, we should be able to create Permission Sets per tenant as well. That would be awesome! (or maybe it’s already possible, and I’m missing something ;-)).

Another feature that was added, is “notifications” which I talked about earlier. There are a few new statements which we can use in code, like:

  • Datatype “Notification”, so you can create a variable of this type
  • With this variable, you can set the scope, data, message, action and such, like this code, that I copied from an unofficial what’s new document

And then we have “Application Area Tagging“. My by far least favorite new feature. The Application Areas system offers us the ability to define “differentiated user experiences according to application scope”. That all sounds very interesting, and I agree it’s very necessary in a cloud-world. Only, the way Microsoft implemented it, is on page-field-property-level, like you see here:

You can only imagine what immense effort it would take to change a tiny bit – let alone to plug in your own vertical into this system.. setting page-field properties on each page, on each field .. . This looks like the same way as the “Show Mandatory” property was implemented. I still hope I’m missing out on some information that all of a sudden makes this feature overly interesting.

As mentioned, one of the Extensions that Microsoft is shipping, is the “Sales and Inventory Forecast”. Well, this system is based on Cortana Intelligence. And to be able to plug into this, Microsoft has foreseen an API in C/AL, that we can use to create our own forecasts on our own business logic and/or data. The API is called the “Time Series library“. I haven’t had a look at it just yet. But basically, you have a codeunit that helps you tremendously with setting up things like that extension:

This is how the “Cash Flow Forecast” looks like with Cortana Intelligence in place:

Another thing I noticed is the fact that the Job Queue / NAS has been completely revised.  Now, you have a Task Scheduler “service” that you can enable in the Admin Console.  I haven’t gone into it fully, but I like the concept.  I like to compare it with something we call internally a “Worker Threading Framework” (or “WTF” if you will :-)) – basically having workers (in the case below: 10 workers) waiting for you to perform (simultaneous) tasks.  Great concept!  We built it on top of a NAS and background processes – I’m looking forward to dive into this.. .  Here you have a screenshot on how you can set it up.  No codeunit that starts an endless loop anymore, but a Task Scheduler straight in the platform:

task-scheduler

And, as you probably already assumed – there are C/AL statements for you to code against the Task Scheduler.

task-scheduler-code

There is an integration with Microsoft Flow and PowerApps– or at least, both can be used in combination with Microsoft Dynamics NAV 2017.  Basically, it enables you to build stuff on the “Microsoft Common Data Model”.  I don’t know the specifics though – but do check out these links to get yourself familiar:

In the demo on the Keynote, Claus showed an app that he built for a phone (looked like it), build in PowerApps (in about 4 hours) with data that was uploaded from 4 NAV instances (and I thought I understood there was also one AX instance) to the Common Data Model – and the PowerApp got the data from that.  The designer op the app was completely online, very intuïtive.  I haven’t checked out yet, but again, please do so with the above links.

The more I dive into NAV 2017, the more I get stimulated to dive deeper .. as there are so much things I don’t know yet.  Next thing that’s new but I’m quite unfamiliar with, is the Media and MediaSet datatype.

mediaset

It’s accompanied by what seems to be a framework around “media”, like you see here some system tables:

mediasettables

Let’s have a quick look at the default content of one of those tables.  Now you see this:

mediasetcontent

It seems quite clear: this is where pictures are stored now.  If you look at the Item table, you see that the “Picture”-field turned into a “MediaSet” datatype.  So my guess is that when you want to use pictures: use that!  And you “automagically” are using the system tables (kind of like you do when you use Record Links).  This is an assumption – need to dive more into it.  Expect a blogpost soon about it.  But if you know more, please comment :-).

And last but not least, the “New developer Experience“. Well it’s not going to be released in the upcoming release. As stated in my previous blogpost, it’s only going to be there in preview at Christmas. To cut short, you should read all about it in the recent blogpost of Arend-Jan Kauffman. It’s cool, and it’s how we are going to do customizations, extensions, .. in the future! Here is a screenshot AJ used in his blog:

Or here is one of mine:

The client that you see, is a well-known, new code editor, known as VSCode or “Visual Studio Code”. You can use it cross-platform (we will be able to write code on a Mac, or on Linux) and is available for many languages by the ability of installing “VSCode Extensions” to add an ability or language to the editor.

As long as it (AL on VSCode) is not available, I have a tip for you: you can use VSCode for PowerShell as well – and it’s maybe a great way to get used to the editor! I’m doing that as we speak. VSCode is not ideal for PowerShell, but I truly believe, the more I use it, the more I’ll make it good enough for PowerShell. And I will be used to VSCode by the time we’re developing AL in this editor! Just my two cents :-).

Next to this new development goody, there will be a completely new “personalization” experience as well (if I may call it like that). As such, you’ll be able to open the web client in design mode, where you will be able to do a number of things, like creating new fields, put them on a page, move the fields to different places on the page. In a way: a visual WYSIWYG-designer. Here are a few screenshots from AJ’s post:

Go into Design mode, and drag a field around:

Add a field to a page:

Save for myself, profile, everybody,…

The future looks bright ;-).

Some other small goodies

There are some other small goodies that I picked up from some Tweets of Jannik Bausager.

First, we will be able to define email body templates in Word:

Also, Tooltips are now available on fields in NAV 2017. I can only imagine how this has been implemented. Well, yet again, on Page-Field-Property (why do they keep doing this?? :( ).

The Mobile App now supports “tap and hold” on list pages to bring up Actions!

Next, there are some new document layouts:

And not only, we have this new “Notifications” .. you’re able so set up when to show them as well:

Also, there is a new setting on the Sales & Receivables Setup: Default Quantity.  Like Jannik puts it: “Often just selling 1 of each item, many company do. In Microsoft Dynamics NAV 2017 you can default quantity on sales document lines to 1”

defaultquantity

And then there was Dynamics 365

Well, as Dynamics 365 is a big topic in the NAV world, it’s not the topic of this blogpost. For Microsoft – how I understood it – “Microsoft Dynamics NAV 2017” and “Microsoft Dynamics 365 for Financials” are two different products. And while the code base of both are the same for this release, Microsoft says it can’t promise that it will be kept the same in the future. And that makes sense. I truly hope it’s not going to be the same .. and we will have a codebase for On Premise, with stuff that makes sense for On Premise .. And visa versa: we will have additions/changes, especially for Dynamics 365, that makes sense for that business.

And make no mistake.  On Prem business is here to stay.  That was very clear after the keynotes (Directions US and EMEA): it’s clear Microsoft has a clear “AND” strategy: On premise is absolutely important. It is what took this community this far, and it will drive that in the future.

But do be aware about the new opportunities though: and that’s called “Dynamics365”. The market is moving – and we (partners) can grow and move with it, THANKS to Dynamics365 !

Where can I get started?

Well, I have a few links for you:

That’s all folks.

It turned out to be a big blogpost. But that’s only because Microsoft once again did a whole lot in (just) one year. Big thanks for that. :-)

I’m currently upgrading our product .. so I might come back with some “Best Practices” or “caveats” or “whatevers…” about upgrading to NAV2017 soon.. .

Stay tuned!

Microsoft Dynamics NAV Extensions – Yesterday, Today, Tomorrow (1/3)

$
0
0

Directions EMEA has just ended .. and how great that was! Let’s see if I will come to writing up a “final thoughts” .. . That’s not for today.

After some “Extension-Extension-writing” recently, I wanted to put out my own thoughts. My first version was a very long blogpost. But since I just bothered you with an insanely long post (for which I apologize ;-)), I decided to do this in three posts :-):

  • First, I’ll give my view on the evolution of Microsoft Dynamics NAV Extensions. (This blogpost)
  • Next, I’ll address a few concerns. Basically give my view on it
  • And to conclude, I’ll give you my view on what to do next. When and what I think you should do with Extensions now.

In this post, I’ll have a look at what we had, have and might expect.

History

Microsoft Dynamics NAV 2016 gave us Extensions. From day zero (when still in beta), I have been diving into it – creating a “kick-ass” (not really..) Rental Extension for demo-purposes together with Gary. And when you looked at it, you had the impression that everything was possible!

Well – not really. It was quite a pain to develop this Extension. Like:

  • You had to restructure code
  • Not all object types were possible – only pages, Tables, Codeunits
  • No Add-Ins
  • No Web services
  • No Job Queues
  • No Reports
  • No Translations
  • … (and a lot more)

Also, to be able to create an extension, you needed to manage the fact you have original and modified, compare them, create deltas and so on. Although – if you put a bit of time into PowerShell, this didn’t have to be painful at all.. .

That was the situation in the past. In my opinion, this was a prototype.

Present

In NAV2017, we see an immense improvement in the capabilities, which I blogged about here. About all is possible now! Sure, we’re still working with Extensions as being mainly a collection of deltas (and some other files that include other things like web services, permission sets, add-ins,… ). But, thanks to the fact Microsoft already released a version of Extensions in NAV2016 (the prototype), we all have been diving into it, right? So the PowerShell scripts are still there! I had to do limited changes on my scripts to facilitate NAV2017 Extensions. And now, it takes me literally 5 clicks to start developing a new Extension, or to set up a DEV environment out of my GitHub to continue developing on an existing one. Like I have now put WaldoNAVPad into an Extension. (If you want to see this example, it’s all on GitHub. Powershell, NAV code, AddIn, everything .. feel free to have a look).

To be honest, I can hardly see any limitations to develop Extensions. Sure, sometimes you have to rethink your code-structure a bit … but that’s because we’re so used to change objects rather than extend them.

I mean .. think of it .. there are design patterns that facilitate upgrade .. which many of us embraced as being good practice! These patterns basically minimize the footprint in code. We all know that these patterns do not facilitate readability. But we accept them, because we want easier upgrades. Right?

Well, guess what. Extensions facilitate upgrades as well .. even better than any existing design pattern .. so let’s implement design patterns that facilitate Extensions .. . This is not so different.

That does not mean that I think this current version is ready for on premise customizations or verticals. Simply by the fact that at this time, there just aren’t enough events. And of course, Microsoft is on it: codeunits like 80, 90, 229, … are completely restructured .. and we will be able to see more and more events in the near future. Probably already in CU’s.

There is only one conclusion: look at the giant leap Microsoft has taken towards an Extension-only development model (if I may call it like that..).

If you’re still denying Extensions .. you’re just plain wrong.

If you’re still avoid diving into it .. you’re just plain wrong.

And that’s only my opinion. :-)

Future – The ultimate dream

As said, I strongly believe that Microsoft is working towards an “Extensions Only” model. All customizations, all customizations on top of customizations – it’s going to be all Extensions. And that’s a good thing.

Just think about this:

The entire default database is a reference model. In your development environment you reference to this model (like you can create references in Visual Studio to any kind of libraries). By referencing, you get your objects, functions .. whatever you can program against, like codeunit 80, pages, … whatever. AND you get your events, to be able to use to hook into code. So in a way, I would be only creating new objects, by using references to existing classes. I would create new “classes” (let’s call them Extension Objects) to extend existing objects. This is exactly what I believe Microsoft is working on. Arend-Jan Kauffman already talked about this in his blog. Please read – it makes all the sense!

.Net people would say: uhm, ok, duh, that’s how we do stuff, indeed. But for NAV developers, this is a big change.

Because we are used to change any existing piece of code.

Some people hide behind the fact that this is an “Open Source” model. In my opinion, this absolutely has nothing to do with Open Source. “Open Source” is a community model where the community works on one code base. We don’t do that. We can read a certain codebase, created by Microsoft, and we modify it for every customer. It’s the complete opposite of open source – and can’t be compared with any orthodox software development environment. Call it a “Copy/Paste/Modify”-model. We’re not extending, we’re changing .. and in many cases, we are “breaking”. I call it “job protection”.

So why not work towards a model that is AS flexible AND we solve the upgrade-issue as well? Let’s not copy-paste-modify .. but just “reference-and-extend”.

Will it come with new pain points? Maybe so, but it will also solve a lot of other ones.

Conclusion

I truly believe that Extensions is going to be the solution for many pain points we have today. May be they are not already .. . But in the future they will! For On Premise, for customizations, for your private and public cloud. And guess what .. for Dynamics365 they already are!

Microsoft Dynamics NAV Extensions – Concerns (2/3)

$
0
0

This is the second “leg” of my “Extensions”-series, where I want to address some “concerns” I hear sometimes regarding Extensions. If you haven’t read the first one, here you can find it.

First a disclaimer: I don’t know about all concerns, but there are a few that I picked up, which I tried to summarize in the below

Why can’t I see the code?

This is one I get a lot. Basically, the problem many people have is that they cannot code against Extensions. And it’s kind of true .. . At least in the current version of Extensions. Just because the fact that your object designer doesn’t see anything of the Extension. So creating reports based on fields that only exist in an extension .. that’s going to be difficult.

Difficult, because it IS possible. The only thing that you need to do is to create a dependent extension. One caveat: you still need the code of the extension you want to create a dependent extension for. So you ARE able to change the behavior of an Extension – you just need the actual objects, and not only the .navx file.

Well – then it’s up to the ISV, right? Are they willing to give you the code? I had to do this once .. asked the ISV .. created my dependent extension (with legalizations for Belgium) and we were good to go.

But I understand not all ISV’s are eager to give you the code .. and then, it’s not that different then we have now – as ISV’s are already able to “protect” their code with the license .. . But let’s keep that for the next section.

And let’s not go into the “Open Source” discussion – NAV is not Open Source.  It never was.

My assumption (not based on any info I have from Microsoft) for the future is that this might change. When you think back about the reference model I talked about in the previous post. Well .. we might be able to get those references as well to our VSCode DEV environment. I mean – who knows – why not. The code is still protected, we would just be able to code against it.

 

The code is protected, right?

The previous concern is about developers. This one is about the ISV, which has conflicting concerns :-). ISV saying “my code is protected, right???”, and developers saying “I can get to the code, right???”.

My opinion: Why bother? I don’t think your IP is in the code. It’s merely a way to “store” your IP. Your IP is in the (industry) expertise.  You power is not the ability to write .. but the ability to translate – translate the IP into business logic.  Code is just a tool. Nothing more. I never cared about other people being able to read my code. I put it on github for crying out loud!

And as said earlier, this is not so different as what we are used to. I was never in favor of this, but people have been “masking” their code, like this download on mibuso. I even have seen partners scrambling their code into unreadable variable names and such .. go figure :-/. And of course, there is the license, that can prohibit you to go into design of an object.  The one and only right way, in my opinion.

So, is there no way to get to the code of an Extension? Actually, yes. I’m aware of tricks to get into the code of an extension. As I’m aware about tricks to get into the code of an object that is not licensed in a normal development environment. So .. also no changes there .. we have been able to do this for years. Just different ways to do the same. Both illegal, by the way ;-).

 

“You can do bad things with Extensions .. or you can use it in the wrong way.”

Well, isn’t this with about every development feature/language/environment? Just think back at what Spiderman’s uncle said: “With great power comes great responsibility“. You, developers, have great power!

I can hack with PowerShell .. does that make PowerShell bad? I can write “Format c:\” in a shell – does that mean I have to remove the command shell?

This is why we have Best Practices.

This is why there are Design Patterns.

This is why we have Education.

 

Extensions is still no solution for upgrades

Wrong!

But let’s first take a step back. On any system that upgrades from A to B, software needs to be revised.

  • Whether it’s apps
  • Whether it’s just software
  • Whether it’s a website

Just a few examples:

  • If you upgrade your phone, the apps usually start upgrading as well very shortly after. These apps have been updated for the new version of your Phone’s OS
  • I just upgraded to the newest build of Windows, well, my display adapter didn’t work anymore .. it needed an upgrade. And guess what – it was already available.
  • When there is a new version of explorer, some (parts of) websites might not work anymore.. .  This happens all the time..

An Extensions “extends” something – so if you break the thing that it is extending .. well, sure you have some work! This is what I believe is called “maintenance”. Quite common in “software development”. So if you think that you write an Extension, and you don’t have to look at it for the next decade .. then you have the wrong expectations from any kind of software, not only Extensions.

Yes, but “in the old days”, we could at least choose to stay on an old version..

Could you really? Because many countries very regularly come with new legislations .. which HAD to be implemented at some point in some way, else, you would just be as illegal as can be. So maintenance was needed as well!

Does this mean that Extensions will not help us in upgrades at all?

Sure they will. I believe that in 99% of the cases, they will be able to upgrade without any problem. Why 99%? Because at any upgrade I do now (which is classic, nothing to do with Extensions …), I can upgrade 99% of my code without conflicts. And extensions are far better in this  .. one reason is because they don’t touch code at all! Remember we probably need to rethink our code to facilitate Extensions. This is where you’ll see the benefit!

There are just a few scenarios where you need to rethink your solution, like:

  • When new functionality in an upgrade version of NAV can replace your extension. Although – they probably can co-exist.
  • When Microsoft redesigns a part of a solution. Even this doesn’t need to be that big of a problem. I’m upgrading to NAV2017 at the moment, and thanks to the hooks pattern, the big redesign of CU80 isn’t that much of a problem. Decent code design always helps. And in case of extension, you would have been using events anyway .. so that’s even less of a problem.

What I would suggest for Microsoft (this is again a personal suggestion, not based on any info I have from MS) is to make the Testability framework a mandatory part of Extensions. Then at least, Microsoft or whoever does code-redesign or upgrade the base platform would have two basic steps to test whether there might be a problem for Extensions:

  • Does the Extension still install (and compile)
  • Does all business logic still execute as expected

If not – contact the author (probably the author was already informed about an upcoming upgrade and he is already working on it.. (like my display-adapter-driver-guy did)), who has a contract that he needs to provide an update in a certain amount of time, and let him fix it!

 

Do I have to change? Can I keep developing in C/SIDE? I’m a dinosaur – I don’t want to change

In NAV2017 you can do whatever you like. You can do Extensions only, or customizations-only .. Or even mixed mode (which I don’t think is a good idea ;-)). I can’t talk about the future .. I don’t know what you will be able to do and what not. I do know that Microsoft is “all in” for Extensions. As am I. I’m looking forward to the day that everything is in place … I really do. When – not “if” – I will be able to do custom development by solely Extensions, I will be a happy man ;-).

So, yes, at this moment, you can do without. But because competition will become faster and more cost efficient, they will soon be more competitive .. and if you still need to start the extension work and repeatability work then, it’s going to be hard to catch up!

 

Will classic On Premise NAV die eventually?

This concern is not directly related with “Extensions”. But “Extensions” is obviously very much to do with Dynamics 365. And apparently some people have the feeling this, in a near future, means we won’t be doing classic On Premise implementations anymore.

Wrong! NAV will stay. On Premise will stay. Microsoft will keep supporting partners doing that.

But make no mistake. The market is shifting. Microsoft gives you a fantastic product to shift with it: Dynamics 365. And it is obvious that this gets priority. That means: Extensions, Web Client, .. and everything that can leverage it. To quote Marko: “The wind is blowing towards Dynamics 365”.

 

All my developers have so much knowledge. Will that be wasted?

You know, one of my favorite quotes is one of Bill Gates: “IT knowledge has the shelf life of bananas“. In my eyes, a good developer is not the one with years of experience, or tons of knowledge. I even dare to say that “too much” experience and knowledge can hold a developer back .. .

In my opinion, a good developer is not only someone who is capable to thinking logical .. but more important, he or she is someone that is able to adapt. A “best practice” of today might be a “worst practice” tomorrow. That’s what is called “evolution”. And a good developer is able to see this and evolve with it.

And this doesn’t even only apply to developers only. Same for consultants, same for marketing .. same for any IT business.

Just my 0.02€

 

These were my views on some “concerns” that I picked up. You know, sometimes people just put “concerns” out there to try to prove that something sucks – while it is actually just marketing: “look how much it sucks, let me fix it for you .. “. Always take opinions with a decent pinch of salt (especially mine  ).

Microsoft Dynamics NAV Extensions – What steps to take (3/3)

$
0
0

Last blog of the “My-view-on-Extensions” series. If you haven’t read the previous ones, here you can find both:

So, after you struggled through these previous two posts .. what do I think we should do now .. . Well, before I do that, let’s talk a little bit about “Innovation

Let’s take cars as an example. Just totally from the top of my hat .. let’s take Porsche and Tesla. As the pictures illustrate – quite similar cars.

Now, we all know that we just can’t keep driving on fossil fuels. Dinosaurs are extinct. These fuels will as well run out at some point … and let’s not forget what they do to the environment! We know that .. but many of us neglect it, because numerous reasons. Sure, Tesla has got quite a price tag. But all in all, it’s the same TCO as an Audi A4 (in Belgium – calculated over 5 years) .. . It’s not per se the price tag that makes people not choose for Tesla or any EV for that matter. Many rather put more money in a Porsche, which smells and makes a lot of noise. While the TCO is much higher – and they are equally fast (well .. Tesla is much faster .. but let’s just assume Porsche did one thing right…:p).

Why? Because people tend to keep hold of what they know and are familiar with. In the world of EV cars, there are a few concerns:

  • Range anxiety: Tesla can only drive 350km – and when depleted, you need to wait for an hour to charge it. This is something we are not used to. “This is something that would change my way of driving. So this is bad… .” Well, you know, I was afraid for it as well when I bought my Tesla. But after 30000 km, I must say that I still didn’t lose one single minute at a gas station. How much time would that Porsche-driver have lost after 30k km, you think? Sometimes, concerns are based on assumptions – and we all know the danger of assumptions..
  • No noise: “I like the sound of my Porsche. Loud and all .. .” Well, I like the sound of mine: none. Nothing more relaxing than no noise. It’s strange in the beginning .. but let’s face it .. no noise is the sound of innovation. Something you’ll enjoy when you get used to it. It’s difficult to form a objective opinion of something you never experienced.
  • Battery Power Loss over time: I have seen Tesla’s with 200000 miles and only 6% degradation. I did NOT know that on the moment I ordered mine. Sometimes, you just need to have a little faith in a company – and reason like: that company (Tesla, Microsoft, …) only benefits from it when it’s successful. So they probably thought about stuff I’m concerned about.. .
  • Autopilot: “I like to drive myself – don’t want the car to drive for me”. Well, guess what, you still can drive yourself with a Tesla :-). You don’t HAVE to use every single bit of an innovative feature .. but you can! And most likely, it’s best that you do. Driving on Autopilot behaves better in traffic, because quite frankly, the autopilot doesn’t fall asleep, and always pays full attention. Don’t deny innovation – go for it!

Anyway, don’t know if this comparison makes sense to you. It does to me. This is how I am. I give innovation a chance. Extensions is innovation, and we’re in the middle of it. Change is usually for the best. So suck it up, accept it, stop whining and go with the flow!

So, what should we do now?

Well, as said, NAV 2017 Extensions are in my opinion good to go to create Extensions for Dynamics 365. Go for it, do it, don’t hold back :-). I know I’m gonna!

If you don’t have a Dynamics 365 approach, you need to determine for yourself if Extension development makes sense in your situation. I can see many opportunities where you can upsell your vertical in your multitenant offering. I can even see possibilities in On Premise business. I created an Extension for our product, which we are now “upselling” on premise. Across multiple different products even. Why not? It works! And because of the type of the extension (you can compare it with the WaldoNAVPad – although it’s not that one :-)) I know it’s never going to be a problem. Not in upgrading. And if so .. I’ll maintain it as expected.

And if you don’t see any use for it just yet, then still you should dive into it. Make yourself familiar! Not only with Extensions, but also with VSCode for example. We all know it’s coming. What I’m doing to make myself familiar with VSCode: I’m doing all my PowerShell in it. It’s not ideal for PowerShell (I miss my in-app shell – but the community is working on that, I read :-)), but I did learn some bits and pieces that makes my VSCode-life somewhat easier .. and guess what, by the time Microsoft releases this new development environment, VSCode will have no secrets for me (I hope)!

So, stop wasting time with reading this blog, and get yourself familiar with Microsoft Dynamics NAV Extensions!  Or like my good friend Daniel said: “move your feet or lose your seat”

Viewing all 336 articles
Browse latest View live