2012年10月29日星期一
How to achieved to copy data from origanl table instance to new table instance
One of the useful method from standard AX is the buf2Buf(), it behave similar to table's .data() method with one difference - buf2buf doesn't copy system field. Another reason of using buf2buf is copying of record from one company to another company. When using changeCompany(), the .data() method copy all field including DataAreaId and when insert(), DataAreaId field does not change, hence, the record is not inserted into the company indicated in the changeCompany(), here is where buf2Buf() comes in handy - copy all field except system field, then during insert(), let the system assign values to system field.
Sometimes, there're some functionality requires copy data from one table to another table which has similar structure (Eg. to history or logging table), in this case, the .data() and buf2Buf() cannot be used. But we can make some modification to the buf2Buf() method to copy based on field name instead of field Id.
Below are the two methods:
> global::buf2Buf() - Standard AX method
> THK_buf2BufBySameFileldName() - modified method
//Standard AX method, copy data based on field Id
1)global::buf2Buf
static void buf2Buf(
Common _from,
Common _to
)
{
DictTable dictTable = new DictTable(_from.TableId);
fieldId fieldId = dictTable.fieldNext(0);
while (fieldId && ! isSysId(fieldId))
{
_to.(fieldId) = _from.(fieldId);
fieldId = dictTable.fieldNext(fieldId);
}
}
2)THK_buf2BufBySameFileldName
//Modified method, copy data from one table to another table with similar structure
// USR Changed on 29 十月 2012 at 11:35:14 by admin
static void THK_buf2BufBySameFileldName(Common _from, Common _to)
{
DictTable dictTableFrom = new DictTable(_from.TableId);
DictTable dictTableTo = new DictTable(_to.TableId);
DictField dictFieldFrom;
FieldId fieldIdFrom = dictTableFrom.fieldNext(0);
FieldId fieldIdTo;
;
while (fieldIdFrom && ! isSysId(fieldIdFrom))
{
dictFieldFrom = new DictField(_from.TableId, fieldIdFrom);
if(dictFieldFrom)
{
fieldIdTo = dictTableTo.fieldName2Id(dictFieldFrom.name());
if(fieldIdTo)
_to.(fieldIdTo) = _from.(fieldIdFrom);
}
fieldIdFrom = dictTableFrom.fieldNext(fieldIdFrom);
}
}
2012年10月28日星期日
How to achieve save(put) and get Last Value on Form
1) Use pack/unpack on Form
If you have used runbase classes, you may be impressed by its pack and unpack mechanism. It allows users to keep the input values of last time. If you want to implement the same mechanism on the Form, it is easy as well:
Besides Pack/Unpack, add these six methods:
public class FormRun extends ObjectRun
{
...
#define.CurrentVersion(1)
#localmacro.CurrentList
values...
#endmacro
}
public container pack()
{
return [#CurrentVersion, #CurrentList];
}
public boolean unpack(container packedClass)
{
Integer version = conpeek(packedClass,1);
switch (version)
{
case #CurrentVersion:
[version,#CurrentList] = packedClass;
break;
default:
return false;
}
return true;
}
public dataAreaId lastValueDataAreaId()
{
return curExt();
}
private UserId lastValueUserId()
{
return curuserid();
}
{
return curuserid();
}
private UtilElementType lastValueType()
{
return UtilElementType::Form;
}
{
return UtilElementType::Form;
}
private IdentifierName lastValueElementName()
{
return this.name();
}
{
return this.name();
}
private IdentifierName lastValueDesignName()
{
return ”;
}
//this is called when no last value is retrieved
void initParmDefault()
{
}
further, in Close method of the form:
public void close()
{
super();
//add saveLast method after super()
xSysLastValue::saveLast(this);
}
in init method of the form:
public void init()
{
;
{
return ”;
}
//this is called when no last value is retrieved
void initParmDefault()
{
}
further, in Close method of the form:
public void close()
{
super();
//add saveLast method after super()
xSysLastValue::saveLast(this);
}
in init method of the form:
public void init()
{
;
//Add getLast method before super()
xSysLastValue::getLast(this);
super();
…
}
2)Container lastValues;xSysLastValue::getLast(this);
super();
…
}
get
lastValues = xSyslastValue::getValue(curext(), curUserId(), utilElementType::Form,element.name() + "out","");
put
xSyslastValue::putValue([JournalName],curext(), curUserId(), utilElementType::Form,element.name(),"");
How to using X++ code create and posting a ledger journal
Here is the adapted example for posting a ledger journal line. Simply replace values between #s (#value#):
static void ExampleLedgerJournal(Args _args) { LedgerJournalTable ledgerJournalTable; LedgerJournalTrans ledgerJournalTrans; LedgerJournalCheckPost ledgerJournalCheckPost; NumberSeq numberSeq; ; ttsbegin; // Journal name ledgerJournalTable.JournalName = "#JOURNALNAME#"; // ex. Daily, daytrans, etc. ledgerJournalTable.initFromLedgerJournalName(); ledgerJournalTable.Name = "#DESCRIPTION#"; // description for this journal ledgerJournalTable.insert(); // Voucher numberSeq = NumberSeq::newGetVoucherFromCode(LedgerJournalName::find(ledgerJournalTable.JournalName).VoucherSeries); ledgerJournalTrans.Voucher = numberSeq.voucher(); // Lines ledgerJournalTrans.JournalNum = ledgerJournalTable.JournalNum; ledgerJournalTrans.CurrencyCode = CompanyInfo::standardCurrency(); ledgerJournalTrans.ExchRate = Currency::exchRate(ledgerJournalTrans.CurrencyCode); ledgerJournalTrans.AccountNum = "#ACCOUNT#"; ledgerJournalTrans.AccountType = LedgerJournalACType::Ledger; ledgerJournalTrans.AmountCurDebit = #VALUE#; ledgerJournalTrans.TransDate = systemDateGet(); //Avoid the Today function ledgerJournalTrans.OffsetAccount = "#OFFSET ACCOUNT#"; ledgerJournalTrans.Txt = "#TXT#"; ledgerJournalTrans.insert(); //Posting the Journal ledgerJournalCheckPost = LedgerJournalCheckPost::newLedgerJournalTable(ledgerJournalTable, NoYes::Yes); ledgerJournalCheckPost.run(); ttscommit; }
How to using X++ code to delete AOT nodes
1) TreeNode class
Sometimes, bad objects can not be removed from the AOT because when you click them, Axapta crush !! Look up this job:
Sometimes, bad objects can not be removed from the AOT because when you click them, Axapta crush !! Look up this job:
static void DeleteDamnTables(Args _args) { TreeNode edt, edt2; ; edt = TreeNode::findNode("Data Dictionary\\Tables"); if (edt != null) { edt2 = edt.AOTfindChild("tablename"); if (edt2 != null) edt2.AOTdelete(); } }
2) UtilIdElements solution
Warning: Serious problems might occur if you modify the UtilIdElements table using this or another method. Modify system tables at your own risk. The code:static void Job1(Args _args) { UtilIdElements utilElement; ; ttsbegin; select utilElement where utilElement.name == 'myElementName' && utilElement.utilLevel == utilEntryLevel::cus // any layer && utilElement.recordType == utilElementType::Table; // object type if (utilelement) { utilElement.delete(); ttscommit; info('Record should be deleted now.'); } else { ttsAbort; info('Could not delete record, or it was not found.'); } }
How to invoking to the caller Form/DataSource method
If we need to notify events to the caller form we can try this pattern:
In the child form, make a method named updateCaller and invoke it when you want to notify the parent:
Implement the handling method in the parent form:
Or if you prefer, in the DataSource:
Invoking it from a simple button inside the child form:
See a pratical sample in the form MarkupTrans (DataSource -> method write).
In the child form, make a method named updateCaller and invoke it when you want to notify the parent:
void updateCaller() { Common common; Object dataSource; Object caller; ; //----------------------------------- //We are notifying using the dataSource common = element.args().record(); if (common && common.isFormDataSource() && formDataSourceHasMethod(common.dataSource(), identifierstr(SomethingWasHappend))) { dataSource = common.dataSource(); dataSource.SomethingWasHappend(); } //----------------------------------- //----------------------------------- //We are notifying using the form caller = element.args().caller(); if (caller && classidget(caller) == classnum(SysSetupFormRun) && formHasMethod(caller, identifierstr(SomethingWasHappend))) { caller.SomethingWasHappend(); } //----------------------------------- }
Implement the handling method in the parent form:
void SomethingWasHappend() { ; info("Something was happend (Form)"); }
Or if you prefer, in the DataSource:
void SomethingWasHappend() { ; info("Something was happend (DataSource)"); }
Invoking it from a simple button inside the child form:
void clicked()
{
super();
element.updateCaller();
}
See a pratical sample in the form MarkupTrans (DataSource -> method write).
2012年10月19日星期五
How to using SQL statement directly execute to Dynamics AX
This is not a recommended approach to run query against AX, but sometimes there's a need for it (Eg. stored procedure), below is the sample code to run SQL statement directly to the AX database (you'll need to do manual security check due to this approach will skip AX security validation).
I usually has a class with 2 simple method:
- executeUpdate
Used to execute INSERT, UPDATE, DELETE statement - executeQuery
Used to execute SELECT statement
//Class :: SQLUtility ==========================================
class SQLUtility
{
}
//Method :: executeUpdate ======================================
server static int executeUpdate(str sql, Connection connection = new Connection())
{
SqlStatementExecutePermission sqlPerm;
Statement statement;
int rowsAffected;
;
if(sql)
{
sqlPerm = new SqlStatementExecutePermission(sql);
sqlPerm.assert();
statement = connection.createStatement();
rowsAffected = statement.executeUpdate(sql);
CodeAccessPermission::revertAssert();
}
return rowsAffected;
}
//Method :: executeQuery =======================================
server static ResultSet executeQuery(str sql, Connection connection = new Connection())
{
SqlStatementExecutePermission sqlPerm;
Statement statement;
ResultSet resultSet;
;
sqlPerm = new SqlStatementExecutePermission(sql);
sqlPerm.assert();
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
CodeAccessPermission::revertAssert();
return resultSet;
}
//Job :: Job_DirectSQLTest =====================================
static void Job_DirectSQLTest(Args _args)
{
ResultSet testRS;
;
testRS = SQLUtility::executeQuery("select top 10 ItemId from InventTable");
while(testRS.next())
{
info(testRS.getString(1));
}
}
2012年10月12日星期五
A table, Extended Data Type, Base Enum or class called THK_AdjJournalTrans already exists. Import of Table aborted.
Issue :
I have created one new module and taken xpo of it from one server and when i going to import that xpo to another server, the following error is coming and import has been aborted. How to solution this case?
Error info:
"A table, Extended Data Type, Base Enum or class called THK_AdjJournalTrans already exists. Import of Table aborted. "
Note: I have already searched this name object (THK_AdjJournalTrans ) in AOT, which is not available in AOT before import.
Resolved :
Maybe the thing is that IMPORT should be done wihout IDs.So I try delete the cache files for my client AX system environment.
Cache files - close the client and then delete files .AUC files in cache directory:
* C:\Documents and Settings\[USERNAME]\Local Settings\Application Data for Win Xp or Server 2003.
* C:\Users\[USERNAME]\AppData\Local for Vista/Win7. [such as : ax_{19F9525D-A1C5-4858-97D9-7863F8FE814A}.auc]
订阅:
博文 (Atom)