Tuesday, December 27, 2016

Error in getting sid AX2012

Hi Guys,

I had recently setup a new environment and it was working fine. Then suddenly one of our team members reported that he is not able to post Purchase Packing Slip. The Packing Slip screen comes and when you press OK it just goes off and does nothing. He also got an error "Failed to create a session" but just once.

I was trying to set default company and I got "Error in getting SID" error. So after doing some hit and trial I reached a really silly solution. Whichever user is facing this issue just disable the UserId from SystemAdministrator->Common->Users->Users. Select the user, click "Edit", then uncheck the "Enabled" checkbox and save the record. and enable it. Now do the reverse and check the "Enabled" checkbox and save the record. Then ask him to re-login and check. And it works now. :)

Monday, December 19, 2016

Update SQLDictionary

When you have a TransactionDB from one environment and ModelDB from another, you will face DBSync issues. This is a very common problem where SQLDictionary in TransDB refers to the objects from older ModelDB hence it throws the error with TableId/FieldId mismatch or Typecasting issue.
The below job is used to update the SQLDictionary in some case when you have issues in DBSync:

static void fixTableAndFieldIds(Args _args)
{
    Dictionary dictionary = new Dictionary();
    SysDictTable dictTable;
    DictField dictField;
    TableId tableId;
    FieldId fieldId;
    SqlDictionary sqlDictionaryTable;
    SqlDictionary sqlDictionaryField;

    setPrefix("Update of data dictionary IDs");
    tableId = dictionary.tableNext(0);
    ttsbegin;

    while (tableId)
    {
        dictTable = new SysDictTable(tableId);

        setPrefix(dictTable.name());

        if (!dictTable.isSystemTable() && !dictTable.isView())
        {
            //Finds table in SqlDictionary by name in AOT, if ID was changed.
            //Empty field ID represents a table.
            select sqlDictionaryTable
                where sqlDictionaryTable.name == dictTable.name()
                && sqlDictionaryTable.fieldId == 0
                && sqlDictionaryTable.tabId != dictTable.id();

            if (sqlDictionaryTable)
            {
                info(dictTable.name());
                //Updates table ID in SqlDictionary
                if (ReleaseUpdateDB::changeTableId(
                    sqlDictionaryTable.tabId,
                    dictTable.id(),
                    dictTable.name()))
                {
                    info(strFmt("Table ID changed (%1 -> %2)", sqlDictionaryTable.tabId, dictTable.id()));
                }
            }

            fieldId = dictTable.fieldNext(0);

            //For all fields in table
            while (fieldId)
            {
                dictField = dictTable.fieldObject(fieldId);

                if (!dictField.isSystem())
                {
                    //Finds fields in SqlDictionary by name and compares IDs
                    select sqlDictionaryField
                        where sqlDictionaryField.tabId == dictTable.id()
                        && sqlDictionaryField.name == dictField.name()
                        && sqlDictionaryField.fieldId != 0
                        && sqlDictionaryField.fieldId != dictField.id();

                    if (sqlDictionaryField)
                    {
                        //Updates field ID in SqlDictionary
                        if (ReleaseUpdateDB::changeFieldId(
                            dictTable.id(),
                            sqlDictionaryField.fieldId,
                            -dictField.id(),
                            dictTable.name(),
                            dictField.name()))
                        {
                            info(strFmt("Pre-update: Field %1 - ID changed (%2 -> %3)",
                                dictField.name(),
                                sqlDictionaryField.fieldId,
                                -dictField.id()));
                        }
                    }
                }
                fieldId = dictTable.fieldNext(fieldId);
            }

            fieldId = dictTable.fieldNext(0);

            //For all fields in table
            while (fieldId)
            {
                dictField = dictTable.fieldObject(fieldId);

                if (!dictField.isSystem())
                {
                    select sqlDictionaryField
                        where sqlDictionaryField.tabId == dictTable.id()
                        && sqlDictionaryField.name == dictField.name()
                        && sqlDictionaryField.fieldId < 0;

                    if (sqlDictionaryField)
                    {
                        //Updates field ID in SqlDictionary
                        if (ReleaseUpdateDB::changeFieldId(
                            dictTable.id(),
                            sqlDictionaryField.fieldId,
                            -sqlDictionaryField.fieldId,
                            dictTable.name(),
                            dictField.name()))
                        {
                            info(strFmt("Final update: Field %1 - ID changed (%2 -> %3)",
                                dictField.name(),
                                sqlDictionaryField.fieldId,
                                -sqlDictionaryField.fieldId));
                        }
                    }
                }
                fieldId = dictTable.fieldNext(fieldId);
            }
        }
        tableId = dictionary.tableNext(tableId);
    }
    ttscommit;
}

Reference: http://sashanazarov.blogspot.in/2012/09/id-change-in-dynamics-ax-data-dictionary.html

Wednesday, December 14, 2016

Display method on InventOnHandItem form

Hi guys,

Today I faced a very typical issue while adding a new display field in InvnetOnHandItem. I had to show the sum of a quantity from a customized table based on ItemId and Warehouse. Now the problem here was the grouping between InventSum and InventDim due to which I was not getting the value of InventDimId in InventSum.

So after a lot of failed techniques, I found a solution to use the InventDim values and the code is below:

display public InventQtyReservOrdered ale_PendingVerificationQty(InventSum _inventSum)
{
    InboundWebOrderLine     inboundWebOrderLine;
    InventDim               joinDim, dimValues;
    InventDimParm           dimParm;

    dimValues.data(_inventSum.joinChild());
    dimParm.initFromInventDim(dimValues);

    select sum(QtyOrdered) from inboundWebOrderLine
        where inboundWebOrderLine.ItemId == _inventSum.ItemId
        && (inboundWebOrderLine.EcomMerchantLocation == dimValues.InventLocationId
        || inboundWebOrderLine.EcomMerchantLocation != '')
        && inboundWebOrderLine.EcomBridgeOrderItemStatus == EcomBridgeOrderItemStatus::Ordered;

    return inboundWebOrderLine.QtyOrdered;
}

This method worked like a charm and a had sigh of relief. :)

Sunday, October 9, 2016

buf2buf to buf2bufByName in Ax2012

Hi Guys,

I would like to share an interesting blog post regarding buf2buf wherein you can also copy table buffer to another table in which field names are same as the same i.e table structure should be similar of both the tables.

We can create below method in the Global class:

//Modified method, copy data from one table to another table with similar structure

static void buf2BufByName(Common  _from, Common  _to)
{
    DictTable   dictTableFrom   = new DictTable(_from.TableId);
    DictTable   dictTableTo     = new DictTable(_to.TableId);
    DictField   dictFieldFrom;
    FieldId     fieldIdFrom     = dictTableFrom.fieldNext(0);
    FieldId     fieldIdTo
    ;


    while (fieldIdFrom && ! isSysId(fieldIdFrom))
    {
        dictFieldFrom   = new DictField(_from.TableId, fieldIdFrom);


        if(dictFieldFrom)
        {
            fieldIdTo = dictTableTo.fieldName2Id(dictFieldFrom.name());


            if(fieldIdTo)
                _to.(fieldIdTo) = _from.(fieldIdFrom);
        }


        fieldIdFrom = dictTableFrom.fieldNext(fieldIdFrom);
    }
}

Source: http://mybhat.blogspot.in/2012/07/dynamics-ax-buf2buf-and-buf2bufbyname.html

Friday, September 23, 2016

Redirect WMS page to Login Screen AX2012

Hi Guys,

We had a requirement with a client wherein they wanted to see the WMS login screen rather than the WMS Homepage to save that 1 extra click ;)

So to achieve this I had to make a small change in the "Index.aspx" file located at "C:\Program Files (x86)\Microsoft Dynamics AX\60\Warehouse Mobile Devices Portal\00\Views\Home".

The change was a small script to redirect the page:

<script type="text/javascript">
setTimeout('Redirect()',10);
function Redirect()
{
  location.href = 'http://wmsServerName:port/Execute/Display';
}
</script>

So this script will take you directly from 








To this screen:


Thursday, August 18, 2016

Create User from another user using X++ AX2012

While searching some issue I have a very interesting blog post about a utility which creates a new user based on an existing user. It copies the Roles and User Options from the existing user and creates a new one.

This utiltiy not limited to copy user roles but can copy User Groups and user Options too.



Some important methods of this utility are;

Create User lookup for FROM USER

public void fromUserLookup(FormStringControl userLookupControl)
{
    Query                   qry = new Query();
    QueryBuildDataSource    qbd;
    QueryBuildRange         qbr;
    SysTableLookup          sysTableLookup;
    Userinfo                userInfo;


    systablelookup = SysTableLookup::newParameters(tableNum(UserInfo), userLookupControl);
    SysTableLookup.addLookupfield(fieldNum(UserInfo, ID));
    SysTableLookup.addLookupfield(fieldNum(UserInfo, NetworkAlias));
    SysTableLookup.addLookupfield(fieldNum(UserInfo, Name));

    qbd = qry.addDataSource(tableNum(userInfo));
    qbd.addRange(fieldNum(UserInfo,Company)).value(curext());
    sysTableLookup.parmQuery(qry);
    sysTableLookup.performFormLookup();
}

Create User lookup for TO USER

public void toUserLookup(FormStringControl userLookupControl)
{
    Query                   qry = new Query();
    QueryBuildDataSource    qbd;
    QueryBuildRange         qbr;
    SysTableLookup          sysTableLookup;
    Userinfo                userInfo;


    systablelookup = SysTableLookup::newParameters(tableNum(UserInfo), userLookupControl);
    SysTableLookup.addLookupfield(fieldNum(UserInfo, ID));
    SysTableLookup.addLookupfield(fieldNum(UserInfo, NetworkAlias));
    SysTableLookup.addLookupfield(fieldNum(UserInfo, Name));

    qbd = qry.addDataSource(tableNum(userInfo));
    qbd.addRange(fieldNum(UserInfo,Company)).value(curext());
    qbr = qbd.addRange(fieldNum(UserInfo, Enable));
    qbr.value('1');
    sysTableLookup.parmQuery(qry);
    sysTableLookup.performFormLookup();
}

Function to copy USER ROLES

/// <summary>
/// copy roles assigned to one user to another
/// </summary>
/// <returns>
/// true; roles are copied across. false; roles failed to copied
/// </returns>
/// <remarks>
/// this method is used to copy user roles assigned to one user to another user.
/// </remarks>
private boolean copyUserRoles()
{
    boolean                 ret = true;

    SecurityRole            securityRole;

    SecurityUserRole        securityUserRole;
    SecurityUserRole        securityUserRoleExist;
    SecurityUserRole        securityUserRoleInsert;
    OMUserRoleOrganization  userRoleOrganization, userRoleOrganization_Insert;

    List                    copiedUserRoles = new List(Types::String);

    ListEnumerator          lEnumerator;

    setPrefix(strFmt("Copy user", fromUser, toUser));

    try
    {
        select securityRole where securityRole.AotName == 'SystemUser';
        delete_from securityUserRole where securityUserRole.User == toUser && securityUserRole.SecurityRole == securityRole.RecId;
   
        while select securityUserRole
                where securityUserRole.User == fromUser
            notExists join * from securityUserRoleExist
                where securityUserRoleExist.SecurityRole    == securityUserRole.SecurityRole
                    && securityUserRoleExist.User           == toUser
        {
            select securityRole where securityRole.RecId == securityUserRole.SecurityRole;

            copiedUserRoles.addStart(securityRole.Name);

            securityUserRoleInsert.initValue();
            securityUserRoleInsert.SecurityRole = securityUserRole.SecurityRole;
            securityUserRoleInsert.User         = toUser;
            securityUserRoleInsert.insert();
            securityUserRoleInsert.clear();

            while select userRoleOrganization
                    where userRoleOrganization.User == fromUser
                        && userRoleOrganization.SecurityRole == securityUserRole.SecurityRole
            {
                userRoleOrganization_Insert.initValue();

                userRoleOrganization_Insert.OMHierarchyType             = userRoleOrganization.OMHierarchyType;
                userRoleOrganization_Insert.OMInternalOrganization      = userRoleOrganization.OMInternalOrganization;
                userRoleOrganization_Insert.SecurityRole                = userRoleOrganization.SecurityRole;
                userRoleOrganization_Insert.SecurityRoleAssignmentRule  = userRoleOrganization.SecurityRoleAssignmentRule;
                userRoleOrganization_Insert.User                        = toUser;

                userRoleOrganization_Insert.insert();
                userRoleOrganization_Insert.clear();
            }
        }
    }
    catch
    {
        ret = false;
    }

    if (ret)
    {
        lEnumerator = copiedUserRoles.getEnumerator();

        if (copiedUserRoles.empty())
            info(strFmt("User %1 and %2 have already the same user role",fromUser, toUser));

        while (lEnumerator.moveNext())
        {
            info(strFmt('%1',lEnumerator.current()));
        }
    }
    else
        error(strFmt("User Roles aborted please review list"));

    return ret;
}

Function to copy USER OPTIONS
/// <summary>
/// copy options assigned to one user to another
/// </summary>
/// <returns>
/// true; options are copied across. false; options failed to copied
/// </returns>
/// <remarks>
/// this method is used to copy user's options assigned to one user to another user.
/// </remarks>
private boolean copyUserOptions()
{
    boolean                 ret = true;

    UserInfo                userInfoSource;
    UserInfo                userInfoTarget;

    SysUserInfo             sysUserInfoSource;
    SysUserInfo             sysUserInfoTarget;

    setPrefix(strFmt("Copy user options", fromUser, toUser));

    try
    {
        select userInfoSource
            where userInfoSource.id == fromUser
        join sysUserInfoSource
            where sysUserInfoSource.Id == userInfoSource.id;

        ttsBegin;
       
            select forUpdate userInfoTarget where userInfoTarget.id == toUser;
            userInfoTarget.filterByGridOnByDefault = userInfoSource.filterByGridOnByDefault;
            userInfoTarget.statuslineInfo = userInfoSource.statuslineInfo;
            userInfoTarget.update();

       
            select forUpdate sysUserInfoTarget where sysUserInfoTarget.Id == toUser;
            sysUserInfoTarget.DefaultCountryRegion = sysUserInfoSource.DefaultCountryRegion;
            sysUserInfoTarget.update();
        ttsCommit;

    }
    catch
    {
        ret = false;
    }

    if (ret)
    {
        info(strFmt("User %1 and %2 have already the same user options ", fromUser, toUser));
    }
    else
        error(strFmt("User Options aborted please review list "));

    return ret;
}

Function to copy USER GROUPS
/// <summary>
/// copy groups assigned to one user to another
/// </summary>
/// <returns>
/// true; groups are copied across. false; groups failed to copied
/// </returns>
/// <remarks>
/// this method is used to copy user groups assigned to one user to another user.
/// </remarks>
private boolean copyUserGroups()
{
    boolean                 ret = true;

    UserGroupList           userGroupList;
    UserGroupList           userGroupListExist;
    UserGroupList           userGroupListInsert;

    List                    copiedGroups = new List(Types::String);

    ListEnumerator          lEnumerator;

    setPrefix(strFmt("Copy user groups", fromUser, toUser));

    try
    {
        while select userGroupList
                where userGroupList.userId == fromUser
            notExists join * from userGroupListExist
                where userGroupListExist.groupId == userGroupList.groupId
                    && userGroupListExist.userId == toUser
        {
            copiedGroups.addStart(userGroupList.groupId);

            userGroupListInsert.initValue();
            userGroupListInsert.groupId = userGroupList.groupId;
            userGroupListInsert.userId  = toUser;
            userGroupListInsert.insert();
            userGroupListInsert.clear();
        }
    }
    catch
    {
        ret = false;
    }

    if (ret)
    {
        lEnumerator = copiedGroups.getEnumerator();

        if (copiedGroups.empty())
            info(strFmt("User %1 and %2 have already the same user Groups ",fromUser, toUser));

        while (lEnumerator.moveNext())
        {
            info(strFmt('%1',lEnumerator.current()));
        }
    }
    else
        error(strFmt("User Groups aborted please review list "));

    return ret;
}

Credit: http://daxture.blogspot.in/2015/01/assigning-security-roles-to-new-ax-user.html

Tuesday, August 16, 2016

Refresh AOD, Data and Dictionary using X++ AX2012

Using the below code you can refresh AOD, Dicitonary and Data:


static void refreshAll()
{
    #Aif
    SysSQMSettings  _sqmSettings;

    ttsBegin;
    update_recordSet _sqmSettings setting GlobalGUID = str2guid(#EmptyGuidString);
    ttsCommit;

    xSession::removeAOC();
    SysTreeNode::refreshAll();
    SysFlushSystemSequence::doFlush(); SysEvent::fireEvent(SysEventType::FlushSystemSequence);

    SysFlushDictionary::doFlush(); SysEvent::fireEvent(SysEventType::FlushDictionary);

    SysFlushDatabaseLogSetup::doFlush(curext()); SysEvent::fireEvent(SysEventType::FlushDatabaseLogSetup,0,[curext()]);
    SysFlushData::doFlush(); SysEvent::fireEvent(SysEventType::FlushData);
    SysFlushAOD::doFlush();
    SysEvent::fireEvent(SysEventType::FlushAOD);
    xSession::updateAOC();
}

Change menuitem property through X++ AX2012

In a new requirement, I had to generate a URL based on the logged-in user using a new parameters table. The URL had to be opened from EP->EmployeeServices and hence it is a URLMenuItem I was not able to handle it through URLRedirection in C#.

What I decided to change the URL property of the menuitem through code. Below is the code:

private void leaveURL(Str60 encryptedUserName)
{
    Name        encryptedName;
    TreeNode    objTreeNode;
    objTreeNode = TreeNode::findNode(@"\Web\Web Menu Items\URLs\ALE_ESSLeave\");

    if (objTreeNode)
    {
        objTreeNode.AOTsetProperties("PROPERTIES\n URL  #" + 'http://' + HcmSharedParameters::find().ALE_ESS_IP + '/oasis/AX_ESS_Integration.aspx?U=' + encryptedUserName +'&C=' + HcmSharedParameters::find().ALE_LeaveEncryptedCode + "\n ENDPROPERTIES\n");
        objTreeNode.AOTsave();
    }
}

Add/Remove Role to User through X++ code AX2012

Hi guys. We had a requirement where I had to provide SysAdmin role to the current user for the task which he is performing and once it is done then the role had to be removed. As ever google came to help big time and finally my code is ready:

Method to add role:

private void grantAdminRole()
{
    SecurityRole        role;
    SecurityUserRole    userRole;
    boolean             added;
    UserInfo            userInfo;

    select role where role.Name == 'System Administrator';

    select * from userRole
        where userRole.SecurityRole == role.RecId &&
            userRole.User == curUserId();

    if (!userRole || (userRole.AssignmentStatus != RoleAssignmentStatus::Enabled))
    {
        userRole.User = curUserId();
        userRole.SecurityRole = role.RecId;
        userRole.AssignmentMode = RoleAssignmentMode::Manual;
        userRole.AssignmentStatus = RoleAssignmentStatus::Enabled;
        SecuritySegregationOfDuties::assignUserToRole(userRole, null);
    }
}


Method to remove the role from user:

private void revokeAdminRole()
{
    fieldName                           userId;
    SysSecTreeRoles                     roleTree;
    SecurityUserRole                    securityUserRole;
    OMUserRoleOrganization              org;
    SecurityUserRoleCondition           condition;
    SecuritySegregationOfDutiesConflict conflict;
    SecurityRole                        role;

    ttsbegin;

    select role where role.Name == 'System Administrator';

    delete_from condition
        exists join securityUserRole
        where condition.SecurityUserRole == securityUserRole.RecId && securityUserRole.User == curUserId() && securityUserRole.SecurityRole == role.RecId;

    select OMInternalOrganization, SecurityRole from org where org.User == curUserId() && org.SecurityRole == role.RecId;

    if (org.SecurityRole)
    {
        EePersonalDataAccessLogging::logUserRoleChange(org.SecurityRole, org.omInternalOrganization, curUserId(), AddRemove::Remove);
    }

    delete_from org where org.User == curUserId() && org.SecurityRole == role.RecId;

    delete_from conflict where conflict.User == curUserId() && ((conflict.ExistingRole == role.RecId) || (conflict.NewRole == role.RecId));

    //<GEEEE>
    EePersonalDataAccessLogging::logUserRoleChange(role.RecId, 0, curUserId(), AddRemove::Remove);
    //</GEEEE>

    delete_from securityUserRole where securityUserRole.User == curUserId() && securityUserRole.SecurityRole == role.RecId;

    ttscommit;

}

Friday, July 8, 2016

Update ProductDimension, StorageDimension & SearchName of Items/Products

We had a requirement to update ProductDimension and StorageDimension of all the items. I wrote 2 jobs for the same. Below are those:

Update ProductDimension:
static void UpdateProductDimension(Args _args)
{
    EcoResProduct                       ecoResProduct;
    EcoResProductDimensionGroupProduct  ecoResProductDimensionGroupProduct;
 
    while select ecoResProduct
        where ecoResProduct.RecId != 0
    {
        ecoResProductDimensionGroupProduct.initFromProduct(ecoResProduct);
        ecoResProductDimensionGroupProduct.ProductDimensionGroup = 'NameHere';
        ecoResProductDimensionGroupProduct.insert();
    }
 
    info("Done");
}

Update StorageDimension:
static void Job48(Args _args)
{
    EcoResProduct                       ecoResProduct;
    EcoResStorageDimensionGroup         ecoResStorageDimensionGroup;
    EcoResStorageDimensionGroupProduct  ecoResStorageDimensionGroupProduct;
    EcoResStorageDimensionGroupItem     ecoResStorageDimensionGroupItem;
    InventTable                         inventTable;
 
    select Name from ecoResStorageDimensionGroup
        where ecoResStorageDimensionGroup.Name == 'NameHere';
 
    while select ecoResProduct
        join inventTable
        where ecoResProduct.RecId == inventTable.Product
    {
        ecoResStorageDimensionGroupItem.initValue();
        ecoResStorageDimensionGroupItem.ItemDataAreaId = inventTable.dataAreaId;
        ecoResStorageDimensionGroupItem.ItemId = inventTable.ItemId;
        ecoResStorageDimensionGroupItem.StorageDimensionGroup = ecoResStorageDimensionGroup.RecId;
        ecoResStorageDimensionGroupItem.insert();
    }
 
    info("Done");
}

Update SearchName:
static void Job48(Args _args)
{
    EcoResProductTranslation    ecoResProductTranslation;
    InventTable                 inventTable;
    EcoResProduct               ecoResProduct;
 
    update_recordset inventTable
    setting NameAlias = ecoResProductTranslation.Name
    join ecoResProduct
    where ecoResProduct.RecId == inventTable.Product
    join ecoResProductTranslation
    where ecoResProductTranslation.Product == ecoResProduct.RecId
    && ecoResProductTranslation.Name == 'Bentinck One Seater Sofa in Provincial Teak Finish with Mudramark';
 
    info("Done");
}

ReferenceSource: http://fandyax.blogspot.in/2013/04/how-to-using-x-create-item-in.html


Checklist can't be skipped Ax2012

Today I faced an issue where I had installed a Hotfix and completed the checklist but even after that the checklist kept popping up.

Then I came to know about the table which controls it. It's named "ReleaseUpdateConfiguration" and there is a field "MinorUpgrade" which will be checked. So in this case we need to uncheck it and the magic happens!

Source: http://yetanotherdynamicsaxblog.blogspot.in/2014/03/skip-modelstore-has-been-modified-dialog.html

Monday, June 13, 2016

Could not load file or assembly 'Microsoft.ReportingServices.RdlObjectModel, Culture=neutral, PublicKeyToken=' or one of its dependencies. Access is denied. ax2012

Today I faced a new error while running reports in AX. "Could not load file or assembly 'Microsoft.ReportingServices.RdlObjectModel, Culture=neutral, PublicKeyToken=' or one of its dependencies. Access is denied. ax2012"

The solution to this is to give permission to all the users on the .NET folder:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files” ( Modify the framework version, root directory etc according to your environment. )

Goto folder properties, select security, select "USERS" and grant full control. Press Apply and OK.


Credit: https://blogs.msdn.microsoft.com/sayanghosh/2007/04/21/solution-to-could-not-load-file-or-assembly-or-one-of-its-dependencies-access-is-denied/

Sunday, May 8, 2016

Change design of Document report

I had to change the design of PO report today and I followed the usual way of mentioning my design in the controller class.
But then realized that it's not working and found that I need to make a change of design from the "FormLetterReport" class.

There is a method named "loadPrintSettings" where you can mention the design change for these document report like PO, GRN etc.

Wednesday, April 27, 2016

Filter on display method using context method

You may find a lot of codes for the same purpose but the one which worked flawlessly for me is written below:

Write the below code in the executeQuery method of the root DS:

public void executeQuery()
{
super();

if(!queryObj)
{
queryObj = true;
existQuery = new Query(this.query());
existQuery.saved();
remFilQuery = new Query();
remFilQuery = existQuery;
remFilQuery = existQuery.makeCopy();
}
}

Now the context method in the display method control:
public void context()
{
int selectedMenu;
formrun fr;
Args ag;
Name strtext;
str filSel = ”;
querybuilddataSource qb1, qb2, qb3;
queryrun qr;
query q, q2;
PopupMenu menu = new PopupMenu(element.hWnd());
int a = menu.insertItem(‘Filter by field’);
int b = menu.insertItem(‘Filter by selection’);
int c = menu.insertItem(‘Clear’);
;

selectedMenu = menu.draw();
switch (selectedMenu)
{
case a: //Filter by field
ag = new args(‘SysformSearch’);
fr = new formrun(ag);
fr.run();
fr.wait();

//Reading User entered value for filter process
strtext = fr.design().controlName(‘FindEdit’).valueStr();
//strtext = ‘”‘ + fr.design().controlName(‘FindEdit’).valueStr() + ‘”‘;
if(strtext)
{
//Creating a query for filter
q = new Query(InventSerial_ds.query());
// existQuery = InventJournalTrans_ds.query();

qb1 = q.dataSourceTable(tablenum(InventSerial));

qb2 = qb1.addDataSource(TableNum(EcoResProduct));
qb2.addLink(FieldNum(InventSerial,ItemId),FieldNum(EcoResProduct,DisplayProductNumber));

qb3 = qb2.addDataSource(tableNum(EcoResProductTranslation));
qb3.relations(true);

qb3.addRange(fieldNum(EcoResProductTranslation, Name)).value(strtext);
qb3.addRange(fieldNum(EcoResProductTranslation, LanguageId)).value(infolog.language());

InventSerial_ds.query(q);
InventSerial_ds.executeQuery();

}

break;

case b: // Filter By Selection3

q = new Query(InventSerial_ds.query());

qb1 = q.dataSourceTable(tablenum(InventSerial));

qb2 = qb1.addDataSource(TableNum(EcoResProduct));
qb2.addLink(FieldNum(InventSerial,ItemId),FieldNum(EcoResProduct,DisplayProductNumber));

qb3 = qb2.addDataSource(tableNum(EcoResProductTranslation));
qb3.relations(true);

filSel = ‘”‘ + ItemDesc.valueStr() + ‘”‘;

qb3.addRange(fieldNum(EcoResProductTranslation, Name)).value(filSel);
qb3.addRange(fieldNum(EcoResProductTranslation, LanguageId)).value(infolog.language());

InventSerial_ds.query(q);
InventSerial_ds.executeQuery();

break;

case c : // Remove Filter

InventSerial_ds.query(remFilQuery);
InventSerial_ds.executeQuery();

break;

Default:
break;
}
}

Credits: https://msddax.wordpress.com/2014/11/29/filter-on-the-display-method-using-context-method/

Tuesday, April 26, 2016

Multiple designs in Print Mgmt

I had to attach a new design to the Product Receipt report (PurchPackingSlip) which would run through a new menu item.

I tried to override the design by checking the calling menu item in the controller class but it always seemed to be picking from Print Management setup. Then I came across this:

If you have new formats for Sales/Purchase/Quotation confirmation/Picking/Packing/Invoice..., following these steps :

Step1. Create new Design for report under visual studio.

Step2. Add code to method:
\Data Dictionary\Tables\PrintMgmtReportFormat\Methods\populate
Add code  before TTSCOMMIT:
addOther(PrintMgmtDocumentType::SalesOrderPackingSlip, ssrsReportStr(SalesPackingSlip, ReportUD), ssrsReportStr(SalesPackingSlip, ReportUD), #NoCountryRegionId);    

Step 3. Choose you new format under:
AR -> setup -> form setup -> Print management -> Sales order Packing slip original -> report format to  SalesPackingslip.ReportUD

Step 4.
New Report design can be executed from use Print management from Inquiry journal forms or during posting by selecting Print management destination.

Step 5. (Optional)
In case: the report still keep original design, add this code to class TradeDocumentReportController in method outputReport:
//original menu item or your new menu item
if(args.menuItemName() == menuitemOutputStr(SalesPackingSlipOriginal))
{
formLetterReport.getCurrentPrintSetting().parmReportFormatName(ssrsReportStr(SalesPackingSlipOriginal, ReportUD));
}

Credits: http://axvuongbao.blogspot.in/2014/04/how-to-add-multiple-report-design-under.html

Thursday, March 31, 2016

Remove HTML tags of Text in EP

I had to retrieve a text value stuck in HTML tags. It was the JobAdText field in HRMRecruitingJobAd table. This field has values like "<BODY>Some description</BODY>" and I had to get the value "Some description".

You can use the below code:

Literal jobAd = this.rightGroup.FindControl("html_JobAd") as Literal;
jobAd.Text = HtmlUtilities.CleanupUnsafeHtml((string)currentJobAdRecord.GetFieldValue("HRMRecruitingJobAd!jobAdText"));

You can take the reference from the standard web control "HcmEPOpenJobsListPagePreviewPart" where the same code is used.

Suggestions and feedback welcome :)

Navigation pane issue in EP

I faced an issue with a new web control (put on Employee Services) which was showing Navigation pane when viewed through Employee Services module but when user goes to some other page and come back the navigation pane was not visible.

The problem looked weird that why would a page do it when the "Module" property on the PageDef was set to Home\EmployeeService!

But in both the cases the URL opening was different. Below are both URLs:
Working:
http://server/sites/DynamicsAx/EmployeeServices/Enterprise%20Portal/Ale_HcmOpenJobs.aspx?WMI=Ale_HcmOpenJobs&WebModuleName=Home%5cEmployeeServices%5cOrganization&WCMP=GCO&WDPK=initial

Non-Working:
http://server/sites/DynamicsAx/EmployeeServices/Enterprise%20Portal/Ale_HcmOpenJobs.aspx?WMI=Ale_HcmOpenJobs&WCMP=GCO&WDPK=initial

So I found a solution which I must say can be a workaround with no other impact.

Just go and set the "ShowParentModule" property to "No" on the node of the WebMenu where the control is added and it will work.

Tuesday, March 22, 2016

Repeat table header SSRS

Sounds easy going by the usual way of Tablix properties and check the "Repeat header rows on each page". Strangely this didn't work for me today so I found out a new way for same.

Under Design Pane - > at the bottom Column Groups - > click on drop down (down arrow) - > Advanced Mode.



Once we selected Advanced Mode, we will be able to notice (static) in Row Groups and Column Groups at the bottom of Design pane .



Under Design Pane - > Row Groups - > click on (static) - > Press F4 - > Properties window will pop-up.

In the Properties window - >Set KeepWithGroup = After and RepeatOnNewPage = True.

If you want the header to be frozen while scrolling down the report, set FixedData = True.




Credit: http://social.technet.microsoft.com/wiki/contents/articles/19398.ssrs-how-to-repeat-headers-on-each-page.aspx

Tuesday, March 8, 2016

Disable/Hide Modules from Ax2012

Today I found an interesting way to disable modules from AX for all users. One of our clients had asked us to do so for them. There are ways which we all are aware of like disabling configuration keys, disable licenses or maybe through code.

I had simply put a different country code (other than the base country) to the modules need to be hidden. When we do this those modules will only be available to that specific country whose code is attached. So this worked perfectly in our case. :)

Sunday, March 6, 2016

AxForm insert not working

Strange is the word when it comes to EP. Today I faced an issue where there is a web control with an AxForm with 2 buttons "OK" and "Cancel" but info is not getting inserted into the table on "OK" button click. The biggest confusion was that this web control was working in Dev environment but not in UAT. So I imported the object from Dev to UAT but to no help.

Then started observing the DataSet's properties and found a property called "InsertIfEmpty", by default it was "Yes". So I changed it to "No" and it worked.

Now the question was "why it worked in Dev not in UAT?". Dev already had some records in the table and UAT was newly created DB.

Tuesday, February 16, 2016

The "deploytoreportsservertask" failed unexpectedly. SSRS deployment fails.

Please GOTO Computer properties and follow the below steps:


Credits: http://axwiki.blogspot.com/2015/04/deploying-ssrs-from-vs-2010-solved.html#comment-form

Tuesday, February 9, 2016

SQL Server 2008 error (Operating system error 2(The system cannot find the file specified.)

I faced this error while installing and configuring Sharepoint 2013 Foundation for a client. I tried installing SP 2013 Foundation and it succeeded but the SQL Server component had failed even after many attempts. So I installed SQL Server 2008 Express with SP2 first and then installed SP 2013 Foundation. Now the installation was successful but I kept getting an error when trying to restart SQL service. the exact error was:

FileMgr:tartLogFiles: Operating system error 2(The system cannot find the file specified.) occurred while creating or opening file 'e:\sql10_main_t\sql\mkmastr\databases\objfre\i386\modellog.ldf'. Diagnose and correct the operating system error, and retry the operation.


So after few hours of installing/uninstalling and tweaking I got a solution:


NET START MSSQL$SQLEXPRESS /f /T3608


SQLCMD -S .\SQLEXPRESS

1>SELECT name, physical_name, state_desc FROM sys.master_files ORDER BY database_id;

Now notice those wrong file names, and run following commands ...

Note: you need to change the file name location.


1>ALTER DATABASE model MODIFY FILE ( NAME = modeldev, FILENAME = 'c:\model.mdf');
2>ALTER DATABASE model MODIFY FILE ( NAME = modellog, FILENAME = 'c:\modellog.ldf');
3> go

ALTER DATABASE msdb MODIFY FILE ( NAME = MSDBData, FILENAME = 'c:\MSDBData.mdf');
ALTER DATABASE msdb MODIFY FILE ( NAME = MSDBLog, FILENAME = 'c:\MSDBLog.ldf');

ALTER DATABASE tempdb MODIFY FILE ( NAME = tempdev, FILENAME = 'c:\temp.mdf');
ALTER DATABASE tempdb MODIFY FILE ( NAME = templog, FILENAME = 'c:\temp.ldf');

go

exit;

NET STOP MSSQL$SQLEXPRESS 





Changing the paths as described above worked, but still I was not able to log in.

My solution:

I started the service with NET START again and listed the users:
select loginname from master..syslogins
My windows account was missing, so I have added it:>
CREATE LOGIN [COMPUTERNAME\USERNAME] FROM WINDOWS;
Then I was able to login with Management Studio with Windows Authentication.


Still not being able to use the 'sa' account with the password supplied during the installation. Solution: login as 'sa' and leave the password empty. Then I could change the password to desired value.


Credit: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/54bbcac3-41c5-4a5d-a4f6-2669e538dc82/sqlserver-2008-express-install-error?forum=sqlexpress

Not able to login to SQL

Scenarios when this might happen:

You are working as a trusted DBA responsible for some extremely important SQL Servers for your company. For the sake of security, you have performed the following steps to secure SQL Servers:

You have removed any and all built-in administrators account from SQL Server logins
You have removed all the users (except SA) that were part of SYSADMIN server role (Including any Windows Accounts and/or SQL Server logins)
You have set the password of SA to something extremely complex which is hard to remember.
For day-to-day operations on SQL Server, you use your domain user account which has DBO permissions on couple of databases but doesn’t have SYSADMIN privileges.
Since you set the SA password to be complex and you have not been using it, you forgot the SA password. You are the only person in the company who would know the SA password and now you have lost the SA password.

What would you do now?

Some quick options I can think of are listed below:

1. You will try to look for the SA password on your computer hard-drive or in your emails (If you stored it in some file which is a bad practice)

2. You will rebuild Master database or reinstall SQL Server and attach all the user databases. However, this could take some time and also doesn’t guarantee that all your logins, users, permissions and server configurations will be recovered unless you plan to restore the Master database from an old backup. However, as you don’t remember the SA password, restoring the Master database will not help you and you are back to square one.

3. You will call up Microsoft PSS

You are now running out of options. What would you do?

There’s a way with which you can gain SYSADMIN access to your SQL Server. However, that would mean your Windows account will need to be a member of the local administrators group.

SQL Server allows any member of Local Administrators group to connect to SQL Server with SYSADMIN privileges.

Here are the steps you will need to perform:

1. Start the SQL Server instance using single user mode (or minimal configuration which will also put SQL Server in single user mode)

From the command prompt type: SQLServr.Exe –m (or SQLServr.exe –f)



Note: If the Binn folder is not in your environmental path, you’ll need to navigate to the Binn folder.

(Usually the Binn folder is located at: C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\Binn)

2. Once SQL Server service has been started in single user mode or with minimal configuration, you can now use the SQLCMD command from command prompt to connect to SQL Server and perform the following operations to add yourself back as an Admin on SQL Server instance.

SQLCMD –S <Server_Name\Instance_Name>

You will now be logged in to SQL Server as an Admin.

3. Once you are logged into the SQL Server using SQLCMD, issue the following commands to create a new account or add an existing login to SYSADMIN server role.

To create a new login and add that login to SYSADMIN server role:

1> CREATE LOGIN ‘<Login_Name>’ with PASSWORD=’<Password>’

2> go

1> SP_ADDSRVROLEMEMBER '<Login_Name>','SYSADMIN'

2>go

To add an existing login to SYSADMIN server role, execute the following:

1> SP_ADDSRVROLEMEMBER ‘<LOGIN_NAME>’,’SYSADMIN’

The above operation will take care of granting SYSADMIN privileges to an existing login or to a new login.

4. Once the above steps are successfully performed, the next step is to stop and start SQL Server services using regular startup options. (This time you will not need –f or –m)

Note: Those that might be thinking this might make it easy for anyone to get access to SQL Server, well remember that you do have Auditing and will have control of who gets access to the local servers administrators group. If you haven't enable controls at that level then you may have bigger security issues in hand!!!

Credit: http://blogs.technet.com/b/sqlman/archive/2011/06/14/tips-amp-tricks-you-have-lost-access-to-sql-server-now-what.aspx

SQL single user mode (DAC: Dedicated Administrator Connection)

If you are stuck with SQL Single server connection mode then you can use DAC (Dedicated Administrator Connection) to the rescue:
  1. Go to New Query.
  2. Type the following into the Server Name dialog box: ADMIN:ServerName\InstanceName (eg: ADMIN:MSSQL\SQLDEV).
  3. Now you can run any scripts needed against your database in Single User Mode.

System restart from previous installation or update is pending

Today while installing Sharepoint 2013 Foundation in our web server I faced an error which says: 
System restart from previous installation or update is pending. Restart your computer and run setup to contiue.


I tried restarting but it didn't work and I understood that it's a trap so started what I am good at "Googling" for solution. Here it is:

  • Open the Registry editor (run > regedit)
  • Navigate to this path “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager“
  • Rename the “PendingFileRenameOperations” value to “OldPendingFileRenameOperations“

Note: Don't reboot your machine after this registry hack because if you do then it will again create a key named "PendingFileRenameOperations" and you will be stuck in this vicious circle.

Credit: https://social.technet.microsoft.com/Forums/office/en-US/f29b066e-9cd5-4a71-9db1-77ba01e40173/a-system-restart-from-a-previous-installation-or-update-is-pending?forum=sharepointadmin

Saturday, February 6, 2016

Hide parameters in Report dialog Ax2012

There are different ways to do it. Some of them are:



Method 1:

Why the default ranges will come in Dialog?:

Because in the Query used it will show the index fields of the Table, so to hide the Default ranges you have to overload the showQueryValues() in the Controller class: return FALSE.

Credit: http://axdevhelper.blogspot.com/2012/09/how-to-hide-default-query-parameters-in.html


Method 2:

The View group fields comes from InventDimViewContract class which is added as a parm method in ProdPickListContract class to show the options for all inventory dimensions.

Suppose, we want to hide all the parameter groups from report dialog and just want to display the dynamics filters on the report. You need to modify the UIBuilder class of your report. For production picking list report, the UI Builder class is ProdPickListUIBuilder. The build method needs to be modified and the code in Bold below can be used to hide the whole parameter group from SSRS report dialog:

public void build()
{
FormBuildGroupControl grp;
grp = this.dialog().curFormGroup();
grp.frameType();
grp.columns(2);

if (this.controller().parmArgs().menuitemName() == #yourMenuItemName)
{
grp.visible(false);
}
super ();
}

To hide the dynamic filters from SSRS report dialog based on caller menu item, you need to override the following method in your controller class and return false from this method based on your condition:

showQueryValues

Override this method in your controller class and write the following code:

public boolean showQueryValues(str parameterName)
{
If (this.parmArgs().menuItemName() == menuItemOutputStr(#YourMenuItemName)
{
return false;
}
else
{
return true;
}
}

Credits: https://syedbaber.wordpress.com/2015/07/23/hiding-parameter-groups-or-dynamics-filters-on-ssrs-report-dialog-at-runtime/

Method 3:
If you use a report controler class, you can overwrite the showQuerySelectButton method and return false.


Method 4 :
If you remove the ranges from the modeled query, and add them back programmatically in the methods of the query, they also will not show up as parameters for the report.


Method 5:

contract parameters are can definately be hidden . Look at any report that uses srstmpTableMarshaller class (Pseudo tmp table report's ). or any document reports . Journal record id is generally passed from controller --> Design (via parameter value in contract class) -- >DP class.


But still it's not visible on the report. All you need to is set its property to hidden in VS.
I feel its more like issue with the usage data.
Once you set the property to hidden you need delete the report from report manager and then redeploy, remove usage data, delete records in SYSLastValue, SRSReportQuery restart Reporting services.

Method 6:

Allow dynamic filtering false and remove the dynamic parameter. redeploy the report and refresh the user cache.


Method 7:

You can also use a not defined group.
SysOperationGroupMemberAttribute(‘group_notdefined_at_classdeclaration’)

The group ‘group_notdefined….’ will not be shown on the dialog, so all its members are
invisible.

Credits: https://community.dynamics.com/ax/f/33/t/83017

https://community.dynamics.com/ax/f/33/p/155614/364546#364546


Now one of them would surely help :)

Friday, January 29, 2016

Finding out which tables were updated by an operation in AX

Today while searching for some issue I stopped at a very interesting and neat trick in AX. Using this trick you can get a list of tables which get updated while performing any operation. I am sharing the blog post as is here with the source:

At times when troubleshooting Microsoft Dynamics AX you need to know which tables were updated by a particular operation, for example after performing a posting. This could be if you want to be sure that all the right tables are updated correctly after an upgrade, if you have unexpected results on one customer/vendor etc or if you have made an update the system and you want to be sure that everything is ok.
It is possible to use the SQL trace for this, but for functional people it can be at times be a bit challenging to interpret these results, so here is a tip for how you can easily find out which tables were updated when performing an operation in AX. In this example, we will show the effect on tables from a Sales Invoice posting in Microsoft Dynamics AX 2012 as this is quite a complex process that affects many tables.
The whole technique here relies on using a standard report ‘Size of company accounts’ in a novel way. This report returns an output of all the tables in AX that have data in them. In this article, we will see how we can use this report to print a snapshot of the tables before and after an operation so that when we print it ‘after’ we will see which tables got data populated into them from an AX operation.


The steps are as follows
1.  Go to the AOT and locate the Class ‘SysCompanySizeDP’ and open up the method called ‘insertIntoTempTable’. Change the highlighted function from ‘tableid2Pname’ to instead ‘tableid2name’. The reason we are changing the function is that it is easier to locate the tables in the AOT when the report prints the tables using the technical names. If we didn’t do this, the report would, for example, print the name of a table like ‘Customer Invoice Journal’ instead of ‘CustInvoiceJour’
2. Now go to System Administration\Reports\Database\Size of company accounts and run the report. Note the technical names

3. Export this report to Excel

4. Open up the saved Excel sheet and notice that the report content is copied over
5. Now let’s make a sales order invoice posting
6. We go back to System Administration and run the company size report again and once more save it, this time under a different name, for example ‘Company Size 2’

7. Now we copy the tables in the sheet from the last run ‘Company size report’ to the first saved Company size Excel sheet

So that we have a sheet with 2 different ‘Company size’ report executions – Before and after the Sales Order invoice posting

8. Now add a new column ‘Difference’ with a simple calculation extracting cell ‘C’ from cell ‘H’ and copy it across the rows as highlighted on
the screenshot in red below. Notice also the black box, that shows already some tables that have been affected

9. Now in order to easily identify which tables were affected by the posting, we can do this trick, apply a filter in the ‘Difference’ column
and unmark ‘blanks’ in the display of the cells’ values
RESULT
We can now see easily which tables were updated by the Sales Order posting. The same technique can naturally be used by any other kind of operation in AX, such as a Journal posting or a Master Planning run – all it relies on is just tracing which tables were updated since the last run of the report. Have fun!


Source: https://blogs.msdn.microsoft.com/axsupport/2013/09/27/finding-out-which-tables-were-updated-by-an-operation-in-ax/