Quantcast
Channel: Microsoft Dynamics AX - Technical
Viewing all 126 articles
Browse latest View live

SysInfoAction class / Go to main table from Infolog message in AX 2009 / AX 2012:

$
0
0

The SysInfoAction class provides the ability to add an action to the standard infolog.

The user can then perform the action by clicking the infolog. The SysInfoAction class can be extended to perform custom actions e.g. opening a file using the default application.

Examples:

The example below uses the SysInfoAction_Formrun class and will open the "Accounts receivable parameters" form if the user clicks the infolog. 

static void AV_TestSysInfoAction_FormRun(Args   _args)
{
    SysInfoAction_FormRun    infoAction = SysInfoAction_FormRun::newFormName(formStr(CustParameters));
    ;

    infoAction.parmDescription("Open parameters");
    info("The parameters have not been entered.", "", infoAction);
}

The Infolog System can be easily customized in Dynamics AX. You can attach actions to the displayed messages in the infolog. Upon double clicking the message you can go to another form, related to the displayed message.

Solution: use SysInfoAction_TableField class
Using the SysInfoAction_TableField class, you can easily add this functionality in your displayed messages. The job beneath shows you the example for this functionality. Double clicking will open the Customer form on the Customer determined in the X++ code. (Instead of double clicking, even you could click the Show button in the Infolog). 

static void MainTableFromInfoMessage(Args _args)
{
    CustTable       ct;
    args            args = new args();
    ;   
    select ct
        where ct.AccountNum     == "1101";
    {
        args.record(ct);
        info(strfmt("Customer account is: %1", ct.Name), "", Sysinfoaction_tablefield::newBuffer(ct));
    }
}


Happy Daxing...!!J 

Understanding Address Framework Technically

$
0
0
LogisticsPostalAddress (Address): This table stores the actual physical postal address.
Postal Address have 2 important fields ValidFrom, ValidTo. This means that there could be multiple entries.

LogisticsElectronicAddress (Contact): This table stores all the contacts. Contacts are of 2 types
·         Contacts linked directly to Customer/Vendor.
·         Contacts linked to a Postal Address.

In Microsoft Dynamics AX, you add postal and electronic address information to entities such as customer, vendor, or contact person by using the DirPartyPostalAddressView and DirPartyContactInfoView views.

Table
Description
DirPartyTable
Global address book. This will contain entries for all people and organizations 
you deal with, including customers, suppliers, employees, etc.
This information is maintained across the entire organization. NB the table structure often refers to address book entries as 'parties'. Generally other records (like customer, supplier, etc) will reference a record in this table by a field named Party.
LogisticsLocation
This is a single 'location' that can be attached to one or more address book entries. This is similar in principle to the old 'Address' table from Ax2009, that no longer exists - The main difference now being that the location header always points to an address book entry, whereas in 2009 the Address table could point to anything.

Note that this is not an address - Physical address details are stored in
LogisticsPostalAddress
LogisticsPostalAddress
A postal address, linked to a LogisticsLocation record via field Location.
LogisticsElectronicAddress
'Electronic' address details, such as email, phone, web address etc.

Each different type of address is represented as a separate record, delineated by 'Type'. This links to the location record.
DirPartyLocation
This table links entries in the LogisticsLocation table to an address book entry (DirPartyTable).
LogisticsLocationRole
This defines types of roles that an address are classified as, such as "Delivery", "Invoice", etc.
DirPartyLocationRole
Links a location role type (LogisticsLocationRole) and an address book entry (DirPartyTable)
DirPartyPostalAddressView (view)
This is a view that collates address book entries with their linked postal adresses



Simple job to understand the relation between DirPartyLocation and DirPartyTable. Creation of postal address and contact info is shown below:

static void AddressJob(Args _args)  // X++ job.
{

    // Declare variables: views.
    DirPartyContactInfoView contactInfo;
    DirPartyPostalAddressView postalAddress;

    // Declare variables: tables.
    ContactPerson contactperson;
    DirPartyTable partyTable = DirPartyTable::findByName("Contoso", DirPartyType::Organization);
    LogisticsPostalAddress logisticsPostalAddressInfo;

    // Declare variables: classes.
    ContactPersonEntity contactPersonEntity;
    LogisticsLocationEntity entity;

    // Declare variables: extended data types.
    Phone phone;

    // Declare variables: primitives.
    str firstName;
    str middleName;
    str lastName;
   
    // Create and populate the contact person.
    contactPersonEntity = ContactPersonEntity::construct(contactPerson);
    contactPersonEntity.parmFirstName('Contact');
    contactPersonEntity.parmMiddleName('M.');
    contactPersonEntity.parmLastName('Person');
    contactPersonEntity.parmAssistantName('AssistantName');
    contactPersonEntity.parmBillingInformation('Billing info');
    contactPersonEntity.parmCharacter('Character description');
    contactPersonEntity.parmComputerNetworkName('Computer network name');
    contactPersonEntity.parmContactForParty(partyTable.RecId);
    contactPersonEntity.parmContactMemo('Memo');
    contactPersonEntity.parmContactPersonId('CP61');
    contactPersonEntity.parmLoyalty('Loyalty');
    contactPersonEntity.parmMileage('Mileage');
    contactPersonEntity.parmOfficeLocation('Office location');
    contactPersonEntity.parmOutlookCategories('Outlook categories');
    contactPersonEntity.parmProfession('Profession');
    contactPersonEntity.parmSensitivity(smmSensitivity::Personal);
    contactPersonEntity.parmSpouse('Spouse');
    contactPersonEntity.parmTimeAvailableFrom(1000);
    contactPersonEntity.parmTimeAvailableTo(2000);
    contactPersonEntity.write();

    // Populate the postal address information by using the view.
    postalAddress.Street = 'One Microsoft Way';
    postalAddress.City = 'Redmond';
    postalAddress.State = 'WA';
    postalAddress.ZipCode = '98052';
    postalAddress.CountryRegionId = 'US';




    // Update the postal address information.
    contactPersonEntity.createOrUpdatePostalAddress(postalAddress);

    // Populate the contact information by using the view.
    contactInfo.Locator = '555-555-5555';
    contactInfo.Type = LogisticsElectronicAddressMethodType::Phone;
    contactInfo.IsPrimary = true;
    // Update the contact information.
    contactPersonEntity.createOrUpdateContactInfo(contactInfo);
   
    // Verify that the data was stored correctly.
    firstName = contactPersonEntity.parmFirstName();
    middleName = contactPersonEntity.parmMiddleName();
    lastName = contactPersonEntity.parmLastName();
   
    logisticsPostalAddressInfo = entity.getPostalAddress();
    phone = contactPersonEntity.getPrimaryElectronicAddressLocation().getPhone();

    info(firstName + "" + middleName + "" + LastName +
        " is located at " + logisticsPostalAddressInfo.StreetNumber +
        "" + logisticsPostalAddressInfo.Street + ", " +
        logisticsPostalAddressInfo.City + ", " +
        logisticsPostalAddressInfo.State + "" +
        logisticsPostalAddressInfo.ZipCode +
        ". They can be contacted at " + phone + ".");
}



Note:
Ø  LogisticsPostalAddressView consists of LogisticsPostalAddress and LogisticsLocation.
Ø  DirPartyPostalAddressView consists of LogisticsPostalAddressView and DirPartyLocation.
Ø  DirPartyLocation consists of Party and Location.


Retrieve Customer/Vendor address:

static void CustomerAddressBook (Args _args)
{
    CustTable               custTable;
    DirPartyTable           dirPartyTable;
    DirPartyLocation        partyLocation;
    LogisticsLocation       logisticsLocation;
    LogisticsPostalAddress  postalAddress;
    ;
    custTable       = custTable::find('Test1001'); // Customer account id
    dirPartyTable   = dirPartyTable::findRec(custTable.Party);
    while select partyLocation
        where   partyLocation.Party     == dirPartyTable.RecId
    {
        logisticsLocation = logisticsLocation::find(partyLocation.Location);       
        if(logisticsLocation.IsPostalAddress)
        {
            postalAddress = LogisticsPostalAddress::findByLocation(logisticsLocation.RecId);           
            info(strFmt("%1 - %2",
                logisticsLocation.Description,
                postalAddress.CountryRegionId));
        }       
    }
}

  


Get Email address:     
             
static void CustomerEmailAddresses(Args _args)
{
    CustTable                   custTable;
    DirPartyTable               dirPartyTable;
    DirPartyLocation            partyLocation;
    LogisticsLocation           logisticsLocation;
    LogisticsElectronicAddress  electronicAddress;
    ;
    custTable       = custTable::find('Test1001'); // Customer account id
    dirPartyTable   = dirPartyTable::findRec(custTable.Party);
    while select partyLocation
        where   partyLocation.Party     == dirPartyTable.RecId
    {
        logisticsLocation = logisticsLocation::find(partyLocation.Location);       
        while select electronicAddress
            where   electronicAddress.Location  == logisticsLocation.RecId
            &&      electronicAddress.Type      == LogisticsElectronicAddressMethodType::Email
        {           
            info(strFmt("%1",electronicAddress.Locator));
        }       
    }
}




Get Phone number from warehouses:

static void PhoneNumbersAttachedToWarehouse(Args _args)
{

    InventLocation                      inventLocation;
    LogisticsEntityPostalAddressView    postalAddressView;
    LogisticsElectronicAddress          elecAddress;
    LogisticsLocation                   contactLocation;
   
    inventLocation = inventLocation::find('NB');
   
    if(inventLocation)
    {
        while select postalAddressView 
            where   postalAddressView.Entity            == inventLocation.RecId
            &&      postalAddressView.EntityType        == LogisticsLocationEntityType::Warehouse
        {               
            while select elecAddress               
                where   elecAddress.Type                == LogisticsElectronicAddressMethodType::Phone
            join contactLocation                                   
                where   contactLocation.ParentLocation  == postalAddressView.Location
                &&      contactLocation.RecId           == elecAddress.Location
            {           
                info(elecAddress.Locator);  
            }                  
        }
    }

}



Here is the static method which I've written to make the life easier for the developer, to find the already existing combination of street, city, zip code, state, country etc.
If it finds the existing data in the tables then it will return the ‘LogisticsPostalAddress’buffer else it creates the new record in the LogisticsPostalAddresstable using the below logic.       

publicstatic LogisticsPostalAddress retrieveMatchingPostalAddress(
    Description                     _locationName,
    LogisticsAddressStreet          _street,
    LogisticsAddressCity            _city,
    LogisticsAddressCountyId        _county,
    LogisticsAddressZipCodeId       _zipCode,
    LogisticsAddressStateId         _state,
    LogisticsAddressCountryRegionId _countryRegionId,
    LogisticsPostalAddressRecId     _originalPostalAddress   = 0
    )
{
    LogisticsPostalAddress              ret;
    LogisticsPostalAddressEntity        postalAddressEntity = new LogisticsPostalAddressEntity();
    LogisticsPostalAddressView          postalAddressView;
    LogisticsPostalAddress              originalPostalAddress;
    LogisticsLocation                   originalLocation;
    boolean                             createAddress = false;

    if (_originalPostalAddress != 0)
    {
        originalPostalAddress   = LogisticsPostalAddress::findRecId(_originalPostalAddress);
        originalLocation        = LogisticsLocation::find(originalPostalAddress.Location);

        if (originalLocation.Description            == _locationName
        && originalPostalAddress.Street             == _street
        && originalPostalAddress.City               == _city
        && originalPostalAddress.ZipCode            == _zipCode
        && originalPostalAddress.State              == _state
        && originalPostalAddress.County             == _county
        && originalPostalAddress.CountryRegionId    == _countryRegionId)
        {
            ret = originalPostalAddress;
        }
        else
        {
            createAddress = true;
        }
    }
    else
    {
        createAddress = true;
    }

    if (createAddress)
    {
        postalAddressView.LocationName      = _locationName;
        postalAddressView.Street            = _street;
        postalAddressView.City              = _city;
        postalAddressView.ZipCode           = _zipCode;
        postalAddressView.State             = _state;
        postalAddressView.County            = _county;
        postalAddressView.CountryRegionId   = _countryRegionId;

        ret = postalAddressEntity.createPostalAddress(postalAddressView);
    }

    return ret;
}



Counting of inventory On-Hand in AX2012

$
0
0

Guys, sometime it’s required to do a counting of inventory and in some case if you want to do it technically, I've created a static method which will be helpful to create a counting history and related voucher posting (Inventory count).

Front end navigation: Inventory On-Hand >> Quantity adjustment














Code:

publicstaticvoid adjustInventoryCount(
                                ItemId              _item,
                                InventLocationId    _inventLocation,
                                WMSLocationId       _wmsLocation,
                                Qty                 _qtyCounted
                                )
{
    WMSOnlineCountingServer     wmsOnlineCountingServer;
    TmpWMSOnlineCounting        tmpWMSOnlineCounting;

    void initOnHandItem(
                        ItemId              _itemId,
                        InventLocationId    _inventLocationId,
                        WMSLocationId       _wmsLocationId,
                        Qty                 _counted
                        )
    {
        InventSum   inventSum;
        InventDim   inventDim;

        if (!_itemId)
        {
            return;
        }

        whileselect ItemId, PhysicalInvent, PdsCWPhysicalInvent, InventDimId
            from inventSum
            where inventSum.ItemId          == _itemId      &&
                  inventSum.ClosedQty       == NoYes::No    &&
                  inventSum.PhysicalInvent  != 0
        existsjoin InventDimId, InventLocationId, wmsLocationId from inventDim
            where inventDim.InventDimId == inventSum.InventDimId
              && (!_inventLocationId || inventDim.InventLocationId == _inventLocationId)
              && (!_wmsLocationId    ||  inventDim.wmsLocationId   == _wmsLocationId)
        {
            tmpWMSOnlineCounting.ItemId         = inventSum.ItemId;
            tmpWMSOnlineCounting.InventDimId    = inventSum.InventDimId;

            tmpWMSOnlineCounting.QtyOnHand      = inventSum.PhysicalInvent;
            tmpWMSOnlineCounting.QtyCounted     = _counted;

            tmpWMSOnlineCounting.PdsCWQtyOnHand     = inventSum.PdsCWPhysicalInvent;
            tmpWMSOnlineCounting.PdsCWQtyCounted    = _counted;

            tmpWMSOnlineCounting.insert();

            wmsOnlineCountingServer.parmSBStmpWMSOnlineCounting(tmpWMSOnlineCounting);

        }
    }

    wmsOnlineCountingServer = WMSOnlineCountingServer::construct();
    initOnHandItem(_item, _inventLocation, _wmsLocation, _qtyCounted);
    wmsOnlineCountingServer.run();
}


A New Dynamics AX in the Cloud with AXIO for Professional Services Firms

AX 2012: Automatically log off the user sessions

$
0
0
Hello guys J

Today I thought of sharing the code where the user AX sessions which doesn’t belong to admin role will automatically shut down if it’s running for more than 8 hours.

The same can be pasted in batch class to be run as batchable.


publicvoid autoUserLogOff()
{
    SecurityUserRole        securityUserRole;
    SecurityRole            securityRole;
    container               usersList;
    SysClientSessions       sysUserSession;
    utcDateTime             dateTime, dateTimeLocal;
    TimeOfDay               totalHours;
    SysUsersTerminate       usersTerminate;
    SysUserInfo             userInfo;
    str                     timeValue;

    #define.AOTName('-SYSADMIN-')
    #define.Admin('Admin')

    dateTime = DateTimeUtil::newDateTime(systemDateGet(), timeNow());

    whileselect Id from userInfo
        where  userInfo.Id != #Admin
    {
        selectfirstOnly RecId from  securityUserRole where securityUserRole.User  == userInfo.Id
            exists  join securityRole where securityRole.RecId                     == securityUserRole.SecurityRole
                    && securityRole.AotName                                        == #AOTName;

        if (securityUserRole.RecId)
            continue;

        whileselect * from sysUserSession
            where sysUserSession.userId == userInfo.Id
            && sysUserSession.sessionType == SessionType::GUI
        {
            dateTimeLocal = DateTimeUtil::applyTimeZoneOffset(sysUserSession.LoginDateTime, DateTimeUtil::getUserPreferredTimeZone());

            totalHours = int642int(DateTimeUtil::getDifference(dateTime, dateTimeLocal));

            timeValue = conPeek(str2con(time2StrHM(totalHours), ":"),1);

            if( str2int(timeValue) >= 8)
            {
                usersList += [[sysUserSession.userId, sysUserSession.SessionId, sysUserSession.LoginDateTime]];
            }
        }
    }

    if (conLen(usersList) > 1)
    {
       usersTerminate = SysUsersTerminate::newUsersList(usersList);
       usersTerminate.run();
    }
}

AX 2012: Excel import/Converting the excel cells value to AX type

$
0
0
The below code helps when converting the excel cells type (variant) to actual AX type.

Below example shows how we can trigger the method:

invoiceNumber = this.formatValue(Types::String,cells.item(row, 1).value());


publicanytype formatValue(
    Types _types,
    COMVariant _variant)
{
    #TimeConstants
    FreeText excelText;
    Qty realValue;
    Integer intValue;
    TimeHour24 timeHour24;

    switch (_types)
    {
        case Types::UtcDateTime :
        // Time
            switch (_variant.variantType())
            {
                case COMVariantType::VT_R4 :
                realValue = _variant.float();
                break;

                case COMVariantType::VT_R8 :
                realValue = _variant.double();
                break;

                case COMVariantType::VT_DECIMAL :
                realValue = _variant.decimal();
                break;

                case COMVariantType::VT_BSTR :
                timeHour24 = str2time(_variant.bStr());
                break;

                case COMVariantType::VT_EMPTY:
                break;

                default:
                throw error(strfmt("@SYS26908", _variant.variantType()));
            }

        timeHour24 = any2int(#secondsPerDay * realValue);
        return timeHour24;

        case Types::Integer :
        // Integer
            switch (_variant.variantType())
            {
                case COMVariantType::VT_EMPTY:
                return0;

                case COMVariantType::VT_I1:
                return _variant.char();

                case COMVariantType::VT_I2:
                return _variant.short();

                case COMVariantType::VT_I4:
                intValue = _variant.int();
                if (intValue == 0)
                {
                    intValue = _variant.long();
                }

                return intValue;

                case COMVariantType::VT_UI1:
                return _variant.byte();

                case COMVariantType::VT_UI2:
                return _variant.uShort();

                case COMVariantType::VT_UI4:
                intValue = _variant.uInt();
                if (intValue == 0)
                {
                    intValue = _variant.uLong();
                }
                return intValue;

                case COMVariantType::VT_R4 :
                realValue = _variant.float();
                return realValue;

                case COMVariantType::VT_R8 :
                realValue = _variant.double();
                return realValue;

                case COMVariantType::VT_DECIMAL :
                realValue = _variant.decimal();
                return realValue;

                default:
                throw error(strfmt("@SYS26908", _variant.variantType()));
            }
            break;

        case Types::Real :
        // Real
        if (_variant.bStr())
        {
            returnstr2num(_variant.bStr());
        }
        else
        {
            switch (_variant.variantType())
            {
                case COMVariantType::VT_EMPTY:
                realValue = 0;
                break;

                case COMVariantType::VT_R4 :
                realValue = _variant.float();
                break;

                case COMVariantType::VT_R8 :
                realValue = _variant.double();
                break;


                case COMVariantType::VT_DECIMAL :
                realValue = _variant.decimal();
                break;

                default :
                throw error(strfmt("@SYS26908", _variant.variantType()));
            }

            return realValue;
        }

        case Types::String :
        case Types::RString :
        case Types::VarString :
        // String
        switch (_variant.variantType())
        {
            case COMVariantType::VT_BSTR :
            return _variant.bStr();

            case COMVariantType::VT_EMPTY:
            return'';

            case COMVariantType::VT_I1:
            return _variant.char();

            case COMVariantType::VT_I2:
            returnint2str(_variant.short());

            case COMVariantType::VT_I4:
            intValue = _variant.int();
            if (intValue == 0)
            {
                intValue = _variant.long();
            }
            returnint2str(intValue);

            case COMVariantType::VT_UI1:
            returnint2str(_variant.byte());

            case COMVariantType::VT_UI2:
            returnint2str(_variant.uShort());

            case COMVariantType::VT_UI4:
            intValue = _variant.uInt();
            if (intValue == 0)
            {
                intValue = _variant.uLong();
            }
            returnint2str(intValue);

            case COMVariantType::VT_R8 :
            realValue = _variant.double();
            returnnum2str(realValue, 1, numOfDec(realValue), 0, 0);

            case COMVariantType::VT_R4 :
            case COMVariantType::VT_DECIMAL :
            return'';
        }

        case Types::Date :
        // Date
        switch (_variant.variantType())
        {
            case COMVariantType::VT_BSTR :
            excelText = _variant.bStr();
            returnstr2date(excelText, 213);

            default :
            return _variant.date();
        }

        case Types::Guid :
        // Guid
        returnstr2guid(_variant.bStr());

        case Types::Int64 :
        // Int64
        returnstr2int64(_variant.bStr());
        }

    return'';
}




Dynamic AX7 / D3FO: Adding display method

$
0
0
Hi Folks :)
Today we will be looking at how we can add display method on forms.
Let’s say I would like to add a new display method on CustTable form.
In Dynamics 365 we won’t be able to add the new method or modify the existing method to the standard table or to an extension table.
It can be achieved by using the extension class.
Step 1: Create a new class and name it as <Classname>_<Extension>.
public static class CustTable_Extension
{
}
Step 2 : Now add the display methods in the class which is required to be shown.
public static class CustTable_Extension
{
    [SysClientCacheDataMethodAttribute(true)] //This attribute will cache your display method.
    public static display Name customerGroupName(CustTable _this)
    {
        return CustGroup::find(_this.CustGroup).Name;
    }
 }

To use your display method on the form, create your new field on the form extension and use the property as below:




To cache your display method on the form set on the field the property “Cache Data Method” = yes if you don’t want to use the Attribute above.
 















**Things to remember**
·         The class must be postfixed with “_extension”.
·         The class must be static.
·         The extension methods must be static.
·         The type of the first parameter determines which type is extended.



Dynamics AX7/D365 For Operations: Source code location

$
0
0
The application code for Dynamics 365 for operations is stored in File system, usually in a directory named PackageDirectory. You can find the details on the configuration related to AOS in a web.config file.

Steps to follow: 
  1. Open IIS and go to the Sites\AOSService (in case you missed, AOS is a web service with D365Ops).
  2. Right click > Explore
  3. You should be directed to a folder (to J:\AosService\WebRoot if you are using MS demo environments)
  4. Now search for the web.config file and open it.
  5. Search for keyword - "Aos.PackageDirectory" and you should be able to find the value for the Package directory.
  6. It should be J:\AosService\PackagesLocalDirectory (if you are using MS demo environments)

How is the Source code updated in File system: 

Now if we try to look into how the code is stored in the File system. In the above identified PackageDirectory, you will find that there are individual folders to consist the code changes for each Model. 


The simplest way to understand the structure is to create your own model and verify the below: 


  1. Look for the folder relevant to your newly created model (in my case, AJDEV)
  2. Under path //PackageLocalDirectory/AJDEV/AJDEV - you will observe that there are #78 new folders created for each element type in the AOT. All with a sub folder Delta which is going to store the changes made.
  3. And under path //PackageLocalDirectory/AJDEV/Descriptor - you will find a config file storing the information about your model.
  4. And under path //PackageLocalDirectory/AJDEV/XPPMetadata - the metadata information about the AOT objects is stored.

D3FO: Features deprecated in Dynamics AX 7!!!!

$
0
0
AIF, AxD, and AxBC integrations
In Application Integration Framework (AIF), data can be exchanged with external systems through business logic that is exposed as services. Dynamics AX includes services that are based on documents and .NET Business Connector (AxBC). A document is created by using XML. The XML includes header information that is added to create a message that can be transferred into or out of Dynamics AX. Examples of documents include sales orders and purchase orders. However, almost any entity, such as a customer, can be represented by a document. Services that are based on documents use the Axd <Document> classes.
The architecture of AIF and AxDs could not be scaled to a cloud service. There were performance issues around bulk import.

Reason for deprecation
The architecture of AIF and AxDs could not be scaled to a cloud service. There were performance issues around bulk import.
Replaced by another feature?
In the current version of Dynamics AX, this feature is replaced by the Data Import/Export framework, which supports recurring bulk import/export. For AxBC, we recommend that you use the actual tables.
Modules affected
AxDs, AxBCs, and AIF

To learn more about deprecated features: https://docs.microsoft.com/en-us/dynamics365/operations/dev-itpro/migration-upgrade/deprecated-features#aif-axd-and-axbc-integrations

D3FO: Event handlers usage throgh code in AX7

$
0
0
Hello guys J
Here are some of basic use of EVENTS handlers of the Form with respective syntax for logic.
Form datasource from xFormRun
[FormEventHandler(formStr(SomeForm), FormEventType::Initialized)]
public static void SomeForm_OnInitialized(xFormRun sender, FormEventArgs e)
{
FormDataSource MyRandomTable_ds = sender.dataSource(formDataSourceStr(SomeForm, MyRandomTableDS));
...
}

Get FormRun from form datasource
[FormDataSourceEventHandler(formDataSourceStr(MyForm, MyRandomTableDS), FormDataSourceEventType::Written)]
public static void MyRandomTableDS_OnWritten(FormDataSource sender, FormDataSourceEventArgs e)
{
FormRun formRun = sender.formRun() as FormRun;
formRun.myCustomMethod();
}

Get FormRun from form control

[FormControlEventHandler(formControlStr(MyForm, MyButton), FormControlEventType::Clicked)]
public static void MyButton_OnClicked(FormControl sender, FormControlEventArgs e)
{
FormRun formRun = sender.formRun() as FormRun;
formRun.myCustomMethod();
}

Access form control from xFormRun
[FormEventHandler(formStr(SomeForm), FormEventType::Initialized)]
public static void SomeForm_OnInitialized(xFormRun sender, FormEventArgs e)
{
sender.design().controlName(formControlStr(SomeForm, MyControl)).visible(false);
}

Get current record in form control event
[FormControlEventHandler(formControlStr(SomeForm, SomeButton), FormControlEventType::Clicked)]
public static void SomeButton_OnClicked(FormControl sender, FormControlEventArgs e)
{
SomeTable callerRec = sender.formRun().dataSource(1).cursor();
}


AX7 / D3FO: Form extensions and their method sequence

$
0
0
You can extend the functionality of a form by extending its controls and data sources. For example, in a form extension, you can:
·         Add a new control.
·         Enable or disable a control.
·         Change the text or label property of a control.
·         Change a control's visibility.
·         Change a form's help text.
·         Change a form's caption.
·         Add a new data source.
·         Change properties at the data-source level.
·         Add a form part.

Other ways to customise a form, such as reordering controls in the form or subscribing to form or control events, are planned to be included in a future release. In Microsoft Dynamics AX 2012, you could override form methods. In the current version, you use extensions to implement event handlers that are called from the base implementations of form methods. The following table lists each method and its associated events.

Published form DataSource method
Preceding event
Succeeding event
active
N/A
Activated
delete
Deleting
Deleted
validateWrite
ValidatingWriting
ValidatedWrite
write
Writing
Written
create
Creating
Created
executeQuery
N/A
QueryExecuted
linkActive
N/A
PostLinkActive
init
N/A
Initialized
validateDelete
ValidatingDelete
ValidatedDelete
reread
N/A
Reread
selectionChanged
N/A
SelectionChanged
markChanged
N/A
MarkChanged
leaveRecord
LeavingRecord
LeftRecord
Published form Object method
Preceding event
Succeeding event
init
Initializing
Initialized
close
Closing
N/A
run
N/A
PostRun
activate
N/A
Activated
Published form Control method
Preceding event
Succeeding event
modified
N/A
Modified
validate
Validating
Validated
leave
Leaving
LostFocus
enter
N/A
Enter
gotFocus
N/A
GotFocus
clicked
N/A
Clicked
selectionChange
SelectionChanging
N/A
pageActivated
N/A
PageActivated
allowPageDeactivate
AllowPageDeactivate
N/A
expand
Expanding
Expanded
tabChanged
N/A
TabChanged
dialogClosed
N/A
DialogClosed







AX7 / D3FO: Table extensions and their method sequence

$
0
0
You can create a table extension to extend a table's design and logic. You can add new fields, field groups, indexes, mappings and relations. You can also add new fields to existing field groups, change the label of a table field, change the Created By, Created Date Time, Modified By, Modified Date Time properties. In Microsoft Dynamics AX 2012, you could override the virtual methods of a table's base class to control the behavior that occurred during table operations, such as when creating, reading, updating, or deleting. In the current version, you instead use extensions to implement event handlers that are called from the base implementations of the table methods. The following table lists each table method and its events.

Published Table method
Preceding event
Succeeding event
validateWrite
ValidatingWrite
ValidatedWrite
validateDelete
ValidatingDelete
ValidatedDelete
validateField
ValidatingField
ValidatedField
validateFieldValue
ValidatingFieldValue
ValidatedFieldValue
modifiedField
ModifyingField
ModifiedField
modifiedFieldValue
ModifyingFieldValue
ModifiedFieldValue
Insert
Inserting
Inserted
Update
Updating
Updated
Delete
Deleting
Deleted
Initvalue
InitializingRecord
InitializedRecord
FinalDeleteValidation
Executed when a delete operation is performed on a table object, before the operation is committed to the underlying database table
N/A
FinalInsertValidation
Executed when an insert operation is performed on a table object, before the operation is committed to the underlying database table
N/A
FinalReadValidation
Executed when a read operation is performed on a table object.
N/A
FinalUpdateValidation
Executed when an update operation is performed on a table object, before the operation is committed to the underlying database table.
N/A

Validation events capture and return results by using the DataEventArgs parameter. The display and edit method modifiers are supported on table extensions.

Dynamics AX7 / D3FO: Code for Excel importing

$
0
0
Now Dynamics AX 365 is running on Web browser so import the data in AX using Excel, CSV, text etc. has been changed. FileName, FilenameOpen extended data type is no more supported to browse the excel file.
If we compare Excel file import process between AX 2012 and Dynamics AX 365 then in AX 2012 file was importing from Client (from local system) to AX database directly but now the new AX is running on web server over the IIS so to import file in AX mean first file need to import on server and need to store in File server or SharePoint or Database. And then read the file from stored location may be from File server, SharePoint or Database.
So how we can import data in AX using Excel file? The simple way is via using Data entities and if data entity does not exist then need to create new Data entity for table and then we can import data via using the excel file or any other supported file using that Data entity.
But sometime required to import the data in AX using dialog in that case Data entities will not help, so in this blog I will describe how we can import data in Dynamics AX 365 using excel file.
In ax 2012 we have used the below classes to import data in AX via using excel file:-
SysExcelApplication application;SysExcelWorkbooks workbooks;SysExcelWorkbook workbook;SysExcelWorksheets worksheets;SysExcelWorksheet worksheet;SysExcelCells cells;
But now in Dynamics AX 365 the above classes does not exist anymore to read excel file.
So how we can import data in AX via Excel file? ,
So first step to import the excel file in AX , create a new class (TestExcelImport) and extend with RunBase and add a new button in dialog with upload caption (Same as AX 2012)
 Object Dialog()
   {
       FormBuildButtonControl buttonControl;
       DialogGroup            dlgGroup;
       FormBuildGroupControl  buttonGroup;
       dialog = super();              
       dlgGroup       = dialog.addGroup('');
       buttonGroup    = dialog.formBuildDesign().control(dlgGroup.formBuildGroup().id());
       buttonControl  = buttonGroup.addControl(FormControlType::Button, 'Upload');
       buttonControl.text("Upload file");
       buttonControl.registerOverrideMethod(methodStr(FormButtonControl, clicked),
                                        methodStr(TestExcelImport, uploadClickedEvent),
                                        this);
       return dialog;
   }
And below is the upload button click event (click event already registered in above dialog method)
private void uploadClickedEvent(FormButtonControl _formButtonControl)
   {
       FileUploadTemporaryStorageResult result = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
       If (result && result.getUploadStatus())
       {
           result.getFileContentType();
           //result.
           fileUrl = result.getDownloadUrl();
           info(fileUrl);
       }
   }
FileUploadTemporaryStorageResult is the class has been introduced in AX and this class is responsible to browse and upload the file on server (On File server, SharePoint or Database). And from here store the file path (for me fileUrl) in a variable to read the file later in run method.
Now next step to read the data from uploaded excel file:-
Public  void   run()
   {
       System.Byte[] byteArray;
       System.IO.Stream     stream;
       try
       {
           stream = File::UseFileFromURL(fileUrl);
           this. readExcelData(stream);
           //info("Done"); 
       }
       catch(Exception::Error)
       {
           info(strFmt("%1 %2",Exception::Error,fileUrl));
       }
   }
File class has been used to read excel data from the url (file location from server) and will return the data in stream. Now the task is to read excel Stream data in AX.
So to read stream data of excel file Microsoft have used ExcelPackage (EPPlus ), we can manipulate the file using this package (Create excel,create new worksheet , pivot table , design ,formatting etc.). Developer can access the classes of  EPPlus using OfficeOpenXml namespace. the below classes is responsible to read the data from stream.
       OfficeOpenXml.ExcelWorksheet;
       OfficeOpenXml.ExcelPackage;
ExcelPackage class is responsible to create  excel package from stream (can be file also in place of Stream), ExcelWorksheet class where the worksheet has been copied with data. And then looping the data of rows from the excel cells.
conData container variable declared in class declaration of class. So now enjoy the excel import in AX.
Note:-Import the data via using CSV or text file still easy and we can use CommaIo or Comma7Io.


Dynamics 365 For Operations [D365FO]: Ledger dimension and Default dimension helper class

$
0
0
Hello guys, I would be posting the important classes which handles the ledger dimension and default dimension logics like merging or replacing in D365FO (Dynamics 365 for operations). Like how we used to have DiemsionDefaultingService class in AX2012, We have the following two classes shown below:

LedgerDimensionFacade : 

This class used for fixed LedgerDimension, we have many static methods here, like Merge Dimension, getdefaultDimension and many more, refer below image.


      publicstaticDimensionDisplayValue getDisplayValueForLedgerDimension(DimensionCombinationBase _dimensionCombinationBase)
    {
        DimensionAttributeValueCombination dimensionAttributeValueCombination;

        if (_dimensionCombinationBase == 0)
        {
            return"";
        }

        selectfirstonly DisplayValue from dimensionAttributeValueCombination
            where dimensionAttributeValueCombination.RecId == _dimensionCombinationBase;

        return dimensionAttributeValueCombination.DisplayValue;
    }



LedgerDimensionDefaultFacade: 

This class will help us to perform action over default dimension. Like Merge specify default dimension into single default dimension, replace attribute value etc.

Both classed having almost same method that is available in AX with DiemsionDefaultingService class.

Dynamics 365 For operations: How can you open table browser in IE?

$
0
0
In order to view the table data, normally from VS(visual studio) we can right click on the table and select the "Open table browser", it open up the table with data.
Other way we can view the table data in the IE/Chrome browser by using the below method:
AX7 URI : XYZ.cloudax.dynamics.com.
Company: "USMF","GBSI" or which company wanted to view the data.
TableName: Mention the AX table name i.e. like Customer/Vendors/CustGroup - which table data wanted to be viewed.


Dynamics 365 For Operations: Pack your data in ZIP folder

$
0
0
  
Hello guys,

Today, I would be sharing the code in D365 FO to pack your data (can be Xml, Excel, Word etc.) in ZIP folder.


using System.IO.Compression;
classSBSCreateZipFile
{       

    /// <summary>
    /// Runs the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    publicstaticvoid main(Args _args)
    {
        SBSCreateZipFile file = newSBSCreateZipFile();
        File.createAndDownloadExcelFileViaZip();
    }

    publicvoid createAndDownloadExcelFileViaZip()
    {
        conststr extensionZIP = '.zip';
        conststr extensionExcel = '.xlsx';

        System.IO.Stream workbookStream = new System.IO.MemoryStream();
        System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();

        using (var package = new OfficeOpenXml.ExcelPackage(memoryStream))
        {
            var worksheets = package.get_Workbook().get_Worksheets();
            var worksheet = worksheets.Add("First sheet");
            var cells = worksheet.get_Cells();
            var cell = cells.get_Item(1,1);
            cell.set_Value("Customer id");

            package.Save();
        }

        memoryStream.Seek(0, System.IO.SeekOrigin::Begin);

        //File::SendFileToUser(memoryStream, "ExcelTesting_Vishal.xlsx"); // TO download only excel i.e. .xlsx

        str formDataFileName = "VishalTiwari_Package";
        str formDataFileNameXML = "ExcelTesting_Vishal";

        System.IO.MemoryStream zipArchiveStream = new System.IO.MemoryStream();
        using (ZipArchive zipArchive = new ZipArchive(zipArchiveStream, ZipArchiveMode::Create, true))
        {
            ZipArchiveEntry dataFileEntry = zipArchive.CreateEntry(formDataFileNameXML + extensionExcel);
            using (System.IO.Stream dataFileEntryStream = dataFileEntry.Open())
            {
                memoryStream.CopyTo(dataFileEntryStream);
            }
                      
        }
        File::SendFileToUser(zipArchiveStream, formDataFileName + extensionZIP);
    }


}

MS Dynamics 365 for Operations: Class extensions (ExtensionsOf[])

$
0
0
Hello J
I would be discussing on how easily classes can be extended in Dynamics 365 for Operations.
As we are aware that classes can be easily customizable by writing delegate and subscribing the method in the new extension class. But this could be sometime hectic and cumbersome as it consume lot of time when complex logic (Multiple scenarios or method) needs to be written. So in order to save time and effort, extension of class features can be easily adopted.
Below syntax shows you how we can write extensions of for a particular class:
Class Student
{
    real studentAge(int arg)
   {
    …
    }
}

[ExtensionOf(ClassStr(Student))]
class Student_Extension
{
    real studentAge (int arg)
    {
     var s = next studentAge(arg + 4);
   
      return s;
    }
}

We have observed a new keyword here “next” in the extension class. For the time being I would just say it creates a Chain of Command,  We will learn more about it in my next blog post. J

Dynamics 365 for Operations: Class extensions and Chain of Command (COC)-Next keyword

$
0
0
The functionality of extension classes (also known as class augmentation) is being improved to allow developers to wrap logic around methods defined in a base class. This allows extending the logic of public and protected methods without the need to use event handlers. When you wrap a method, you can also access other public and protected methods and variables of the class. This way, you can start transactions and easily manage state variables associated with your class.
Class Student
{
    real studentAge(int arg)
   {
    …
    }
}
It is now possible to extend the functionality of method1 using an extension class by reusing the same name to add pre-and post-logic to it.
[ExtensionOf(ClassStr(Student))]
class Student_Extension
{
    real studentAge (int arg)
    {
     var s = next studentAge(arg + 4);
    
      return s;
    }
}

Wrapping studentAge and the required use of the next keyword creates a Chain of Command (CoC) for the method. Here is what happens when the following code executes.
Student c = new Student ();
Print c. studentAge (33);

The system will find any method that wraps studentAge. It will randomly execute one of these methods (say, studentAge of the Student _Extension class). When the call to next studentAge() occurs the system will randomly pick another method in the CoC or call the original implementation if none exist.

Dynamics 365 For Operations: Keyboard shortcuts

$
0
0

Action Shortcuts:
1. Open Action Search: Ctrl + ‘ and Alt + Q
2. Move to the Standard Action Pane: Ctlr + F6
3. To Open a Tab in the Action Pane or a Menu: Enter/Space/Alt+Down Arrow
4. Go to Next/Previous Option in a Menu: Down Arrow/Up Arrow
5. To Close a Tab in the Action Pane/Menu: Esc
6. Simulate a Right-Click: Shift + F10
7. To Open Dynamics 365 for Operations Context Menu: Ctlr + F10
8. To Execute the Default Button on a Form/Dialog: Alt + Enter
9. Click a Button or Tile: Enter/Space
10. View Refresh Information for a Count Tile: Alt + Up Arrow
Date Picker Shortcuts:
1. To Open the Date Picker: Alt + Down Arrow
2. Move between Dates in the Date Picker: Ctlr + Arrows
3. Move to Next/Previous Month: Page Down/Page Up
4. Move to the Next/Previous Year: Ctrl + Shift + Page Down/Ctlr + Shift + Page Up
FactBox Shortcuts:
1. Open the FactBox Pane: Ctrl + F2
2. Close FactBox Pane: Esc
3. Move to Previous/Next FactBox: Alt + Shift + Down Arrow/Alt + Shift + Up Arrow
4. Move to the th Factbox: Alt + ( = 1-9)
5. Expand a FactBox (FactBox Header): Space/Enter
6. Collapse Current FactBox: Alt + 0
Filtering Shortcuts:
1. Open Grid Filtering for Current Column: Ctlr + G
2. Close Grid Filtering for Current Column: Esc
3. Open Filter Pane: Ctlr + F3
4. Close Filter Pane: Esc
5. Open Advanced Filtering/Sort: Ctlr + Shift + F3
Form Shortcuts:
1. To Create a New Record: Alt + N
2. To Delete a Record: Alt + Del/Alt + F9
3. Save Record: Alt + S/Ctlr + S
4. Revert: Ctlr + Shift + F5
5. Data Refresh: Shift + F5
6. Move to the Visible First Field on the Form: Alt + Shift + F
7. Toggle Edit Mode: F2
8. Attach a Document: Ctlr + Shift + A
9. Export to Excel: Ctlr + Shift + E
10. Move to Previous Record: Ctrl + Up Arrow
11. Move to Next Record: Ctrl + Down Arrow
12. Move to First Record: Ctlr + Home
13. Move to Last Record: Ctlr + End
14. Open Navigation List on Details Forms: Ctrl + F8
15. Close Navigation List on Details Form: Esc
Grid Shortcuts:
1. Move to Previous/Next Column: Tab/Shift + Tab
2. Move to Previous/Next Row: Down Arrow/Up Arrow
3. Move to Previous/Next Row without Selecting One: Ctlr + Up Arrow/Ctrl + Down Arrow
4. Select/Clear the Current Row: Ctlr + Space/Ctrl + Click
5. Add Next/Previous Row to the Select Set: Shift + Space
6. Add a Range of Rows to the Selected Set: Shift + Click
7. Go to Next/Previous Page of Data: Page Up/Page Down
8. Create New Row at the Bottom of the Grid: Down Arrow
9. Select/Clear All Rows: Ctlr + Shift + M
10. Move to First Record: Ctlr + Home
11. Move to Last Record: Ctrl + End
Input Control Shortcuts:
1. Enter Session Date in a Date Field: D + Tab
2. Enter Current Date in a Date Field: T + Tab
3. Open Lookup, Combo Box, Date Picker, Drop Dialog: Alt + Down Arrow
4. Close Lookup, Combo Box, Date Picker, Drop Dialog: Esc
5. Move Focus into a Lookup: Alt + Down Arrow
6. Open the Control’s Enhanced Preview: Alt + Up Arrow
7. Select Text in the Current Field: Ctlr + A
8. Enter/Leave the Text Area in an HTML Editor Control: Alt + Down Arrow/Alt + Up Arrow
Messaging Shortcuts:
1. Go to the Message Center: Ctlr + Shift + F7
2. Go to the Message Bar: Ctlr + F7


Dynamics 365 For Operations: GenerateDrillThroughLink/DrillThroughProvider - Hyperlink on SSRS report data fields

$
0
0
The process for embedding custom drill-down navigation links into application forms in Dynamics 365 for Operations differs from solutions in AX2012

Dynamics 365 for Operations Reporting framework does NOT auto-generate hyperlinks based on EDT relations.

How to get hyperlink on SSRS report data field:

In the Precision design, field 'Action' property, you can find the expression like:

=Microsoft.Dynamics.Framework.Reports.BuiltInMethods.GenerateDrillThroughLink(Parameters!AX_ReportContext.Value, Parameters!AX_UserContext.Value, "[MenuItemName]", "[MenuItemType]", "[TableName]", "{Arguments}")

Example:
=Microsoft.Dynamics.Framework.Reports.BuiltInMethods.GenerateDrillThroughLink(Parameters!AX_ReportContext.Value, Parameters!AX_UserContext.Value, "ProjTable","Display","ProjTable", "ProjId",fields!ProjId.Value)





Viewing all 126 articles
Browse latest View live