Monday, March 7, 2016

Counting of inventory On-Hand in AX2012


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:

public static void 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;
        }

        while select ItemId, PhysicalInvent, PdsCWPhysicalInvent, InventDimId
            from inventSum
            where inventSum.ItemId          == _itemId      &&
                  inventSum.ClosedQty       == NoYes::No    &&
                  inventSum.PhysicalInvent  != 0
        exists join 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();
}


Monday, February 29, 2016

Understanding Address Framework Technically

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 LogisticsPostalAddress table using the below logic.       

public static 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;
}



Monday, December 15, 2014

Debugging AIF Service in AX2012

Hi Guys,
In this post we’ll take a closer look at debugging AIF services in Dynamics AX 2012. Debugging is a critical part of the development process as it helps you identify any defects in your code, so you can fix them. The MorphX integrated development environment (IDE) provided by Dynamics AX provides excellent support for debugging X++ code, and you can use it to inspect X++ code that is part of your Dynamics AX Jobs, Reports, Forms, Tables, etc.
But when it comes to debugging AIF services in Dynamics AX 2012, you can’t rely on the in-environment debugging capability of the MorphX IDE alone. Granted that you can use the Jobs feature in to help you debug the service classes while staying within AX, but debugging a service call that is triggered from outside by some client consuming the AIF service, can’t be done while staying within MorphX. For that you will need to utilize the debugging capabilities provided by Visual Studio. Here’s how you can do that.
Debugging AIF Services in Dynamics AX 2012
·         First, you’ll need Visual Studio installed on the same machine where AOS reside. You’ll also need Dynamics AX Application Explorer component installed as well. The AX Application Explorer is a Visual Studio add-on that is used browse the AX AOT (Application Objects Tree) within Visual Studio. You can install it from the Microsoft Dynamics AX CD by choosing Developer Tools > Visual Studio Tools.
·         Next, open Visual Studio and in the Application Explorer pane expand the AOT (Application Objects Tree) and navigate to the AIF Service. Expand the methods given here and open the method that you want to debug. Add a breakpoint to the code line that you’re interested in.
·         Since AIF Services code is compiled into CIL (Common Intermediate Language) and run on the server, to debug it you’ll need to attach the debugger to the server (Ax32Serv.exe) process. For this, select Debug menu > Attach to Process. Then select ‘Ax32Serv.exe’ and press ‘Attach’. Wait for until the code is attached to the process and all the relevant files and debugging symbols have been loaded into the memory.
·         Now, make the service call from the client application and the control will eventually stop over the break point.

There are a few things that should keep in mind though while debugging AIF Services in Dynamics AX 2012. First off, if you want to step into another method or methods during the debugging process, you will need to have them pre-loaded (opened) in your Visual Studio IDE before you can actually attach the process. Also, you can’t alter the code while debugging in Visual Studio. You will have to make the changes in X++ code and compile it, in order to make it available for further debugging, if required.

Friday, July 25, 2014

AX2012 R3: Lookup in Issue Search

Today’s post will be on AX2012 R3: Issue search

Guys this is one of the wonderful feature introduced in R3 release, you sometimes find the different kind of issues in AX, so this is the page which provides you hot fixes released for AX 2012 and you will be able to look at the KB number and the release date (if applicable).

As shown below, “Lookup in Issue Search” is available under all the AOT objects. Here I’ve just navigated to AOT >> Tables >> CustTable and right clicking and Add-Ins provided me the option:





And clicking on the search, opens up the new browser [To login as customer/Partner], where you can login.





Now on the below page you can have a look how it filters with then current AOT object, and shows all the hotfixes and KB article recently updated by MS. 





 Hope you all like it. Keep daxing J Enjoy R3