Thursday, December 19, 2013

Find top layer objects

static void Job13(Args _args)
{

    #AOT
    #File
    TextIo textIo;
    TreeNode treeNodeTables = TreeNode::findNode(#ClassesPath);
    TreeNode treeNode;
    int myCount, valueID;
    TreeNode treeNode2 = TreeNode::findNode(#TablesPath + '\\' + tableStr(Address));
    UtilEntryLevel layer;
    EnumId  enumId;
    DictEnum dictEnum;
    ;
 
    textIo = new TextIo(@"C:\temp\textlayerchanged_classes.txt", #IO_WRITE);
 
    treeNode = treeNodeTables.AOTfirstChild();
    while (treeNode != null)
    {
        layer = treeNode.applObjectLayer();
        valueID  = enum2int(layer);
        if( valueID ==  10)
        {
//            info(treeNode.AOTname());
            textIo.write(treeNode.AOTname());
        }
        treeNode = treeNode.AOTnextSibling();
    }

}

To find a property from the object

static void findAOTObjectByProperty(Args _args)
{
#AOT
TreeNode treeNodeTables = TreeNode::findNode(#TablesPath);
TreeNode treeNode;
str strPropertyName = 'SaveDataPerCompany';
str strPropertyValue = 'No';
;
// first table
treeNode = treeNodeTables.AOTfirstChild();
while (treeNode != null)
{
if (treeNode.AOTgetProperty(strPropertyName)== strPropertyValue)
{
info(treeNode.AOTname());
}
// next table
treeNode = treeNode.AOTnextSibling();
}
}

Sunday, November 17, 2013

PowerPivot Data Source for Excel, Power View

Data in Dynamics AX can be brought into Excel/PowerPivot in the SharePoint, so that the end user can open Excel to view the AX data and use PowerPivot to create report.

First needs to create PowerPivot Gallary once created the site collection out of template "PowerPivot Site".

From the start menu, open an excel, make sure the powerpivot has been installed on your local Excel. Then use the Excel to connect to the Dynamics AX SQL Database to retrieve the data, save the Excel data sheet into the SharePoint.

Now the data sheet in SharePoint is connected to the data in the AX. From there, you can view AX data from Excel.

Wednesday, October 30, 2013

DropDown

1. The approach used most in the application is the implicit lookup based on table fields. As you can see from the control properties, DataSource and DataField specify which table field the control is based on. In my example, it is based on SalesTable.CustAccount, which uses the ExtendedDataType CustAccount, which has a relation to CustTable.AccountNum. Therefore, any time you add this field to a form, it will automatically provide a lookup button to select one of the customers.
Relations being specified on EDTs is at times confusing to people, who are used to seeing the relations between tables on the tables themselves. Technically speaking, such definitions on EDTs are incorrect. But no worries, AX supports the “correct” scenario out of the box as well. The relation to CustTable could have been specified on the table just as well.

2. 2nd most used approach is to specify the ExtendedDataType on the control directly. This is used to allow the user to specify a value to filter the records on directly from the form, for example (without the need to go to Extended Query form). It is also indirectly used on all RunBase dialogs in the system (when creating a DialogField, we specify the EDT to be used, which is transfered to the ExtendedDataType property on the corresponding control). In the tutorial, it is again the CustAccount EDT that is specified in the properties.

3. The 2 above examples both used metadata to define what lookup is to be displayed. The 3rd most used approach is relying on SysTableLookup class and builds the lookup at runtime. The code is relatively straightforward, and is described in more detail on MSDN. Using this approach, the developer can specify the query to filter the data being displayed in the lookup form. Note, that SysTableLookup only allows to have data from one table in the lookup form (+ display methods on this table). SysMultiTableLookup is an extension I have created a while ago, that adds this and other useful functionality to SysTableLookup class.

4. The remaining 2 standard approaches are rarely used in the application. Lookup based on ExtendedDataType is very similar to approach described under #2, but is executed from code at runtime. This way, you can change the lookup data displayed dynamically, based on some conditions (For example, on Ledger Journal lines, the offset account lookup shows vendors, customers, banks, etc. based on the offset account type specified on the line).

5. BaseEnum values are usually represented as a ComboBox (and, in special cases, CheckBox or RadioButton) control on forms in Dynamics AX. The lookup is provided by the kernel automatically for this type of control. But, sometimes, it is required to show the values of an enumeration in a string control – most common scenario is providing filter capabilities based on enums, where multiple values can be specified at once, similar to extended query form filters. In fact, this approach is actually used on SysQueryForm for enum fields. As you can see from the code, the lookup call is also very simple in this case.

http://kashperuk.blogspot.com/2009/04/lookup-methods-tutorial-custom-list.html

Thursday, October 24, 2013

ExistsJoin

List all the customers that has entries in the Sales table.

    Query                   query;
    QueryBuildDatasource    datasource,SalesDataSource,CustomerDataSource;
    QueryRun   queryrun;
    CustTable custtable;
    SalesTable salesTable;
    query = new Query();
    CustomerDataSource  = query.addDataSource(tableNum(CustTable ));
    SalesDataSource  = CustomerDataSource.addDataSource(tableNum(SalesTable ));
 
    SalesDataSource.joinMode(JoinMode::ExistsJoin   );
 
    SalesDataSource.relations(false);

    SalesDataSource.addLink(fieldNum(CustTable, AccountNum),
    fieldNum(SalesTable  , CustAccount ));
    queryrun = new QueryRun(query);
    while(queryRun.next())
    {
        custtable = queryRun.get(tablenum(CustTable ));
        Info(custtable.AccountNum);
    }

List customers who has entries in the project table
    Query q;
    QueryBuildDataSource        vrfQBDSCustTable;
     QueryBuildDataSource        qbr1;
   
    q = new Query();
    vrfQBDSCustTable = q.addDataSource(tableNum(CustTable));
    qbr1 = vrfQBDSCustTable.addDataSource(tableNum(ProjTable));
    qbr1.joinMode(JoinMode::ExistsJoin);
    qbr1.relations(true);
   
    info(q.xml());

Timesheet Process

Create a new timesheet,
Submit for approval, after it is approved
The timesheet is in “Unposted timesheets”, then posted it.
After it is posted, it can be found in “Posted project transactions”.
From all project, find the project which timesheet was created based on. Create an invoice proposal.
The invoice proposal can be found in PMA/common/project invoices/invoice proposals, post the invoice proposal,
After invoice proposal has been posted, the status updated to “Invoiced”

To find the posted data, go to General Ledger\Voucher Transactions

Tuesday, October 22, 2013

tmpdata

PSATmpSchedEmplResource             tEmplResource;
tEmplResource.setTmpData(tmpSchedEmplResource);

update data in tEmplResource
once tEmplResource is out of the scope, it is deleted. 
Updated data is still at tmpSchedEmplResource

tmpSchedEmplResource contains the data, after setTmpData is called, tEmpResource will contains the data. Edit/Update data in tEmplResource will update data in tmpScheEmplResource. 


1. Create temporary table as TmpTestTable with 3 fields(ItemId,Itemname,ItemGroup).
2. Create a form as TempTestTable and attach the table as datasource
3. Create a new method on the form and copy the following code

TmpTestTable populateRecords(ItemGroupId _itemGroupId)
{
 
TmpTestTable tmpTable;
InventTable inventTable;
;

while select inventTable
where inventTable.ItemGroupId == _itemGroupId
{
tmpTable.Itemid = inventTable.ItemId;
tmpTable.ItemName = inventTable.ItemName;
tmpTable.Itemgroup = inventTable.ItemGroupId;
tmpTable.insert();
}

return tmpTable;


4. Call the above method in init() after super as below
public void init()
{
 
super();

// Call the setTmpData method to attach records to datasource
TmpTestTable.setTmpData(element.populateRecords("Parts"));
}

Sunday, October 20, 2013

Create a reference dropdown

  1. 1.       PSAActivitySetup VRFSPPRequestor create EDT, EDT’s table reference points to the hcmWorker.
  2. 2.       On PSAActivitySetup create a new field, the new field’s EDT is the newly created EDT.
  3. 3.       Create new relation on the PSAActivitySetup table. The relation needs to be used the new field pointing to the hcmWork recid
  4. 4.       On hcmWorker field, whatever needs to be displayed from the dropdown needs to be drag and dropped into the autoidentification group.
  5. 5.       On the ProjTable form, to either drag and drop from datasource or right click to create a new reference group.  


Monday, September 30, 2013

KeyReplacement

static void Job1(Args _args)
{
    VendTable vendTable;
    DirPartyTable dirpartyTable;
    int myCount;
    DirPartyPostalAddressView   addressView;
    DirPartyLocation dirpartyLocation;
    str 100 strStreet;
    LogisticsLocation logisticsLocation;
    LogisticsPostalAddress logisticsPostalAddress;
    RecId   recID;

    /*
    while select forupdate * from dirPartyTable
        where dirpartyTable.Name == 'Admin UnemploymentCompensation'
    {
        myCount++;
    }
    info(strFmt("%1", myCount));

    changeCompany('LOG')
    {
    strStreet = 'State of Connecticut; Dept. of Labor # 4713 PO Box 2905';
    select * from addressView
       where addressView.Street == strLRTrim(strStreet);
    info(strFmt("%1", addressView.RecId));
    }
      */
changeCompany('VER')
    {
        //Admin UnemploymentCompensation
        while select forupdate * from dirPartyTable
            where dirpartyTable.Name == 'Admin UnemploymentCompensation'
        {
            if(dirpartyTable.RecId)
            {
               while select forupdate dirpartyLocation
                    where dirpartyLocation.Party == dirpartyTable.RecId
               {
                   
                   logisticsLocation = LogisticsLocation::find(dirpartyLocation.Location, true);
                   logisticsPostalAddress = LogisticsPostalAddress::findByLocation(logisticsLocation.RecId, true);
                     
                    ttsBegin;
                    logisticsPostalAddress.doDelete();
                    logisticsLocation.doDelete();
                    dirpartyLocation.doDelete();
                 
                    ttsCommit;
                 
               }
               
               select forupdate * from vendTable
               where vendTable.Party == dirpartyTable.RecId;
                if(vendTable.RecId)
                {
                    ttsBegin;
                    vendTable.doDelete();
                    dirpartyTable.delete();

                    ttsCommit;
                }
               
            }
        }

    }


}

TempTable

static void CopyPersistedTableToInMemoryJob(Args _args)
{
    CustTable custTable;
    CustTable custTmpLedger;

    custTmpLedger.setTmp();
    custTable.recordLevelSecurity(true);

    while select * from custTable where custTable.AccountNum like '1*'
    {
        custTmpLedger.data(custTable.data());
        custTmpLedger.doInsert();
        info(strFmt("Inserted a row for AccountNum = %1",custTable.AccountNum));
    }

    custTmpLedger = null;
}
The code copies data from the CustTable into an InMemory table. 
The setTmp method is used to create the same structure as it is on the physical table. The method changes the value from TableType::regualar to TableType::InMemory. A TempDB table is instantiated in the underlying database management system when the first SQL operation is sent to the database system from the AOS. The SQL operation can be either select, insert, update, or delete.
Variable goes out of scope. The typical case is that a method declares a variable for the particular TempDB type. The method inserts data into the TempDB table. The insert causes the table to be instantiated in the database. When the method finishes, all variables declared in the method go out of scope. The AOS tells the database system to drop the TempDB table when its corresponding variable does out of scope.

Create a TestTmp as InMemory table with Id a string type as a field.
static void TestTmpInMemory(Args _args)

{

    TestTmp tmp1, tmp2;
    ;

    tmp1.Id = "1000";
    tmp1.insert();

    tmp2.setTmpData(tmp1);

    info("Tabletype: " + enum2Str(tmp1.getTableType()));
   
    info("tmp1data begin: ");

    while
        select tmp1
            info("    Id " + tmp1.ID);


    info("tmp1data end.");

    info("tmp2 data begin: ");

    while
        select tmp2
            info("    Id " + tmp2.ID);

    info("tmp2 data end.");

}

Thursday, September 26, 2013

Insert and Delete

 LOGISTICSADDRESSCOUNTRYREGIONTRANSLATION country;

    select forupdate country where country.LanguageId == 'en-us';
    country.CountryRegionId = 'Canada';
    country.ShortName = 'Canada';
    country.LongName = 'Canada';
    country.insert();
   
    ttsBegin;
    while select forupdate country where country.LanguageId == 'en-us' && country.CountryRegionId == 'Canada'
    {
        country.delete();
    }
    ttsCommit;

   

Wednesday, September 25, 2013

Run a Query

static void Job3(Args _args)
{
    Query       q;
    QueryRun    qr;

    VendTable   vendTable;
    LogisticsLocation logisticsLocation;
    LogisticsPostalAddress postalAddress;


    q = new Query(queryStr(VendTableListPagePreviewPane));
    qr = new QueryRun(q);
    while (qr.next())
    {
        vendTable = qr.get(tableNum(VendTable));
        logisticsLocation = qr.get(tableNum(LogisticsLocation));
        postalAddress = qr.get(tableNum(LogisticsPostalAddress));

        info(strFmt("Account: %1, location; %2, County: %3", VendTable.AccountNum,
            logisticsLocation.LocationId,
            postalAddress.County));
    }
}

Apply Style


DEV and TEST

        On Dev
1.       Each team must use separate code prefix, label file and model:
a. All objects (except label file) must belong to model.
b. If development is going on DEV server - CUS layer must be used and
c. All changes must be checked in to MorphX VSS.
d. For development on other environments, please, use VAR layer.

To Test
                a. Delivery into TEST will be done by models.
                b. Delivery will be done during maintenance windows, so plan your work accordingly.
                c. Direct changes in TEST CUS layer are prohibited. If you need to apply quick fix/change you can do it in USR layer and move it to DEV. USR layer on TEST will be deleted on next deployment.
                d. If you are ready to move code to TEST:
- check that all objects are included in your model;
- check that code in model compiles without errors;
- notify my team. If you are ready with some piece, but you need to continue – please, export your model and label file in separate folder – and notify my team (same for external development).

Edit a page 

HTTP:\\MYSHAREPOINT?ToolPaneView=2&pagemode=edit

Tuesday, September 24, 2013

Args

static void OpenFormFunction()

Object formRun;
Args args = new Args();
;
args.name(formstr(CustTable));
args.record(CustTable::find('ABC'));

formRun = ClassFactory.formRunClass(args);
formRun.init();

formRun.yourmethodgoeshere(); /* !!

formRun.run();
formRun.wait();
}

Saturday, September 14, 2013

AXModel

What is the difference between MenuItem types - Display, Action and Output
output normally run reports
action normally run classes
display normally run forms


PS C:\>Export-AXModel -model Packaging -file c:\models\Packaging.axmodel

This example installs the MyModel file into the same layer that it was exported from.
Any conflicts will be pushed to the related update layer.
PS C:\>Install-AXModel -File MyModel.axmodel -Conflict Push

http://technet.microsoft.com/EN-US/library/hh352314.aspx

Friday, September 13, 2013

DAX Cue

Create a table, then a query, next create a menuitem. The menu item will use the Query, if not a form involved, in the objectType, it's Query, Object: [QueryName]; if it has form, it's Form and FormName.
Lastly, create a cue, put the label name and MenuItemName.

When all's done, Cue can be added from the Form and EP.

To Create the Menu Item

  1. In the AOT, expand Menu Items, right-click Display, and then click New Menu Item.
  2. Click the new menu item. In the property sheet, click Name and specify a name that uniquely identifies the menu item.
  3. Click Label and select the label for the menu item. The menu item label appears in the FactBox when the Label property of the cue is empty.
  4. Click Query and then click the name of the query that you want to use to retrieve the data records.
  5. Click ObjectType and select Form.
  6. Click Object and then click the name of the form that you use to show the records from the query. If you do not want to open a form from the cue, you can leave the property empty.
  7. If the cue appears in Enterprise Portal, click WebMenuItemName and select a WebMenuItem. Use a WebMenuItem that opens a list page that can display the set of records that the cue retrieved.
  8. Right-click the menu item and then click Save.

To Create the Cue

  1. In the AOT, expand Parts, right-click Cues, and then click New Cue. A cue is added to the list of Cues in the AOT.
  2. Click the new cue. In the property sheet, click Name and specify a name that uniquely identifies the cue.
  3. Click Label and select the label you want to appear in a FactBox. This step is optional. If the label is not specified, the cue uses the label from the menu item.
  4. Click MenuItemName and select the menu item that you created for the cue. This menu item specifies the query you want to use, as well as the form (for the Microsoft Dynamics AX client) or list page (for EP) that will be opened when the cue is clicked.
  5. Right-click the cue and then click Save.

Remove Duplicates

--select * from LOGISTICSADDRESSZIPCODE where ZIPCODE = '92620'

create table #TempRECID (myRecID bigint)

insert into #TempRECID (myRecID)
select A.RECID
from LOGISTICSADDRESSZIPCODE A
inner join LOGISTICSADDRESSZIPCODE B
on A.ZIPCODE = B.ZIPCODE
and A.COUNTY<>B.COUNTY
and B.COUNTY <> ''
--and B.ZIPCODE = '92620'

--select * from #TempRECID

delete AA from LOGISTICSADDRESSZIPCODE AA
inner join #TempRECID B on AA.RECID = B.myRecID
--where RECID = '5637148309'

drop table #TempRECID

--select * from LOGISTICSADDRESSZIPCODE where RECID =  '5637148309'

===========================================================
update dbo.LOGISTICSADDRESSZIPCODE
set ZIPCODE = '0' + ZIPCODE
where len(zipcode) = 4

Thursday, September 12, 2013

Master Page and Logo

In dynamics ax 2012, you can change the logo from C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ep\images\ location and change the name you your logo file to DynamicsLogo64x64.png

Dynamics AX 2009 Enterprise Portal was designed with templates compatible with SharePoint 2007, and they are in this compatible mode in SharePoint 2010. Hence not all features are compatible for example with SharePoint Designer. Hence you are unable to edit the master page using this tool. Even if you could edit the master page using the SharePoint Designer, every time you update Enterprise Portal, say using AXUPDATEPORTAL.EXE, it would override the default master page with the copy stored in the AOT.
So therefore to edit the master page do the following:
1. Go to: AOT\Web\Web Files\StaticsFiles\defaultax
2. Export defaultax object
3. Make a backup copy
4. Edit this defaultax object, which is the master page, in NotePad for example
5. Import the new modified master page back into the AOT
6. Run AXUPDATEPORTAL.EXE to deploy the edited master page

Sunday, September 8, 2013

Form Method


FormDataSource.Init
This method initializes the data source and is called from the super() of FormRun.Init(). The method is only called once when the form opens. The main task for this method is to initialize the query used for fetching data. To modify or replace the query automatically created by the form, do this after
the super() call of this method.

FormDataSource.InitValue
This method is used to initialize a new record with default values. The super() of this method calls the initValue() method on the underlying table. If you have initialization that applies system-wide, put the code on the table.

FormDataSource.Active
This event is called when a new record becomes active in the data source. This method is typically overridden to change properties which depend on the contents of the current record: Commonly this method will:
Modify permissions to the data source
Modify permissions to the fields
Enable/Disable buttons

FormDataSource.LinkActive
This method is the engine that joins data sources. This method is called on the joined data source every time that the active record in the main data source is changed. The method is also called when the form is opened as the system tries to join the calling data source to the main data source of the called form.

FormDataSource.ValidateWrite
This method validates an insert or update of a record. The super() of this method calls the corresponding method on the underlying table. If you need to distinguish between an insert and update, make a condition on the RecId field, which only has a value if it is an update.

FormDataSource.Write

This method controls the insert and update of records. The super() of this method calls the corresponding method on the underlying table. If you have form-specific tasks to perform in relation to the commitment of the record, add it here.

Friday, September 6, 2013

Update Recordset


VendTable vendTable;

    ttsBegin;

    update_recordSet vendTable

        setting VendGroup = "GEN"

            where vendTable.VendGroup == "CORPORATE";

    ttsCommit;

Monday, August 26, 2013

Cardinality and Related Table Cardinality

The Cardinality property corresponds to the notation on the child table (SalesLine in this case). So it should be ZeroMore (0...*). RelatedCardinality property corresponds to the notation on the parent table (SalesTable). So it should be ExactlyOne (1…1).

Semantically, Cardinality specifies how many instances of SalesLine row can be related to a single instance of SalesTable row. ZeroMore means that for every sale order, there can be zero, or more sales lines related to it. If the business requirement dictates that you need to have at least one sales line to be able to create a sales order, the Cardinality would be OneMore (1…*).

RelatedCardinality specifies how many instances of SalesTable row can be related to a single instance of SalesLine row. ExactlyOne means that every sales line should belong to one and only one sales order.

Wednesday, August 7, 2013

Project and TimeSheet

From project --> Activity --> Scheduling and Resource Requirement, it allows skills to be added, and resources to be assigned.

At the project detail level, it also has scheduling and resource assignment fast tab.

Question is: how to bring in a resource to the project?

Monday, July 29, 2013

pack and unpack

The pack method must be able to read the state of the object and return it in a container. Reading
the state of the object involves reading the values of the variables needed to pack and unpack the
object. Variables used at execution time that are declared as member variables don’t have to be
included in the pack method. The first entry in the container must be a version number that identifies
the version of the saved structure.
container pack()
{
return [#CurrentVersion, #CurrentList];
}
Where #CurrentList is defined as:
#LOCALMACRO.CurrentList
TransDate,
Specification,
ProdJournal,
UpdateLedger,
#ENDMACRO

To run the business operation job on the server and push the dialog box to the client. When the job is initiated, it starts on the server, and the RunBase framework packs the internal member variables and creates a new instance on the client, which then unpacks the internal member variables and runs the dialog box.

On the client side, When the user clicks OK in the dialog box, RunBase packs the internal member variables of the client instance and unpacks them again in the server instance.

Friday, July 19, 2013

Client and Server Side Report

The MorphX framework is a proprietary client-side solution that is fully integrated
into the Microsoft Dynamics AX integrated development environment (IDE). In this model,
reports contain references to data sources that are bound to local Microsoft Dynamics AX tables and
views. They also define the business logic.

SSRS for Microsoft Dynamics AX, is a server-side reporting solution.
This framework takes advantage of an industry solution that offers comprehensive reporting
functionality for a variety of data sources. This platform includes a complete set of tools that you can
use to create, manage, and deliver reports. With SSRS, you can create interactive, tabular, graphical,
or free-form reports from relational, multidimensional, or XML-based data sources.

Thursday, July 18, 2013

Debug C# from code editor

1. Create a Class library project,
2. Add to AOT
3. Deploy to AOS
4. Create a client class

http://msdn.microsoft.com/en-us/library/hh129886.aspx

Update to Scheduling and Resource Assignment

           ttsBegin;
            psaActivitySetup = PSAActivitySetup::findActivityNumber(myActivityNumber);
            while select forUpdate psaActivitySetup
                    where PSAActivitySetup.ActivityNumber == myActivityNumber
            {
                psaActivitySetup.PSASchedStart = _startDate;
                psaActivitySetup.PSASchedEnd = dateMthFwd(_startDate, 1);
                psaActivitySetup.update();  
            }
            ttsCommit;

Wednesday, July 17, 2013

DateTime

    UTCdatetime startDate;
   
    startDate= DateTimeUtil::newDateTime(systemDateGet()+1,timenow());
    info(strFmt("%1", startDate));

   UTCdatetime datetimeTo;   
   datetimeTo = DateTimeUtil::addDays(_startDate,1);

    activities.ActivityNumber = smmParameters::getNumber(smmNumbSeqUsed::Activity);
                activities.Purpose =  _activityNum;
                activities.ResponsibleWorker = _worker;
                activities.StartDateTime = _startDate;
                activities.EndDateTime = datetimeTo;
                activities.insert();

Thursday, July 11, 2013

(Sub)Project

static void SR_CreateProjects_subProjects(Args _args)
{
ProjInvoiceTable projInvoiceTable;
ProjTableType projTableType;
NumberSeq projNumberSeq;
ProjId projIdLastSon, projectId;
ProjTable projTable;
ProjId projMask;
Integer sonNum;
ProjType _projType = ProjType::TimeMaterial;
ProjGroupId _projGroupId = ‘TM1′;
ProjName _projName = “Construction – Prj”;
ProjInvoiceProjId _projInvoiceProjId = ’100104′;
ProjParentId _projParentId = ’121′; // define parent project here
NumberSequenceFormat _numberSequenceMask;
ProjLinePropertyId _projInvoiceStatusId;
;
if (_projParentId)
{
projTable.type().initFromParent(_projParentId);
projIdLastSon = ProjTable::projIdLastSon(projTable.ParentId);
if (projIdLastSon)
{
projMask = substr(projIdLastSon, strlen(projTable.ParentId) + 1, (strlen(projIdLastSon) – strlen(projTable.ParentId) + 1));
sonNum = ProjTable::numRemTemplate(projMask, ProjTable::find(projTable.ParentId).Format);
}
projTable.ProjId = projTable.ParentId + NumberSeq::numInsertFormat(sonNum + 1, ProjTable::find(projTable.ParentId).Format);
}
else
{
projNumberSeq = NumberSeq::newGetNum(ProjParameters::numRefProjId(), true);
if (projNumberSeq)
{
projTable.ProjId = projNumberSeq.num();
}
}
projectId = projTable.ProjId;
projTable.Type = _projType;
projTable.ProjGroupId = _projGroupId;
projTable.Name = _projName;
projTableType = projTable.type();
projTableType.initProjTable();
projTable.ProjInvoiceProjId = _projInvoiceProjId;
projInvoiceTable = ProjInvoiceTable::find(_projInvoiceProjId);
projTable.CustAccount = projInvoiceTable.InvoiceAccount;
projTable.initFromInvoice(projInvoiceTable);
projTable.Format = _numberSequenceMask;
projTable.CheckBudget = ProjGroup::find(_projGroupId).CheckBudget;
if (_projInvoiceStatusId)
{
ProjLinePropertySetup::updateLinePropertyProj(projTable.ProjId, _projInvoiceStatusId, TableGroupAll::Table, true);
}
projTable.initFromCustTable(CustTable::find(projTable.CustAccount));
if (ProjTable::exist(projTable.ProjId))
{
// Project already exists.
throw error(“@SYS56494″);
}
if (!projTableType.validateWrite())
throw error (“Validations failed”);
projTable.insert();
if (projNumberSeq)
{
projNumberSeq.used();
}
else
{
projTable.clear();
if (projNumberSeq)
{
projNumberSeq.abort();
}
}
info (strfmt(‘Project %1 successfully created ‘, projectId));
}

Tuesday, July 9, 2013

Calculate Code Execution Time

static void GetExecutionTime(Args _args)
{
    int start;
    int end;
    int i;
    start = WinAPI::getTickCount();
    for (i = i; i <= 1; i++)
    {
        sleep(1000); // pause for 1000 milliseconds
    }
    end = WinAPI::getTickCount();
    info(strFmt("%1", end - start));
}

Saturday, July 6, 2013

Store Dimension Attribute Value

static void XXXX(Args _args)
{
    DimensionAttribute dimAttr;
    DimensionAttributeValue dimAttrValue;
    Name dimName;
    Name dimValue;
    Common                          common;
    DictTable                       dictTable;
    Map dimSpec;
    dimName = "Department";
    dimValue = "025";
   
     dimSpec =
        DimensionDefaultingEngine::createEmptyDimensionSpecifiers();
   
    dimAttr = DimensionAttribute::findByName(dimName);
   
    dictTable = new DictTable(dimAttr.BackingEntityType);
    common = dictTable.makeRecord();
  
    select firstonly common where common.(dimAttr.ValueAttribute) == dimValue;
    if (common)
    {
        dimAttrValue = DimensionAttributeValue::findByDimensionAttributeAndEntityInst(
            dimAttr.RecId, common.(dimAttr.KeyAttribute),
            false, false);
       
        info(strFmt("%1", common.(dimAttr.ValueAttribute)));
    }
   
}

Find or Create Attribute Value
    select firstonly
            dimensionAttributeValue
        where
            dimensionAttributeValue.DimensionAttribute == _dimensionAttribute
            && dimensionAttributeValue.EntityInstance == _entityInstance
            && dimensionAttributeValue.IsDeleted == false;
    // Create value if specified and not currently existing
    if (!dimensionAttributeValue && _createIfNecessary)
    {
        ttsbegin;
        dimensionAttributeValue.DimensionAttribute = _dimensionAttribute;
        dimensionAttributeValue.EntityInstance = _entityInstance;
        dimensionAttributeValue.insert();
        dimensionAttributeValue.selectForUpdate(_forUpdate);
        ttscommit;
    }

Wednesday, July 3, 2013

Periodic Journal

Create a Journal name, with journal type as "Periodic", such as RentExp. optional with an offset account.

From General Ledger, Periodic, Journals, Periodic Journals, create a new periodic journal.
during creation, select journal name: RentExp created on step1.
From "Lines", enter line info.

From General Ledger, Journals, General Journals, create a new GJ. Pick up name as: GerJrn, go to "Lines", From Periodic Journal, Retrieve Journal. Then "Post".

Sunday, June 30, 2013

Customize Caller Args

From the caller:
    Args args;
    Object formRun;
    args = new Args();
    args.name(formStr(BudgetModelLookup));
    args.caller(_ctrl);
    formRun = classfactory.formRunClass(args);
    formRun.init();
    _ctrl.performFormLookup(formRun);

With the pre-built form BudgetModelLookup
the caller's formRun.init() invokes callee's init method, as the callee is a form itself.
 public void init()
{
    FormStringControl callingControl;
    callingControl = SysTableLookup::getCallerStringControl(
        this.args());
    super();
    budgetModelTree = BudgetModelTree::construct(
        ModelTree,
        callingControl.text());
    budgetModelTree.buildTree();
}
The callee contruct and build the UI. After the callee's init, the process flow goes back to the caller's code, which performs lookup
 _ctrl.performFormLookup(formRun);

Saturday, June 29, 2013

NonExist Join

static void Job7(Args _args)
{
    container ret;
    container data;
    CustTable custTable;
    InventBuyerGroupList groupList;
    InventBuyerGroup inventBuyerGroup;
    while select custTable
        notExists join firstOnly groupList
        where groupList.CustAccount == custTable.AccountNum
        join inventBuyerGroup
        where
         groupList.GroupId == InventBuyerGroup.Group
    {
       
        data = [custTable.AccountNum,
        custTable.AccountNum,
        custTable.name()];
        ret = conIns(ret, conLen(ret)+1, data);      
    }
}

select custTable
        notExists join firstOnly groupList
        where groupList.CustAccount == custTable.AccountNum
select all customers whose account number does not exist in GroupList(InventBuyerGroupList ).

Friday, June 28, 2013

Create Seq Handler

Methods to be added on the datasource (override)

Form method
public NumberSeqFormHandler numberSeqFormHandler() { if (!numberSeqFormHandler) { numberSeqFormHandler = NumberSeqFormHandler::newForm( CustParameters::numRefCustGroupId().NumberSequenceId, element, CustGroup_ds, fieldNum(CustGroup,CustGroup)); } return numberSeqFormHandler; } Override Datasource
public void create(boolean _append = false) { element.numberSeqFormHandler().formMethodDatasourceCreatePre(); super(_append); element.numberSeqFormHandler().formMethodDatasourceCreate(); }
public void delete() { ttsBegin; element.numberSeqFormHandler().formMethodDatasourceDelete(); super(); ttsCommit; }
public void write() { ttsBegin; super(); element.numberSeqFormHandler().formMethodDatasourceWrite(); ttsCommit; }
public boolean validateWrite() { boolean ret; ret = super(); ret = element.numberSeqFormHandler().formMethodDatasourceValidateWrite(ret) && ret; return ret; }
public void linkActive() { element.numberSeqFormHandler().formMethodDatasourceLinkActive(); super(); }
override form method public void close() { if (numberSeqFormHandler) { numberSeqFormHandler.formMethodClose(); } super(); }

Generate Number Seq

User CustGroupId as an example,
add code to class: NumberSeqModuleCustomer
    datatype.parmDatatypeId(extendedTypeNum(CustGroupId));
    datatype.parmReferenceHelp("Customer group ID");
    datatype.parmWizardIsContinuous(false);
    datatype.parmWizardIsManual(NoYes::No);
    datatype.parmWizardIsChangeDownAllowed(NoYes::Yes);
    datatype.parmWizardIsChangeUpAllowed(NoYes::Yes);
    datatype.parmWizardHighest(999);
    datatype.parmSortField(20);
    datatype.addParameterType(NumberSeqParameterType::DataArea, true, false);
    this.create(datatype);

Locate Table: CustParameters, add a new method to this table.
public server static NumberSequenceReference numRefCustGroupId()
{
    return NumberSeqReference::findReference(
    extendedTypeNum(CustGroupId));
}
To Test the sequence generated, create a job as the following:
static void number(Args _args)
{
    NumberSeq  numberSeq;
    CarId num;
    ;
    numberSeq = NumberSeq::newGetNum(CustParameters::numRefCustGroupId());
    num = numberSeq.num();
    info(num);
}
 

Wednesday, June 26, 2013

LinkType

The LinkType property determines how two data sources
are joined. The following list describes each option:
• Passive: The query on the joined data source is only
executed when the form is opened. A later change in the
controlling data source does not change the view.
• Delayed: The query on the joined data source is
executed every time that the controlling data source is
changed. The query execution is delayed to avoid the
fetch of data, if the controlling data source is changed
multiple times in a short time. This is the case when the
user is scrolling through data on a grid.
• Active: This option is similar to Delayed, except there is
no delay before the query is executed.
• InnerJoin: Selects records from the main table that have
matching records in the joined table and vice versa. If
the joined table does not have any records related to the
main table record, the main table record is not
displayed. Each match returns a result set of the main
table record and joined table record joined together as
one record. This is useful when wanting to display
records from both tables in a grid.
• OuterJoin: Selects records from the main table whether
they have matching records in the joined table. Each
match returns a result set of the main table record and
joined table record joined together as one record. If
there is no match, the fields from the joined table will be
empty.
• ExistsJoin: Selects a record from the main table only if
there is a matching record in the joined table. As soon as
a matching record is found, the main table record is
returned. The record in the joined table is never
retrieved.
• NotExistsJoin: Select records from the main table that
do not have a match in the joined table.

Tuesday, June 25, 2013

Map

    Map mapStateNumbers;
    MapEnumerator enumerator;
    CustTable custTable;
    mapStateNumbers = new Map(Types::String, Types::Integer);
    while select custTable
    {
        if(mapStateNumbers.exists(custTable.stateName()))
        {
        mapStateNumbers.insert(custTable.stateName(), mapStateNumbers.lookup(custTable.stateName())+1);
        }
        else
        {
        mapStateNumbers.insert(custTable.StateName(), 1);
        }
    }
    enumerator = mapStateNumbers.getEnumerator();
    while (enumerator.moveNext())
    {
        info(strfmt("%1 customers are located in %2.", enumerator.currentValue(), enumerator.currentKey()));
    }

Write a text file

    FileName fileName = 'c:\\test.txt';
    FileIoPermission permission;
    FileIO fileIO;
    str outputText;
    #File
    ;
    permission= new FileIoPermission(filename,#io_write);
    permission.assert();
    fileIO= new FileIO(filename, #io_write);
    if (fileIO)
    {
        outputText = "text that will go into the text file.";
        fileIO.write(outputText); //write the text to the file.
        fileIO.finalize(); //finish the file.
    }

Monday, June 24, 2013

Activities and SubActivities

Table HierarchyTreeTable and smmActivities contain activities and subactivities data.
In class HierarchTreeTable, method insertNode and insertRoot called from Form Hierarchy:write(), here is the node insertion:

ttsbegin;
        hierarchyTreeTable.clear();
        hierarchyTreeTable.HierarchyId = _hierarchyId;
        hierarchyTreeTable.ElementNumber = NumberSeq::newGetNum(CompanyInfo::numRefElementNumber()).num();
        hierarchyTreeTable.SiblingNumber = HierarchyTreeTable::nextSiblingNum(_hierarchyId, _parentElementNumber);
        hierarchyTreeTable.ParentElementNumber = _parentElementNumber;
        hierarchyTreeTable.ElementNodeType = ElementNodeType::Node;
        hierarchyTreeTable.Name = strLTrim(_name);
        hierarchyTreeTable.Path = HierarchyTreeTable::findElementNumber(_hierarchyId, _parentElementNumber).Path + hierarchyTreeTable.ParentElementNumber + #sharp;
        hierarchy=Hierarchy::find(_hierarchyId);
        smmActivities.clear();
        smmActivities.setActivityNum();
        smmActivities.Purpose =hierarchyTreeTable.Name;
        smmActivities.IsTemplate =hierarchy.IsTemplate;
        if (Hierarchy::isHierarchyTypeCRM(hierarchy.HierarchyType))
        {
            common = HierarchyLinkTable::findAssociation(hierarchyTreeTable.HierarchyId, Hierarchy::hierarchyType2tableId(hierarchy.HierarchyType));
            smmActivities.initFromCommon(common);
        }
        smmActivities.insert();
        hierarchyTreeTable.RefRecId = smmActivities.RecId;
        hierarchyTreeTable.insert();
        ttscommit;

Root Insertion:
  ttsbegin;
        hierarchyTreeTable.clear();
        hierarchyTreeTable.HierarchyId = _hierarchyId;
        hierarchyTreeTable.ElementNumber = NumberSeq::newGetNum(CompanyInfo::numRefElementNumber()).num();
        hierarchyTreeTable.SiblingNumber = 0;
        hierarchyTreeTable.ElementNodeType = ElementNodeType::Node;
        hierarchyTreeTable.Name = strLTrim(_name);
        if (isConfigurationkeyEnabled(configurationKeyNum(Project3)))
        {
            hierarchyTreeTable.psaLevelName = strLTrim(_name);
            hierarchyTreeTable.psaLevelDescription = ProjTable::find(strLTrim(_name)).Name;
            hierarchyTreeTable.psaNameLink = strLTrim(_name);
            hierarchyTreeTable.psaHierarchyLevelType = PSAHierarchyLevelType::Root;
        }
        hierarchyTreeTable.insert();
        ttscommit;

Thursday, June 13, 2013

Find a field -- ProfessionalTitle

select * from syscolumns A inner join sysobjects B
on A.id = B.id
where A.name = 'professionaltitle'

select professionaltitle,* from DMFEMPLOYEEENTITY where professionaltitle <> ''
select professionaltitle, * from dirpartytable where professionaltitle <> ''

AX Reference Sources

AX Server Team Blog:
http://blogs.msdn.com/b/daxserver/archive/2013/05.aspx
 
AX Support Blog:
http://blogs.msdn.com/b/axsupport/
 
AX Demo/Sample Solution Site:
https://mbs.microsoft.com/Cms/Templates/document/General.aspx?NRMODE=Published&NRNODEGUID=%7bACA31BB9-CB57-4B7F-A948-880FAEF28099%7d&NRORIGINALURL=%2fpartnersource%2fdeployment%2fmethodology%2fvpc%2fAX2012DemoToolsMaterials&NRCACHEHINT=Guest
 
AX UK Blog
http://blogs.msdn.com/b/ukax/
 
AX Business Intelligence
http://blogs.msdn.com/b/dynamicsaxbi/
 
http://blogs.msdn.com/b/saveenr/
 
http://blogs.msdn.com/b/mfp/

Table Permissions Framework

Set up TPF: http://msdn.microsoft.com/en-us/subscriptions/hh965683.aspx
Set up Record level permission: http://blogs.msdn.com/b/daxserver/archive/2013/05/13/enabling-field-level-authorization-in-ax-2012.aspx

Join 2 Tables

    Address addressTable2;
    AddressState addressStateTable3;
    struct sut4;
    ;
    sut4 = new struct("str StateName; str StateId; str Phone");

    while select *
        from addressTable2
            order by addressStateTable3 .Name
                , addressTable2 .Phone
        join addressStateTable3
            where addressTable2 .State ==
                addressStateTable3 .StateId
                && addressTable2 .State LIKE "N*"
    {
        sut4.value("StateName", addressStateTable3 .Name );
        sut4.value("StateId", addressStateTable3 .StateId );
        sut4.value("Phone", addressTable2 .Phone );

        info(sut4.toString());
    }

Group and Order
static void SelectGroupBy6Job(Args _args)
{
    Address tabAddress;
    AddressState tabAddressState;
    ;
    info("Start of job.");

    WHILE SELECT
        count(RecId)
        from tabAddress
        join tabAddressState
            GROUP BY
                tabAddress .State
                ,tabAddressState .Name
            ORDER BY tabAddress .State desc
            where
                tabAddress .State like "*N*"
                && tabAddressState .StateId == tabAddress .State
    {
        info(strFmt
            ("%1 = Count , StateId = %2 , StateName = %3"
            ,tabAddress .RecId ,tabAddress .State
            ,tabAddressState .Name
            ));
    }
    info("End of job.");
}

Tuesday, June 11, 2013

label files

C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin\Application\Appl\Standard
AxSysen-us.ald

Sunday, June 9, 2013

select for update

static void UpdateOrg(Args _args)
{
Organization tstOrg;
ttsBegin;
while select forupdate * from tstOrg
{
tstOrg.State = "IL";
tstOrg.NumberOfEmployees = tstOrg.NumberOfEmployees+10;
tstOrg.update();
}
ttsCommit;
}

Friday, June 7, 2013

Call WebService from AX

Create class library project in VS, add services references, add to AOT, and deploy the project to both AX server and client.
In AX, add code some reference code.

VERAxServicesNameServcies.AxCustomerSvc.CustomerClient customerClient;
    VERAxServicesNameServcies.AxCustomerSvc.ICustomer customer;
    System.Exception Exception;
     System.ServiceModel.Description.ServiceEndpoint endPoint;
    System.ServiceModel.EndpointAddress endPointAddress;
    System.Type type;
    ;
    try
    {
        // Create a service client proxy
        type= CLRInterop::getType('VERAxServicesNameServcies.AxCustomerSvc.CustomerClient');
        customerClient = AifUtil::createServiceClient(type);
          // Create and endpoint address, This should be a parameter stored in the system
        endPointAddress = new System.ServiceModel.EndpointAddress("http://10.242.25.20:8081/Customer.svc");
        // Get the WCF endpoint
        endPoint = customerClient.get_Endpoint();
        // Set the endpoint address.
        endPoint.set_Address(endPointAddress);
        // Use the zipcode to find a place name
        //customer= customerClient.GetCustomer(1);
        // Use the getAnyTypeForObject to marshal the System.String to an
        //Ax anyType
        // so that it can be used with info()
        info(strFmt('%1', CLRInterop::getAnyTypeForObject(customerClient.GetCustomerTest(1))));
    }
    catch
    {
        // Get the .NET Type Exception
        exception = CLRInterop::getLastException();
        // Go through the inner exceptions
        while(exception)
        {
            // Print the exception to the infolog
            info(CLRInterop::getAnyTypeForObject(exception.ToString()));
            // Get the inner exception for more details
            exception = exception.get_InnerException();
        }
    } 

Wednesday, June 5, 2013

AxInternalBase Class

The AxBC table classes have the following characteristics:
  • A One-to-one relationship between class and table.
  • The classes use the same name, with an Ax prefix, as the corresponding table. For example, the AxSalesTable class changes records in the SalesTable table.
  • The interface methods on the AxBC table class for fields in the related table use the same name, with a prefix of parm, as the fields on the table.For example, the parmCustAccount method on the AxSalesTable class is used externally to set or get the value of the CustAccount field in the SalesTable table.
  • The classes change the table records directly by setting the values of the table field and executing the update and insert methods on the table.
  • The default business logic is performed in the methods the methods that have a prefix of set. For example the setCurrencyCode method on the AxSalesTable class is used internally to set the value of the CurrencyCode field in the SalesTable table, depending on the value in the CustAccount field.
http://msdn.microsoft.com/en-us/library/axinternalbase.aspx

 

Sunday, June 2, 2013

Interop

Add AX table directory to Visual Studio library project, use C# code to access AX data.
http://msdn.microsoft.com/EN-US/library/gg889200.aspx
  • Creating the class library, adding a Microsoft Dynamics AX table to the project, and accessing that table from code.
  • Creating a console application project and testing the generated assembly.

create user control for EP

Dynamics AX user controls are ASP.net user controls developed in Visual studio 2008, parked in Dynamics AX application and can be used in Enterprise portal.
In section 1, I explained about how to create a simple user controls and in section 2, I explained about how to connect the web parts and in section 3, I explained about how to call X++ methods within the web part.

Section 1
Create a Dynamics web project in visual studio:
Open visual studio 2008.
File ->New ->Web site.
In new website selection screen select the template ‘Dynamics AX web project’ under ‘My templates’ section.
Select Location as HTTP and provide the project name
Language: Visual C#
A new solution will be created with a project.
Right click the solution project and select ‘Add New Item’.
Select the ‘Dynamics AX User control’ as template which is under ‘My templates’.
Provide the name of the control.(SampleUserControl)
Select the language as the C#. Click OK.
New control “SampleUserControl” will be added to project.
Add the control to Dynamics AX AOT :
Right click the control and select “Add to AOT’. The user control will be added to AOT under AOT/Web/Webfiles/Web Controls.
Also a managed content item will be created under AOT/Web/Web Content/Managed which is pointing to user control SampleUserControl. This managed content item will be visible in enterprise portal.
Create a dataset in AX
Data set acts as a data source for the user control. Data sets can be created in AOT.
Under AOT/data sets, Create a new data set and name it as CustomerSample.
Data sets look exactly like the AX forms without design node.
For the CutomerSample data set add CustTable as the data source.
Save the dataset.
Design the control in visual studio:
Now go to visual studio and right click the sampleUserControl and click view designer.
Design mode of the control will be opened.
Now open the tool box and you find the Dynamics AX group in the tool box.(If it is not available then right click on tool bar and choose items they are available in .Net Framework Components tab)
Add the AXDataSource from the tool box.
Rename the data source to SampleCustDS.
Select the SampleCustDS in design mode and click > mark at the right top corner.
It asks for Dataset name. Select the ‘CustomerSample’ that you have created in AOT.
Now add AX grid view.
Select the AX grid view and open the properties screen and select the datasource as SampleCustDS and then > mark and then Edit columns option.
Under available fields from bound fields add AccountNum,Name, InvoiceAccount,CustGroup, Currency to selected fields.
Now right click the SampleUserControl and select ‘Refresh AX user control’. This will refresh the changes to AOT.
Creating a web part page and adding user control to it:
Now open Enterprise portal and select site actions and then create.
Select web part page under web pages.
Give the name of the page as “CustomerSample” and then choose the layout.
Select the document library as “Enterprise portal”.
A new blank page will be created.
Here we need to add the required web parts.
Under the body section click “add a web part”.
From the dialog opened select the “Dynamics User control web part” to hold the user control that we have just created and then click ‘Add’.
A blank Dynamics User control web part will be added. Now go to Edit at the right corner of the web part and click the arrow mark and then modify shared web part from the context menu.
Dynamics AX web part properties editing window will be opened to the right of the page.
Under the managed content item select the “SampleUserControl” managed content item. Click Apply and then OK.
Exit the edit mode by clicking exit edit mode below the site actions.
This will display the data from the Custtable in the design that you have created in visual studio.
Section 2
Go to visual studio 2008.
Add a new control named “CustomerContacts” to the same project that you have created above. Go to design part and add the AXDataSource’ control and rename to ‘CustDS’ and point to same dataset “CustomerSample”.
Add the AX form control and Select the datasource as ‘CustDS’ that you have created just now by clicking on > at the top right of form control. Select the datamember property(CustTable_current) also from properties window in visual studio.
Add an AXGroup inside AX form from tool box.
Select the AX group and go to properties window. For the fields property click on … symbol. A new window opens.
Select the required fields like Address, Phone, TeleFax, URL, Email etc,.
Add the user control to AOT.
Now go to the same EP page that you have created in section I above and add the new user control web part and point to the “CustomerContacts” managed content item.
Connecting two web parts:
By selecting the modify shared web part option, open the Dynamics properties window for first web part (SampleUserControl) and make the web part role as provider.
Open the Dynamics properties window for second web part (CustomerContacts) and make the role as Consumer to make this web part the consumer of information.
Now apply the changes by clicking on Apply.
Now on the first web part click on Edit and then connections àSend AX contextList to and then select the second web part.
You can verify it from the second web part; here it shows as ‘Connections àGet AXContextList from àfirst web part name.
Now if you change the section in the first web part that is if the customer selection is changed in grid the contact details for customer will automatically be changed in the second web part.
Section 3
In the first two examples we have written user controls without writing a single line of C# code. And we did not call X++ methods explicitely. But, sometimes it may be required to call the X++ class methods or table methods to get the related information. For example if the customer type is organization then we may need to get the number of employees, organization number of the customer (These details can be seen on Details tab of CustTable form in rich client.) Which are being stored in table DirOrganizationDetail. In the same way if the customer type is person we may need BirthDate and MarritalStatus which are stored in DirPersonPartyDetail. This we can achieve by writing a code in C# to call X++ methods.
I will explain how to get these details by calling table methods.
Import the below namespaces before adding the following lines of code.
Microsoft.Dynamics.Framework.BusinessConnector.Adapter
Microsoft.Dynamics.Framework.BusinessConnector.Session
For the above control CustomerContacts add the field lblNumberOfEmployees. This will display the number of employees in the selected customer organization.
In the pade_load () of CustomerContacts write the following line if code.
AxBaseWebPart.GetWebpart(this).ExternalContextChanged+=new EventHandler<Microsoft.Dynamics.Framework.Portal.UI.AxExternalContextChangedEventArgs>(CustomerContacts_ExternalContextChanged);
And write the event hander for ‘CustomerContacts_ExternalContextChanged’ like below
void CustomerContacts_ExternalContextChanged(object sender, Microsoft.Dynamics.Framework.Portal.UI.AxExternalContextChangedEventArgs e)
{
IAxaptaRecordAdapter currentRecord = AxBaseWebPart.GetWebpart(this).ExternalRecord;
ISession currentSession = AxBaseWebPart.GetWebpart(this).Session;
IAxaptaRecordAdapter recordAdapter;
object obj1,obj2;
string partyId;
IAxaptaAdapter axAdapter = currentSession.AxaptaAdapter;
obj1 = axAdapter.CallStaticRecordMethod(“CustTable”, “find”, currentRecord.GetField(“AccountNum”).ToString());
recordAdapter = axAdapter.CreateAxaptaRecord(obj1);
partyId = recordAdapter.GetField(“partyId”).ToString();
obj2=axAdapter.CallStaticRecordMethod(“DirOrganizationDetail”,”find”,partyId);
recordAdapter = axAdapter.CreateAxaptaRecord(obj2);
lblNumberOfEmployees.Text = recordAdapter.GetField(“NumberOfEmployees”).ToString();
}
Here first I get the customer record by passing the customer id that was selected in the grid.
And then get the party id of the customer from the customer record. Pass the party id to find method of DirOrganizationDetail to get the DirOrganizationDetail record.
From DirOrganizationDetail we can get number of employees directly.

Post from: http://blogs.bojensen.eu/?p=225