Tuesday, 12 September 2017

Breakpoint symbols not loading AX7/D365

Recently faced a problem where my breakpoints weren’t loading for objects in the AOT that were not in my solution and therefore I could not debug.
Solution: In order to load symbols for objects that have not been included in your solution you have to uncheck the following check box.
Dynamincs365>Options>Dynamics 365>Debugging
Load symbols only for items in the solution
BreakpointsNotLoadingD365

To know more about how to debug in D365 visit
https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/dev-tools/debug-xpp

Debugging without a startup project or startup Object in D365

To debug without setting up a startup project and startup object in D365 fotr operations. Follow these steps:
  1. Open your object in visual studio
  2. Place breakpoints
  3. Goto Debug>attach to process and check show processes from all users
  4. Select w3wp.exe and click attach.
DebugWithoutStartupObject
You can execute your process now from the client and execution will be stopped at your breakpoints.

How to deactivate a specific action of a workflow

Recall basically represents “Cancelation of an activity on purpose and manually” e.g. if you submitted a leave application for 10 days. And suddenly you realize that you have entered wrong date in your leave application that you just submitted via workflow. Yet, workflow is in-review. So, you have a chance to click Recall. Recall will allow you to edit record once more and you can submit record after modification.

Similarly workflow represent other actions such as Approval, Rejection, and Request Change and so on.

If for any reason you want to disable a specific activity of workflow such as Recall in my example, this can be achieved.

In our given example of recall, my client don’t want to give option of recall to end user, once submitted.

To achieve such a requirement we need to know one basic point i.e. every single action of workflow appears to you via a menu item. E.g. if approver of workflow is able to see Approve button, that is because of a menu item. Similarly Recall comes appears on UI via a menu item, via workflow framework.

Let’s suppose we want to disable Recall for Customer Payment Workflow. So to do that we will keep same theory in mind which we just described above.

Let’s dive into AOT.

First step which you need to do is that, you need to remove Cancel Menu Item for Workflow Type. You need to take workflow type which is for your workflow. For our given example our workflow type is named as CustPaymentTemplate, which is an existing workflow type for customer payment workflow. Have a look at next screenshot and see from where we removed canceled menu item.
  
Next you need to remove Cancel Event Handler for corresponding workflow Approval. Going back to our example of customer payment workflow, have a look at next screenshot, how we are removing canceled event handler.
  
That is all, what is needed to remove Recall button from workflow. Doing so will not show Recall button to submitter of workflow, from action strip, from where, he/she has submitted the workflow.

But wait a moment that does not ends the story, yet Submitter of workflow can Recall the workflow from workflow status History screen.
To make sure that submitter has no way to click Recall from anywhere, you need to do change so that workflow submitter cannot do action, from workflow history button. To achieve this, you need to customize underlying class named as WorkflowStatusForm. This class enables and disables the workflow history form controls. So accordingly depending upon what you want to disable you need to further add your custom logic in WorkflowStatusForm(Class)

Friday, 8 September 2017

Event Handlers in Dynamics AX 7 and Dynamics AX 365 - D365

Event Handlers in Dynamics AX 7 and Dynamics AX 365 - D365



If you want to create new event handler then have to create a class and define.

Example:

class VendTableHandler
{
[PostHandlerFor(tableStr(VendTable), tableMethodStr(VendTable, insert))] // Post even Handler

public static void VendTable_Post_insert(XppPrePostArgs _args)
{
VendTable vendTable = _args.getThis();

_args.setReturnValue(VendTable::exists(vendTable.AccountNum));
}

}

// For Pre event handler
// [PreHandlerFor(tableStr(VendTable), tableMethodStr(VendTable, insert))]

Note: once you created an event handler for a table method. You can see + symbol for the method left side.

Event handlers for Table methods in Dynamics 365 / AX7

Event handlers for table methods in Dynamics 365 / AX 7


In the below code, You can see a class which is included with one method for post event handling for ReqPO->ValidateQuantity() method.
According to the D365 concept, have to use the static method for event handler but, in this case, need to return value.
Using the below function we can return value even in a static method.

Below method is to execute after ReqPO->ValidateQuantity() method as a post event method.



Ref: Method for the above event method.

Thursday, 24 August 2017

Restrict Field Access By Security Role

https://dynamicsaxtipoftheday.com/2014/04/15/restrict-field-access-by-security-role/


Restrict Field Access By Security Role


The roles within Dynamics AX are pre-configured to restrict users from accessing areas that they usually don’t need to use, but you don’t have to stop there. You can tweak the security even more by restricting access to the field level data that shows up on the forms through the security role maintenance feature, allowing you to give people read only access, or not even show the field in the case of sensitive information. As an added benefit, if you restrict access this way, then the users will not be able to access the data through the reports, or through the Excel add-in.
If your users are already paranoid that they don’t see all of the data, then redacting access this way may push them over the edge.
How To Do It…
Start off by finding the field that you want to restrict access to. In this case we want to stop particular users from maintaining the Vendors Bank Account.
Then find the Role that you want to restrict access through – in this example we are cheating a little and finding the Role through the user account, an then clicking on the Edit Role button within the User Roles tab.
When the Security Roles maintenance form is displayed, click on the Override Permissions button within the menu bar.
This will open up the Override Permissions form. To override the permissions on a table, click on the Table/Fields node.
This will open up the Override Permissions form. To override the permissions on a table, click on the Table/Fields node.
This will allow you to browse all of the tables within Dynamics AX. Select the table that you want to restrict access to (in this case the VendTable) and expand it so that you can see all of the fields.
To override the permissions by field, select the parent table and then uncheck the Do Not Overridecheckbox.
This will allow you to override the general access to the table if you like. In this example though we will leave it with the Full control access level because we want to override the access to an individual field.
Now select the field that you want to restrict access to, and uncheck the Do Not Override checkbox.
Now you can change the access rights to that field by selecting the option from the Override Access Level dropdown box. You can set it to No AccessView, or Edit.
Now when the user returns to the form, they are not able to change the field if they belong to that security role.
Access Denied.

Wednesday, 26 July 2017

Text File import/ Export

class PrisonFileImport extends RunBaseBatch
{
    DialogField dialogImport;
    Dialog      dialog;
    CustTable   custTable;
    container   contain;
    AsciiIo     importFile;
    str         fileNameOnly;
    FilenameOpen fileNameOpen;

    #define.CurrentVersion(1)
    #define.Version1(1)
    #localmacro.CurrentList
        filenameOpen
    #endmacro
}
public boolean canGoBatch()
{
    boolean ret;

    ret = super();

    return ret;
}
                protected Object dialog()
{
    ;
    Dialog = super();
    //Set a title for dialog
    dialog.caption("@CIT263");
    dialogImport = dialog.addField(extendedTypeStr(FilenameOpen),"@CIT238","@CIT239");
    return dialog;
}
                public boolean getFromDialog()
{
    // Retrieve values from Dialog
    fileNameOpen = dialogImport.value();

    return super();
}
public container pack()
{
    return [#CurrentVersion,#CurrentList];
}
public void processData()
{
    TextIo txIoRead;
    FileIOPermission fioPermission;
    container containFromRead;
    int    record,iConLength;
    str    sOneRecord;

    int    customersUpdated = 0;
    int    customersNotFound = 0;
    //str    Y,N;
    str      prisonFlag;
    CustAccount custAccount;
    #File
    ;
    // Assert permission.
    fioPermission = new FileIOPermission(fileNameOpen ,#IO_READ);
    fioPermission.assert();

    // Open the same file for reading.
    txIoRead = new TextIo(fileNameOpen ,#IO_READ);
    // Read the entire file into a container.
    containFromRead = txIoRead.read();

    // Loop through the container of file records.
    while (containFromRead)
    {
        sOneRecord = "";
        iConLength = conLen(containFromRead);
        // Loop through the token in the current record.
        for (record=1; record <= iConLength; record++)
        {
            if (record > 1)
            {
                sOneRecord += " ";
            }
            sOneRecord += conPeek(containFromRead ,record);
        }

        custAccount = subStr(sOneRecord,1,10);
        prisonFlag = subStr(sOneRecord,12,1);

        custTable = CustTable::find(custAccount,true);

        ttsBegin;
        if(custTable)
        {
            if (prisonFlag=="Y")
            {
                custTable.PrisonFlag = NoYes::Yes;
            }
            else if (prisonFlag=="N")
            {
                custTable.PrisonFlag = NoYes::No;
            }
            custTable.update();
            customersUpdated++;
        }
        else
        {
            customersNotFound++;
        }
        ttsCommit;
        // Read the next record from the container.
        containFromRead = txIoRead.read();
    }
        info("@CIT261" + int2str(customersupdated));
        info("@CIT262" + int2str(customersNotFound));
    // Close the test file.
    txIoRead = null;
    // revertAssert is not really necessary here,
    // because the job (or method) is ending.
    CodeAccessPermission::revertAssert();
}
public void run()
{
    //super();
    this.processData();
}
                public boolean unpack(container packedClass)
{
    Version version = RunBase::getVersion(packedClass);
;
    switch (version)
    {
        case #CurrentVersion:
            [version,#CurrentList] = packedClass;
            break;
        default:
            return false;
    }

    return true;
}
public static void main(Args _args)
{
    prisonFileImport   prisonFileImport = new prisonFileImport();

    if (prisonFileImport.prompt())
    {
        prisonFileImport.run();
    }
}