Skip to main content

Posts

Showing posts from February, 2012

How to reverse these hundreds of posted inventory movement journals by X++ code

If you wanted to automate the reversing process in code, you could use InventJournalCopy something like this (look at InventJournalCopy::main(..) for usage hints) InventJournalCopy copy = InventJournalCopy::construct(); copy.getLast(); copy.parmJournalIdFrom( journalNumHere ); copy.run(); This is not tested code. Use at your own risk.

query range example

queryBuild     = this.query(); confirmedDlv = queryBuild.dataSourceTable(tablenum(PurchLine)).addRange(fieldnum(PurchLine, ConfirmedDlv)); confirmedDlv.value(queryRangeConcat(strfmt('..%1', queryValue(collectionLetterDate-1)), *queryNotValue(dateNull())*)); queryRun_PurchCollectionLetterList            = new QueryRun(queryBuild); //PUT BREAKPOINT HERE

Set up a new number sequence (e.g. for Blanket Orders)

New Data Type Add a new SalesParameters method: static client server NumberSequenceReference numRefSalesIdBlanket() { return NumberSeqReference::findReference(typeId2ExtendedTypeId(typeid(SalesIdBlanket))); } Assign SalesId in the somewhere during the creation of the blanket order: salesTable.SalesId = NumberSeq::newGetNum(SalesParameters::numRefSalesIdBlanket()).num(); Add new reference to NumberSeqReference_SalesOrder class (used to add the new number sequence to the list in the CustParameters form) protected void loadModule() { NumberSequenceReference numRef; ; //blanket order number sequence numRef.DataTypeId = typeId2ExtendedTypeId(typeid(SalesIdBlanket)); numRef.ReferenceHelp = literalstr("@SYS53960"); numRef.ReferenceLabel = literalstr("@CUS525"); numRef.WizardManual = NoYes::No; numRef.WizardAllowChangeDown = NoYes::No; numRef.WizardAllowChangeUp = NoYes::No; numRef.SortFie

Reset program cache file for all clients

I had a problem with some clients running on old code (e.g. experiencing bugs that have already been fixed). The following job resets the GUID which in turn forces all clients to create a new program cache file (AX_*.auc). A restart of the AOS is required. static void forceNewAUC(Args _args) { #AiF SysSQMSettings sysSQMSettings; ; ttsbegin ; update_recordset sysSQMSettings setting GlobalGUID = str2Guid(#EmptyGuidString); ttscommit ; info(" Restart the AOS Service to generate a new GlobalGUID."); } Another option would be adding the following to the Info.startupPost() method, however I’m not sure if this is a cleaver thing to do routinely, but if you cannot restart the AOS it is you only option. xSession::removeAOC(); SysTreeNode::refreshAll(); SysFlushDictionary::doFlush(); SysFlushAOD::doFlush(); xSession::updateAOC();

Fetch number of records in a FormDataSource (e.g. of a Grid)

SysQuery::getTotal works fine, but the trick is how to handle temporary data sources where getTotal does not work: if(!formDataSource.curser().isTmp()) { recordCount = SysQuery::getTotal(formDataSource.queryRun()); } else { while(!formDataSource.allRowsLoaded()) { formDataSource.getNext(); } recordCount = formDataSource.numberOfRowsLoaded(); } Now recordCount contains the number of Records in the FormDataSource irrespective of the Tmp status of the data source. Of course the whole tmp-data source has been loaded in the process, which might be an issue in some cases.

Allow changing of Inventory UnitId

Dynamics Ax does not allow you to change the Inventory UnitId of an item if there is stock or if there are open transactions. We as a food business wanted to change the inventory unit of canola oil from 200L barrels to 1000L Pallecons (or even better to Ltrs, avoiding future changes of the Inventory Stock Item), but as nearly every one of our products use canola oil the will be no point in time without open transactions (production orders) and deleting and reentering all open orders is also a nuisance. Therefore I decided to make a mod which allows us to change to unitId with open transactions and existing stock. The modifications only need to be done in the update method of the InventTableModule table and are marked with //bw . IMPORTANT: The code here only updates InventTrans and InventSum. There are other tables with Quantities referring to the Inventory Unit such as InventJournalTrans which I do not update. This leads to inconsistencies which we can live with but you might not! vo

Delete Invoice double posted

Table affected: 1. InventTrans Key: Voucher 2. VendInvoiceJour Key: internalInvoiceId = Voucher 3. VendInvoiceTrans Key: internalInvoiceId = Voucher 4. VendInvoicePurchLink Key: internalInvoiceId = Voucher 5. VendTrans Key: Voucher 6. VendTransOpen Key: RefRecId = vendtrans.recid 7. LedgerTrans Key: Voucher delete all those record stored in the above table and 1. Create a job static void updateInventSum(Args _args) { InventSumReCalcItem inventSumRecalcItem = new InventSumreCalcItem("0100000000117-AV",true,CheckFix::Fix); ; inventSumrecalcItem.updateNow(); } Pass in the itemid affected or InventSumReCalcItem InventSumReCalcItem; ; InventSumReCalcItem = new InventSumReCalcItem("YourItemId" ); InventSumReCalcItem.updateNow(); 2. Run General Ledger->Periodic->Recalculate Ledger Balance

Get the next unique file name

Sometimes you are saving a temporary file so you don't want to delete or overwrite anything that already exists in a directory...there is a nice function to find the next unique file name fileNameTemp = Global::fileNameNext(fileOriginalsPath + fileName);

image exists in the correct directory

The code checking that the image exists in the correct directory has a BUG and well it is overly complex (KISS).  Just replace this method below... public static boolean isFromAttachmentsFolder(str _pathName) { str attachmentsFolder; str pathName; ; // Fix embedding images in emails attachmentsFolder = SysEmailParameters::find().AttachmentsPath; pathName = _pathName; attachmentsFolder = Global::strReplace(attachmentsFolder,'\\','/'); pathName = Global::strReplace(pathName,'\\','/'); // Fix embedding images in emails return Global::strStartsWith(strupr(pathName), strupr(attachmentsFolder)); }

rename items quickly

Here is a little script to rename items quickly. Just provide a CSV file with old and new item number. I found that making the CSV file was faster than making a job to intelligently rename items because I could use Excel functions and review the new item numbers faster static void renameFromCSV(Args _args) { #File CommaIO file; Container values; Dialog dialog; DialogField dfFileName; LineNum lineNum; ItemID itemIDfrom; ItemID itemIDto; InventTable inventTable; ; setPrefix(funcName()); dialog = new Dialog("Rename items"); dialog.filenameLookupFilter(["@SYS39829", #AllFilesName + #csv]); dfFileName = dialog.addField(typeid(FileNameOpen)); if (!dialog.run() || !dfFileName.value()) return; if (!WinAPI::fileExists(dfFileName.value())) throw error(strfmt("@S

Modifying tab order

In AX to change tab order normally you change the order of the controls in the form. You can set the "Skip" property on a control to Yes and when the user tabs it will skip that control. Certain forms where there are many groups or process flow should be different than the default way require a different approach. There is a way to specify the tab order for an entire form. First set AutoDeclaration to Yes on all your controls. Second override the init method and provide it an array of control IDs in the order that you want to tab void init() { Array tabOrder = new Array(Types::Integer); ; super(); tabOrder.value(1, Control1.id()); tabOrder.value(2, Control2.id()); tabOrder.value(3, Control3.id()); tabOrder.value(4, Control4.id()); tabOrder.value(5, Control5.id()); tabOrder.value(6, Control6.id()); tabOrder.value(7, Control7.id()); tabOrder.value(8, Control8.id()); tabOrder.value(9, Control9.id()); tabOrder.value(1

Disable users who are not active in Active Directory

Occasionally when auditors come by I like to disable all user accounts in AX which have been disabled in Active Directory. Even though AD will not let them login auditors have a hard time understanding it, so I disable the users. Many times we do not get notification that someone has left the company, or sometimes it does not reach the right people in charge of AX security. So I made the job below which disables users in AX because they are disabled in Active Directory. The job takes a little while to run static void disableUsersMissingInAD(Args _args) {     UserInfo userInfoUpdate;     xAxaptaUserManager xAxaptaUserManager;     xAxaptaUserDetails xAxaptaUserDetails;     #Guest ;     s uper();     xAxaptaUserManager = new xAxaptaUserManager(); Global::startLengthyOperation();     ttsbegin;     while select forUpdate userInfoUpdate order by networkAlias where userInfoUpdate.Id != #GuestUser && userInfoUpdate.enable ==

Go To Main Table Functionality in Axapta

This is done by three ways: EDT Relations: If you use an EDT in tables which have relation with some other table fileds, that time you can able to navigate the main table or main form. FormRef Property: Select the Table and go to properties and select the required form in the FormRef property. JumpRef method: If you are not having that option, simply write a override the JumpRef method in a field of DataSource or in a Form Control. Here i show you a sample jumpRef method code: public void jumpRef() { Args args; MenuFunction menuFunction; ; args = new Args(); menuFunction = new MenuFunction(menuitemDisplayStr(“FormName”), MenuItemType::Display); args = new Args(menuFunction.object()); args.caller(element); args.record(“RecordName”); // to be a datasource which added in the current form menuFunction.run(args); } In the form init() which we called here, we have to check the condition whether the form is called by any dataset or not.

Dynamic Query Using Aggregative Functions and Joins

use the Aggregate functions and Joins in Dynamic Query in spite of using normal Select statement in Ax 2009 Normal Select Statement: while select sum(qty) from inventTrans where inventTrans.ItemId == “OL-2500” join inventDimgroup by inventBatchId where inventDim.InventDimId == inventTrans.InventDimId { // Our Code Here } Dynamic Query: static void Vasanth_Query_Eg1(Args _args) { Query query; QueryBuildDataSource queryBuildDataSource; QueryBuildRange queryBuildRange; QueryRun queryRun; Qty total; InventTrans inventTrans; ; query = new Query(); queryBuildDataSource = query.addDataSource(tableNum(InventTrans)); queryBuildDataSource.addSelectionField(fieldNum(InventTrans,Qty), SelectionField::Sum); queryBuildDataSource.orderMode(OrderMode::GroupBy); queryBuildRange = queryBuildDataSource.addRange(fieldNum(InventTrans,ItemId)); queryBuildDataSource = queryBuildDataSour

Using AOT Query Object in X++ Code

use the AOT Query Object in our X++ code. Steps: 1) Create a AOT Query Object as your requirement. 2) Create a Job and paste the below code to check the Query Object. static void ExecuteAOTQuery(Args _args) { QueryRun queryRun; Counter totalRecords; ; queryRun = new QueryRun(queryStr( CreatedAOTQueryName )); if (queryRun.prompt()) { while (queryRun.next()) { totalRecords++; } } info(strFmt(“Total Records : %1”, totalRecords)); }

Create LookUps Using X++

Lookups using X++ Code(without Ax Table or EDT relation). For that i override a String(Text) Control’ s lookup method. public void lookup() { //super(); // Added by Vasanth Arivali // Declaration Query LookupQuery = new Query(); QueryBuildDataSource LookupQueryBuildDataSource; QueryBuildRange LookupQueryBuildRange; SysTableLookup CustomSysTableLookup = SysTableLookup::newParameters(tableNum(InventTable), this); ; // Add fields that you want in Lookups CustomSysTableLookup.addLookupField(fieldNum(InventTable, ItemId)); CustomSysTableLookup.addLookupField(fieldNum(InventTable,ItemName)); CustomSysTableLookup.addLookupField(fieldNum(InventTable,ItemGroupId)); CustomSysTableLookup.addLookupField(fieldNum(InventTable,ItemType)); // Add DataSource, Range and Value LookupQueryBuildDataSource = LookupQuery.addDataSource(tableNum(InventTable)); LookupQueryBuildRange= LookupQueryBuildDataSource.addRange(fieldNum(InventTable,ItemVisibility)); LookupQueryBuildRan

Find the Table Name in Dynamics Ax 2009

a sample code to find the Table Name of Ax Tables using the TableId. static void findTables(Args _args) { Dictionary dictionary; TableId tableId; tableName tableName; ; dictionary = new Dictionary(); // tableId = dictionary.tableNext(0); tableId = 359; //366; //62; tableName = dictionary.tableName(tableId); info(strfmt(“%1 – %2″,int2str(tableId), tableName)); //while (tableId) //{ // tableId = dictionary.tableNext(tableId); // tableName = dictionary.tableName(tableId); //} }

Argument Passing between Forms in Dynamics Ax 2009

Here a sample code to pass argument to one form to another form and using of Args() class. Steps: 1) Create two Forms named FormA and FormB 2)Use the EmplTable as the Datasource of both forms 3)Design FormA with one Grid and add 4 data fields to the Grid(EmplId,DEL_Name,Grade,EmplStatus…..) 4)Assign the datasource for the grid and the data fields 5)Add a Button in FormA 6)Override the Clicked() method and write the below code: void Clicked() { Args _args; FormRun _formRun; EmplId _empId; ; _empId = EmplTable.EmplId; // Selected employee id in the Grid is assigned to the variable which is pass to the next form _args = new Args(); // creating a object for args class _args.name(formstr(VA_FormB)); // Form Menuitem _args.caller(this); // Form Caller(Current Form is mentioned as this ) _args.parm(_empId); // Employee Number is passed to next form[but parm() is not a best practise] _args.record(EmplTable); // Table name is passed _formRun = ClassFactory.formRunClass(_args); //new Fo

Connect External Data Base in Dynamics Ax 2009 odbc

void ODBCConnection() { LoginProperty LP = new LoginProperty(); OdbcConnection myConnection; TableName TableName; Statement myStatement; ResultSet myResult; #define.Constring(“DSN= DSNNAME ;UID= USERID ;PWD= PASSWORD “) try { LP.setOther(#Constring); myConnection = new OdbcConnection(LP); } catch { info(“Check username/password.”); return; } myStatement = myConnection.createStatement(); new SqlStatementExecutePermission(“SELECT * from TableNamewhere bImportflag = 0″).assert(); myResult = myStatement.executeQuery(“SELECT * from TableNamewhere bImportflag = 0″); CodeAccessPermission::revertAssert(); while (myResult.next()) { TableName.nMember = Member; TableName.tMemberName = MembName; TableName.tMemberAddress = Address; TableName.tRescountry = Country; TableName.tResState = State; T

invent table Import Excel Data into Dynamics AX 2009

Here the code written in Command Button clicked event, and also i added the Excel format below of this post which i used. void clicked() { SysExcelApplication application; SysExcelWorkbooks workbooks; SysExcelWorkbook workbook; SysExcelWorksheets worksheets; SysExcelWorksheet worksheet; SysExcelCells cells; COMVariantType type; System.DateTime ShlefDate; FilenameOpen filename; dialogField dialogFilename; Dialog dialog; //Table Declarations Starts InventSize _InventSize; InventBatch _InventBatch; InventSerial _InventSerial; InventTable _InventTable; VendParameters _vendParameters; //Table Declartions Ends InventBatchId batchNumber; InventBatchExpDate expdate;

Code for Excel import by choosing file in Axapta

his is a very useful code for importing excel into DAX table. Developer can add/modify their logics while importing excel into DAX table. This is a simple and compact code. Developers have to change this code as per their requirements. Note: Here excel file will be 3 columns, 1. Vendor Code, 2. vendor Group and 3. vendor Currency. You can add or less fields in excel but same incorporation should be done in this job (bold area). While you incorporate this type of procedure then I recommend to all do this code into a class instead of job. static void ExcelUploadByFile(Args _args) { #AviFiles FilenameOpen filename; dialogField dialogFilename; Dialog dialog= new Dialog("Excel Upoad"); Container vendCont[]; int rowIdx; Counter linesImported; int lastRow; boolean ok = true; str c1,c2,c3; str input; SysExcelApplication application; SysExcelWorkBooks workBooks; SysExcelWorkSheets workSheets; SysExcelWorkSheet workSheet; SysExcelCells cells; SysOpe

x++ while select "next" record

while select salesTable { do something; if (you need to skip) continue; do something else; } you use next like this: select salesTable; while(salesTable.RecId != 0) { do something; if (you need to skip) { next salesTable; continue; } do something else; next salesTable; }

next button method

My disabling button last, is now working. I manage to figure out that the record is the last record. I will post the pseudocode if wanted. thanks, here is the psedocode: check first the property of data source "InsertAtEnd" = yes void clicked() { boolean flag=false; ; super(); record_ds.next(); record_ds.next(); //check if the next record is null  If(record.next() == null) // i use strcontrol in my form so if(strcontrol.valustr=="") { record_ds.prev();// button.enable(false); flag=true; } elseif(flag==false)//if not null { record_ds.prev(); } flag=false; record_ds.refresh(); }

.NET Business Connector Session Terminated

You were right, that RPC error is of no use, the key was that the session was lost. I can reproduce the error: - I start my .NET windows service that uses the BC.NET - go to AX - Adminstration - Online users - terminate the BC.NET user sessions - execute an action that triggerd my service to use the BC.NET What I then did, is add a try/catch to my service, and a loop so I could reconnect to AX with the BC in case the error occurred. Like this: public void DoSmth( ) { int retryCount = 1; while (retryCount >= 0) { try { object o = axapta.CallStaticClassMethod( "DAXClass" , "DAXMethod" , "parameter" ); break ; } catch ( Exception ex) { if (retryCount < 0) { logger.Error(ex.Message); } else { logger.Debug( "Retry. Recreating connector instance&

Set focus on specific control when opening form

To set the focus on specific control when form is open, you need to override the firstField() method of the form and set your desired control after super() call. public void firstField(int _flags=1) { ; super(_flags); desiredControlName.setFocus(); }

Closed and closedQty in inventsum table

Hi ! The closedQty flag informs that all quantities for given inventory item and inventory dimension set to zero. No item in stock, no item in process of receiving/shipping, also item is not expected to be sold/purchased/produced. The closed flag informs that the monetary balance for given item and inventory dimension is zero. Basically it means that all the costs related to given item was written-of from inventory account to COGS/WIP etc. Usually, the closed flag is set by inventory closing. (But in simple cases it can be set immediately during posting of inventory issue). Regards, Denis

Mass update on table properties

Mass update on table properties We all have run in trouble in the past when for a lot of tables the same update needs to be done: Below script can help you out. It will look in all tables that are in the database (SQLDictionary) Next it will find the related AOT Tree node. static void ChangeTableProperties(Args _args) { SQLDictionary dictionary; TreeNode treeNode; str properties; ; while select dictionary where dictionary.fieldId == 0 && dictionary.name like("Proj*") { treeNode = TreeNode::findNode('\\data dictionary\\tables\\'+dictionary.name); if (treeNode) { properties = treeNode.AOTgetProperties(); properties = setProperty(properties, 'CreatedDate', 'Yes'); properties = setProperty(properties, 'CreatedTime', 'Yes'); properties = setProperty(properties, 'CreatedBy', 'Yes'); treeNode.AOTsetProperties(properties); treeNode.AOTsave(); }