Copying existing data is one of the data manipulation tasks in Dynamics AX. There are numerous places in the standard Dynamics AX application where users can create new data entries just by copying existing data and then modifying it. A few examples are the Copy button on the Costing versions form located in Inventory management | Setup | Costing and the Copy project button on the All projects list page located in Project management and accounting | Common | Projects. Also, although the mentioned copying functionality might not be that straightforward, the idea is clear: the existing data is reused while creating new entries.
In this recipe, we will learn two ways to copy records in X++. We will discuss the usage of the table's data()
method, the global buf2buf()
function, and their differences. As an example, we will copy one of the existing main account records into a new record.
Carry out the following steps in order to complete this recipe:
Navigate to General ledger | Common | Main accounts and find the account to be copied. In this example, we will use 130100, as shown here:
Open the AOT, create a new job named
MainAccountCopy
with the following code snippet, and run it:static void MainAccountCopy(Args _args) { MainAccount mainAccount1; MainAccount mainAccount2; mainAccount1 = MainAccount::findByMainAccountId( '130100'); ttsBegin; mainAccount2.data(mainAccount1); mainAccount2.MainAccountId = '130101'; mainAccount2.Name += ' - copy'; if (!mainAccount2.validateWrite()) { throw Exception::Error; } mainAccount2.insert(); ttsCommit; }
Navigate to General ledger | Common | Main accounts again and notice that there are two identical records now, as shown in the following screenshot:
In this recipe, we have two variables: mainAccount1
for the original record and mainAccount2
for the new record. First, we find the original record by calling findMainAccountId()
in the MainAccount
table.
Next, we copy the original record into the new one. Here, we use the table's data()
method, which copies all the data fields from one variable into another.
After that, we set a new main account number, which is a part of the table's unique index.
Finally, we call insert()
on the table if validateWrite()
is successful. In this way, we create a new main account record that is exactly the same as the existing one apart from the account number.
As we saw before, the data()
method copies all the table fields, including the system fields such as the record ID or company account. Most of the time it is OK because when the new record is saved, the system fields are overwritten with the new values. However, this function may not work for copying records across companies. In this case, we can use another function called buf2Buf()
. This function is a global function and is located in the Global
class, which you can find by navigating to AOT | Classes. The buf2Buf()
function is very similar to the table's data()
method with one major difference. The buf2Buf()
function copies all the data fields excluding the system fields. The code in the function is as follows:
static void buf2Buf( Common _from, Common _to, TableScope _scope = TableScope::CurrentTableOnly) { DictTable dictTable = new DictTable(_from.TableId); FieldId fieldId = dictTable.fieldNext(0, _scope); while (fieldId && ! isSysId(fieldId)) { _to.(fieldId) = _from.(fieldId); fieldId = dictTable.fieldNext(fieldId, _scope); } }
We can clearly see that during the copying process, all the table fields are traversed, but the system fields, such as RecId
or dataAreaId
, are excluded. The isSysId()
helper function is used for this purpose.
In order to use the buf2Buf()
function, the code of the MainAccountCopy
job can be amended as follows:
static void MainAccountCopy(Args _args) { MainAccount mainAccount1; MainAccount mainAccount2; mainAccount1 = MainAccount::findByMainAccountId('130100'); ttsBegin; buf2Buf(mainAccount1, mainAccount2); mainAccount2.MainAccountId = '130101'; mainAccount2.Name += ' - copy'; if (!mainAccount2.validateWrite()) { throw Exception::Error; } mainAccount2.insert(); ttsCommit; }