Thursday, 18 April 2019

Cross reference alternative D365 FO

FindStr


Today we are going to look interesting stuff, which can be very handy for the developers. This is nothing but, FindStr windows command, which searches for the patterns of text in files.


As everyone is aware of the thing that, in D365FO all the objects i.e. Tables, forms, classes, etc are stored as files (XML's). So it makes sense that, we use can use the Windows command to find the files. 

Now we will see how we can use the FindStr command. 

1. Open command prompt (as administrator)
2. Change to Package directory (CD ) Ex: C: cd K:\AosService\PackagesLocalDirectory\XXXModel\XXXModel
3.  FindStr /S /L /I  /C:"serachText"*.xml

it will give you the result similar to cross-reference 

Sunday, 3 March 2019

Form Lookup Event Handler D365FO

Here we are going to see how we can filter / override the lookup on the form control.

Scenario, we need to override the TemplateID lookup based on the ProjGroupId field.

1. First we need to copy the OnLookup event on the form field control.


2. Create a new class and past the above event
 ///
    ///
    ///
    ///
    ///
    [FormControlEventHandler(formControlStr(MkQuoteCreate, TemplateId), FormControlEventType::Lookup)]
    public static void TemplateId_OnLookup(FormControl sender, FormControlEventArgs e)
    {
//
    }
 
3. Now the actual lookup code.

 FormRun                     callerFormRun;
        MKQuotations          quotations;
//Get the Data source of the form
        quotations = sender.dataSourceObject().cursor();
        Query                       query = new Query();
        QueryBuildDataSource        queryBuildDataSource;
        QueryBuildRange             queryBuildRange;
        SysTableLookup              sysTableLookup = SysTableLookup::newParameters(tableNum(MKTemplateTable), sender);
        sysTableLookup.addLookupField(fieldNum(MKTemplateTable, TemplateId));
        sysTableLookup.addLookupField(fieldNum(MKTemplateTable, Description));
        queryBuildDataSource = query.addDataSource(tableNum(MKTemplateTable));
        queryBuildRange = queryBuildDataSource.addRange(fieldNum(MKTemplateTable, ProjGroupId));
        queryBuildRange.value(quotations.ProjGroupId);
        sysTableLookup.parmQuery(query);
        sysTableLookup.performFormLookup();

 FormControlCancelableSuperEventArgs ce = e as FormControlCancelableSuperEventArgs;
        //cancel super() to prevent error.
        if (ce)
        {
            ce.CancelSuperCall();
        }


A few things we can note here are...
1. To get the Datasource cursor we need to use the "sender" parameter as below.

quotations= sender.dataSourceObject().cursor();
Eg:
 ecoResProduct   = sender.formRun().dataSource(formDataSourceStr(EcoResProductDetailsExtended, EcoResProduct)).cursor();





   

Tuesday, 11 December 2018

Message: Duplicate type with name 'Dynamics.Ax.Application.XXXX' in assembly 'Dynamics.Ax.Application, Version=6.3.5000.138, Culture=neutral, PublicKeyToken=null'.

Scenario: 

Trying to deploy Inbound custom service using HTTP adapter.

Issue:

IIS Application pool stops automatically and not stay in start mode.

Below is the event viewer 

There was an error during processing of the managed application service auto-start for configuration path: 'MACHINE/WEBROOT/APPHOST/Default Web Site/MicrosoftDynamicsAXAif60'. The error message returned is: 'An initialization error occurred while trying to preload an application.

Exception: System.Configuration.ConfigurationErrorsException

Message: Duplicate type with name 'Dynamics.Ax.Application.xxxxxxx' in assembly 'Dynamics.Ax.Application, Version=6.3.5000.138, Culture=neutral, PublicKeyToken=null'.

StackTrace:    at System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective)
   at System.Web.Configuration.CompilationSection.LoadAllAssembliesFromAppDomainBinDirectory()
   at System.Web.Configuration.CompilationSection.LoadAssembly(AssemblyInfo ai)
   at System.Web.Compilation.BuildManager.GetReferencedAssemblies(CompilationSection compConfig)
   at System.Web.Compilation.BuildManager.GetPreStartInitMethodsFromReferencedAssemblies()
   at System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath, Boolean& isRefAssemblyLoaded)
   at System.Web.Compilation.BuildManager.ExecutePreAppStart()
   at System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException)

InnerException: System.BadImageFormatException

Message: Duplicate type with name 'Dynamics.Ax.Application.AMS_ItemImportContract' in assembly 'Dynamics.Ax.Application, Version=6.3.5000.138, Culture=neutral, PublicKeyToken=null'.

StackTrace:    at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
   at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.Load(String assemblyString)
   at System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective)'.  The worker process will be marked unhealthy and be shutdown.  The data field contains the error code.


Solution: 

This solution worked for me, but I'm not sure if it works for every one.

1. Goto IIS Manager
2. Click on application pools
3. Select MicosoftDynamicsAXAif60
4. Right click  and select Basic Settings.
5. In the Edit application pool form select .NET CLR version
6. From the drop-down select V2.0xxxx
7. Now start the Application pool.
8. Now goto AX and try to deploy the AIF service.
9. Now change the .NET CLR version back to V4.0xxxx

It has worked for me and hope will work for you also, let me know in the comments.

Hope this will solve the issue.


Thursday, 6 December 2018

Item Service in AX 2012- This document does not support the AxEcoResProductIdentifier class or AxEcoResProductCategory class

Item Service in AX 2012- This document does not support the AxEcoResProductIdentifier class or AxEcoResProductCategory class


This is just my reference, thanks to original post 

In AX 2012's Item service as part of creating an Item/Product through AIF Service if you try to populate entities EcoResProductIdentifier, EcoResProductCategory you may receive these errors: 

This document does not support the AxEcoResProductIdentifier class 
or 
This document does not support the AxEcoResProductCategory class 

You may very well receive these errors after going through the AIF wizard and regenerating the Item Service (InventTable and EcoResProduct), the reason being wizard doesn't generate some code related to all the Tables involved in the AxdQuery.

To fix this, I had to add following code in class -> AxdEcoResProduct-> prepareForSave()

       case classNum(AXEcoResProductIdentifier):
            axecoResProductIdentifier   = _axdStack.top() as AxEcoResProductIdentifier;
            axEcoResProduct             = axecoResProductIdentifier.parentAxBC() as AxEcoResProduct;
            axecoResProductIdentifier.parmProduct(axEcoResProduct.parmRecId());
            return true;       
        
        case classNum(AxEcoResProductCategory):
            axEcoResProductCategory     = _axdStack.top() as AxEcoResProductCategory;
            axEcoResProduct             = axEcoResProductCategory.parentAxBC() as AxEcoResProduct;
            axEcoResProductCategory.parmProduct(axEcoResProduct.parmRecId());
            return true;   

Tuesday, 23 October 2018

Create Credit note using X++ AX2012

In the below example we are going to create a credit note for a Sale order which is already invoiced.

For this, we need to create a class and add the below method.
In this method, we are getting the data from a temp table mkCreditNote or any other source like CSV, WCF etc.
public void postCreditnote( RecId               _creditnoteRecid)
{
    CustInvoiceTrans        custInvoiceTrans;
    SalesCopying            salesCopying;
    boolean                 isCreditNoteCreated,isCreditNotePosted;
    SalesId                 salesid;
    SalesTable              salesTable;
    MKCreditNote           mkCreditNote;
    TmpFrmVirtual           tmpFrmVirtualHeader;
    TmpFrmVirtual           tmpFrmVirtualLines;

    void loadTmpVirtualTableAndCreateCN(CustInvoiceTrans _custInvoiceTrans,MKCreditNote _mkCreditNote,SalesTable _salesTable)
    {
        delete_from tmpFrmVirtualLines;
        tmpFrmVirtualLines.TableNum = _custInvoiceTrans.TableId;
        tmpFrmVirtualLines.RecordNo = _custInvoiceTrans.RecId;
        tmpFrmVirtualLines.Id = _custInvoiceTrans.SalesId;
        tmpFrmVirtualLines.LineNum = _custInvoiceTrans.LineNum;
        tmpFrmVirtualLines.TransDate = _custInvoiceTrans.InvoiceDate;
        tmpFrmVirtualLines.Qty = _mkCreditNote.Qty;

        tmpFrmVirtualLines.write();

        salesCopying = SalesCopying::construct(SalesPurchCopy::CreditNoteLines);
        salesCopying.initParameters(_salesTable,
                                    tmpFrmVirtualLines,
                                    tmpFrmVirtualHeader,
                                    1,
                                    true,
                                    false,
                                    true,
                                    true,
                                    false);

        salesCopying.copy();
    }

    select firstOnly salesid, RecId from mkCreditNote
    where mkCreditNote.RecId == _creditnoteRecid;
    if (mkCreditNote.RecId)
    {
        salesTable = SalesTable::find(amsCreditNote.SalesId,false);
        if (salesTable.RecId)
        {
            while select amsCreditNote
                join custInvoiceTrans
                where custInvoiceTrans.SalesId == mkCreditNote.SalesId
                && custInvoiceTrans.ItemId == mkCreditNote.ItemId
                && custInvoiceTrans.InventDimId == mkCreditNote.InventDimId
                && custInvoiceTrans.InventTransId == mkCreditNote.InventTransId
                && amsCreditNote.RecId == _creditnoteRecid
            {
                isCreditNoteCreated = true;
                loadTmpVirtualTableAndCreateCN(custInvoiceTrans,mkCreditNote,salesTable);
            }
        }
    }


}

Monday, 8 October 2018

Pass Date and Real (double) datatype from C# to AIF (Sales order)

This is a tricky thing to do. Let's say you are creating a sales order using AIF and you wanted to test it using WSDL from C#.

Everything until this point seems to be very easy, now let's say you have some custom field of type Real and you want to pass a value to that field.

Here comes the trick, for any real or date type variables we need to explicitly specify that we are passing these field values, then only these values will appear in the inbound XML file.

Now how do we do that?
Below is the code sample, as you can see there are two lines for adding MK_PurchPrice. One we will specify that this filed value is going to pass by using MK_PurchPriceSpecified = true and the second line is the actual value. i.e MK_PruchPrice = xxxx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using createSalesOrderAIF.ServiceReference1;

namespace createSalesOrderAIF
{
    class Program
    {
        static void Main(string[] args)
        {
            var line = new AxdEntity_SalesLine()
            {
                MK_PurchPriceSpecified = true,
                MK_PurchPrice = Convert.ToDecimal(100.20),
                ItemId = "2476619",
                SalesQty = 20,
                SalesUnit = "ea",
                SalesPriceSpecified = true,
                SalesPrice = 200
               
            };

            var order = new AxdEntity_SalesTable()
            {
                CustAccount = "C5001",
                PurchOrderFormNum = "xyz",
                MK_VendAccount = "V1310",
               
                ReceiptDateRequestedSpecified = true,            
                ReceiptDateRequested = DateTime.Now.Date,
                MKTotalGSTSpecified = true,
                MKTotalGST = Convert.ToDecimal(20.10),
                MK_PurchInvoiceId = "Inv_25",
                SalesLine = new AxdEntity_SalesLine[] { line }
            };

            var orderList = new AxdEntity_SalesTable[] { order };
            var callContext = new CallContext() { Company = "ZW01" };
            var client = new SalesOrderServiceClient();

            try
            {
                client.create(callContext, orderList);
                client.Close();
            }
            catch
            {
                client.Abort();
                throw;
            }
        }
    }
}

Thursday, 4 October 2018

Using Delegates in the new Dynamics 365 AX (AX 7)

Here we are going to see how we can use the "Delegates".

Background: 
delegates: X++ delegates expose the publisher-subscriber pattern where a delegate defines a clear contract in a publisher class. It is a great way of encapsulating a piece of code. Delegates were introduced in AX 2012 couple of years back. With the new AX release, delegates are the recommended way for customizing standard AX classes.

Example
Now we will see an example of how we can use delegates in the new D365 to develop customizations. 

here we will see how to customise standard AX sales order confirmation process. To keep it simple we will the change value of a field in confirmation journal table. The idea here is how to develop delegates and call them.

Confirmation journal header data is stored in CustConfirmJour table and it is initialised in the below class method during the sales confirmation posting process. Let’s modify it using delegates.




First we create a new delegate method in this class. This serves the purpose of defining a contract between the delegate instance and the delegate handler. There is no business logic inside the delegate method. Also notice that the delegates have return type as Void. In order to access the result value we have to pass EventHandlerResult object as a parameter.
 
 

 
Now we modify the actual method, declare the Event handler result object and call the delegate with the parameters in the method which we need to customise . The only customization in this method is 2 lines of code as highlighted below:
 
 

 
The class structure looks as below:



 Now we create the event handler method and this is where MS has done really nice stuff in moving the AX development environment to Visual Studio. Right click on the delegate method and copy the handler method definition.


Create a new class which will be used to subscribe to the delegate. So we create a new class, let call is salesConfirmJournalExt and paste the copied clipboard text
 
 

The delegate handler definition is automatically added with the below information:
 
 

Now we can add our custom code in this method. I just changed the purchase order field value and added some Infolog. Note that I am actually not returning anything in this method and not using the eventHandlerResult object really.


 So we are done. The see it wokring let's build the solution and confirm a sales order.



During the process the  Infolog messages we added in the delegate handler method are shown 

The confirmation journal has the custom text appended to it in the field we used in our new class method.
 
 

Microsoft strongly recommends to use Delegates for customization due to all the good reasons of having minimum code changes in standard product. So try to use delegates to have a cleaner and manageable solutions.

How to Disable “Advanced Filter or Sort” and Enforce Custom Filters on Any D365FO Form

 In Dynamics 365 Finance and Operations, users can apply filters through the “Advanced filter or sort” feature found under the Options tab...