Pages

Thursday, August 4, 2011

How to change a customer's party type

I often see customers that are created wrong in client's systems.  They will have a party type of "organization" when they should really have "person" or vice versa.  The difference between the two, as far as I can tell, is that organizations are permitted to have multiple contacts, and other little things like that, while person types are supposed to be simpler.

Here is a little static method I wrote to change their party type.  One thing to worry about, when you go from Organization to Person, you may want to check if there are multiple contacts before you make the change.  Not exactly sure what it will do.




static server boolean changeCustPartyType(CustTable _custTable, DirPartyType _dirPartyType)
{
    CustTable       custTable;
    ;

    if (_custTable && _custTable.PartyType != _dirPartyType)
    {
        ttsbegin;

        custTable.selectForUpdate(true);
        custTable.data(_custTable);

        custTable.PartyType = _dirPartyType;

        custTable.setNameAlias();

        DirParty::updatePartyFromCommon(custTable.PartyId, custTable, DirSystemPrivacyGroupType::Public, true);

        custTable.doUpdate();

        smmBusRelTable::updateFromCustTableSFA2(custTable);

        custTable.setAccountOnVend(custTable.orig());

        smmTransLog::initTrans(custTable, smmLogAction::update);

        ttscommit;

        return true;
    }

    return false;
}

Wednesday, June 22, 2011

Dynamics AX 2012 Upgrade thoughts

I've been working on upgrading a demo environment of AX 2009 to AX 2012 to get the experience under my belt.  Like any good upgrade, it takes careful planning and execution to be performed properly.  I'll be blogging my adventures through this upgrade as I go, so check back for additional posts.

I have a good deal of AX 4.0 to AX 2009 upgrade experience and AX 2012 upgrade is definitely different.  From AX 4.0 to AX 2009, you basically did three things (not exactly this order):
  • Merged all of your customizations against the new sys/syp layers
  • Extended the ReleaseUpdateDB41* classes if needed to upgrade data
  • Ran the upgrade scripts to upgrade fields and move data around

With AX 2012, there are much more pre-upgrade validation steps where your business data is prepped and validated more closely.  This can make more up-front work, but ideally it will be a more seamless upgrade.  The downside is that it is much more of a team effort initially to upgrade, involving financial decision makers in your organization and a few developers.  I do see the benefit of reducing the potential for error.  The upgrade to 2009 did more blind, data dumping, while 2012 validates/examines what its doing.


I've been working for about 6 hours now hitting a few bumps, mainly because I don't have the answers to some of the financial bits of the upgrade.  Some helpful upgrade whitepapers that were recommended to me can be found here.  My environment is Microsoft's Refresh 3.5 with no layers other than SYS/SYP and a dash of USR.  I just added a couple modifications to see how the new process would work.  The demo data however is not perfect, and because it's a demo company, a good deal of the business inputs required for the upgrade will be guesses by me.

The first steps of the upgrade have you import a giant XPO (9.43 MB) into the USR layer that prepares the system for upgrade.  The first step is a "Check upgrade readiness", which is essentially a bunch of batch jobs that do all sorts of validation on your system.  I had 1228/1229 complete with one erroring.  The results of the completed jobs seemed a little daunting.  Be prepared to have your controller handy.


This step is not required, but let's face it...if you don't get past most of this stuff, it's almost guaranteed you will face issues down the road.  The issues it brings up are for the most part, legitimate issues with data that do need to be fixed.  I chose to ignore most of the errors as I didn't want to spend a long time guessing my way through fixing them, when I don't really know the answers.

The next step "Initialize preprocessing", I think just creates or fills these "Shadow_*" tables in the AOT used to store upgrade data.

Then you have 22 steps to complete, with varying difficulty.  Some of these steps seem like they could have had a "best guess" option.  For example, I had to setup country/region code mapping.  AX 2009 was on ISO 3166-1 alpha-2, and AX 2012 is going to be on ISO 3166-1 alpha-3.  Meaning 2 letter country codes to 3 letter country codes.  This seems like something that can auto-populate.  You also must configure it for each company.  For me, this was 54 country codes such as "US" that I had to map to "USA" across each company.  Just a little time consuming.  Better safe than sorry though.

Other parts of the upgrade require the DAT company to have a number sequence, ledger account, and other small bits of data filled in to proceed.

The errors can seem cryptic, and if you have any bad data in your system, the errors will easily lead you astray.  They are pretty easy to determine via code and the debugger what is causing an error to be thrown.  For example, the demo data had some strange unit conversions for a specific item in one of the companies setup.  The company had 0 items, so the conversions were just bad data.  The errors were just catch-all type messages.  Some quick debugging and it was pretty easy to track down.

I've put in a few long nights after work, so I'm taking breaks in between so I don't wear myself out.  My next steps are:

  • Preprocess data on live system
    • Run live preprocessing scripts
    • Country/region upgrade
    • Run delta preprocessing scripts
  • Preprocess data in single user mode
    • Enter into single-user mode
    • Run single-user mode preprocessing scripts
The single user mode stuff I believe basically bulk copies the prepared data from AX 2009 to the AX 2012 system and finalizes it.

The perk of this is supposed to be a shorter downtime to actually go live.  Stay tuned!

Tuesday, April 26, 2011

Generating better random numbers with dynamics ax 2009

Inspired by this post, I created a neat little way to generate good random numbers between a range.

If you use AX Random class or RandomGenerate class to generate random numbers, you will find that if you do several in a row, they turn out fairly sequential.

The xGlobal::randomPositiveInt32() does a slightly better job of producing a random integer.

I would sometimes see patterns using this when the output is sorted:



static void Job6(Args _args)
{
    int total = 20;
    int i;
    ;
    
    while (total)
    {
        i = xGlobal::randomPositiveInt32();

        /*
        // This is better
        while (i > 9999999)
            i = i div 10;

        while (i < 1000000)
            i = i * 10;
        */
        
        while (i > 9999999)
            i = i >> 1;

        while (i < 1000000)
            i = i << 1;

        info(int2str(i));
        
        total--;

    }
}

Sunday, March 6, 2011

How to iterate over addresses in the global address book of Vendors/Customers

I'm frequently asked to mass update addresses for vendors or customers during data imports when something isn't imported correctly.  Here is a simple job that I wrote that shows the relationship between the tables, that you can modify to fit your needs.



static void iterateOverAddresses(Args _args)
{
    VendTable                           vendTable;
    Address                             address;
    DirPartyAddressRelationship         dpar;
    DirPartyAddressRelationshipMapping  dparm;
    ;

    while select address
        join dpar
        join dparm
        join vendTable
        where
              vendTable.PartyId         == dpar.PartyId                         &&
              dpar.RecId                == dparm.PartyAddressRelationshipRecId  &&
              dparm.RefCompanyId        == curExt()                             &&
              dparm.AddressRecId        == address.RecId
    {
        info(address.Name);
    }
}

Saturday, March 5, 2011

Enterprise portal security issues with Refresh 3.5 and AX 2009

I wanted to play around with a well-built demo environment so that I could experiment with some workflow ideas I had, so I downloaded Microsoft's Refresh 3.5 VPC from partnersource (located here).

I prefer to work as a typical user with typical security settings so that I can identify issues quickly.  After booting up the VPC and restarting it a few times so that it would adjust to my machine, there were quite a few changes I had to make to get it usable.

Firstly, I had to change the client configuration to point to AX593 and the business connector configuration as well.  If you forget the business connector, you will have EP issues, amongst other things.

I randomly chose contoso\Nancy as my testing user, and I immediately notice that I don't have access to EP at all:

So I switch back over to contoso\administrator to take a look at the security settings in EP and notice there really isn't anything setup:

Let's add Nancy to the viewer's group and see what happens...


So now we can at least see the role centers, but the KPIs are having an issue...hmm...this issue is typically related to Kerberos, but on the Refresh 3.5 VPC, it's only NTLM security because everything is on the same machine.

Let's remove Nancy from the viewers group, and just create our own "AX Users" group, but let's make it "Read" only instead of "View only", add Nancy to it, then refresh our role center:

And we're good.  The only difference between View Only and Read is the ability to view the source of documents with server-side file handlers.  See Sharepoint permissions matrix here for security differences.

Friday, March 4, 2011

Undoing the Financial Dimension Wizard

During the initial setup of AX, I've seen where dimensions in one environment don't match the other environment. One place will have one called "Brand" and another will say "Brands", or "Event/Place" vs "Place/Event", for example. This can cause big issues, and be a bit of a pain.

To fix this, what I recommend doing is deciding which environment is the good one, and dropping the dimension code down to the bad one. You'll then need to drop the good database down to the bad environment too...otherwise data will look screwy all over the place.

Fixing data is another discussion, and just reloading/replacing the database is the easiest.

Replacing the code is easy. This screenshot should give you all the information you need to fix the dimensions in the AOT:



  • Tables\LedgerJournalTrans
  • EDT\COSAllowDimensions
  • EDT\Dimension
  • EDT\DimensionAllocation
  • EDT\DimensionCriteria
  • EDT\DimensionKeepFromTransaction
  • EDT\DimensionLedgerAllocCriteria
  • EDT\DimensionLedgerJournal
  • EDT\DimensionPriority
  • EDT\MandatoryDimension
  • EDT\XMLMapDimension
  • BaseEnums\SysDimension
  • Forms\LedgerAllocation
LASTLY!!!
Labels need to be fixed.  The dimension wizard creates labels for these items, I believe in the DPA label file.  Just check on the objects and see what their labels are.  You can open the axDPAen-us.ald file in notepad (US English) and take a look there too.

Wednesday, February 16, 2011

Fixing addresses that are not formatted correctly

An issue that comes up often at various clients is addresses being formatted incorrectly.  Functional people have been able to fix this by "touching" the zip code on the address with an issue...basically they click on it and re-choose the zip code and save to fix it.

What is going on behind the scenes, is the AddressMap is being used to correctly format the address.

The Address.address, CustTable.address, VendTable.address, etc. fields should not be manually filled in.  They should not have data dumped into them either.  It is a derived/built field that depends on the country code and each individual format.

This is important when you deal with international customers and customs.  Customs for European countries, for example, can be very picky with documents.  If the address is even slightly malformed, I've seen product get held for up to a month, and sometimes it's seized...all for a silly address error.

Here is a job I wrote that can be adapted to automate the process of building addresses using AddressMap.  Right now, it lets you pick a customer that you want to correct the addresses for, then displays a before/after of the addresses.

Enjoy!  As always, comments are welcome!



static void updateCustAddress(Args _args)
{
    Dialog                              dialog;
    DialogField                         field;
    CustTable                           custTable;
    Address                             address;
    DirPartyAddressRelationship         dpar;
    DirPartyAddressRelationshipMapping  dparm;
    AccountNum                          accountNum;
    ;

    dialog  = new Dialog("Build customer address");
    dialog.addText("Select your customer to update:");
    field   = dialog.addField(typeid(CustAccount));
    dialog.run();

    if(dialog.closedOk())
    {
        accountNum = field.value();

        if (CustTable::exist(accountNum))
        {
            ttsbegin;
            while select forupdate address
                join dpar
                join dparm
                join custTable
                where custTable.AccountNum      == accountNum          &&
                      custTable.PartyId         == dpar.PartyId         &&
                      dpar.RecId                == dparm.PartyAddressRelationshipRecId  &&
                      dparm.RefCompanyId        == curExt()               &&
                      dparm.AddressRecId        == address.RecId
            {
                info(strfmt("Type %1 - before:", address.type));
                info(strFmt(">%1<", address.Address));
                
                address.AddressMap::setAddress();
                address.update();
                
                info(strfmt("Type %1 - after:", address.type));
                info(strFmt("<%1>", address.Address));
            }
            ttscommit;
        }
        else
        {
            info("Invalid Account");
        }

    }

    info("Done");
}