Do you know the wonderful C/AL (ops, now AL) command called TRANSFERFIELDS? This command permits you to copy all matching fields in one record to another record:
Record.TRANSFERFIELDS(FromRecord [, InitPrimaryKeyFields])
TRANSFERFIELDS copies fields based on the Field No. Property of the fields. For each field in Record (the destination), the contents of the field that has the same Field No. in FromRecord (the source) will be copied, if such a field exists.
The fields must have the same data type for the copying to succeed (text and code are convertible, other types are not.) There must be room for the actual length of the contents of the field to be copied in the field to which it is to be copied. If any one of these conditions are not fulfilled, a run-time error will occur.
TRANSFERFIELDS is widely used in Microsoft’s Base App code (posting routines and so on) but unfortunately at the moment there’s a problem with this command on Dynamics 365 Business Central: it ignores the ObsoleteState property.
As an example, imagine to have a SOURCE table with the following fields:
Here, Field3 was declared with ObsoleteState = Removed (this field will never be used).
Now, consider a DESTINATION table with the following fields:
If now in your AL code you execute DESTINATION.TransferFields(SOURCE) you receive an error at runtime (like “the following fields must have the same type“), because the TRANSFERFIELDS command tries also to transfer Field3 from SOURCE to DESTINATION tables (despite the ObsoleteState property) and data type doesn’t match.
There’s also an issue opened on GitHub lots of time ago about this, but no news from Microsoft at the moment.
How to avoid this? Quite difficult (alias impossible) on Microsoft’ Base App code (you cannot modify that code).
For your solutions (extensions), you should implement a “safe TRANSFERFIELDS” command that consider also the ObsoleteState field property. Obviously, also Microsoft should do that on its standard codebase.
Here a possible solution (Microsoft, please check/think on this) of a “safe” TRANSFERFIELDS that:
procedure SafeTransferFields(SourceTableID: Integer;TargetTableID: Integer);
FieldsSource: Record Field;
FieldsTarget: Record Field;
FieldsNoToTransfer: Record Integer temporary;
IF FieldsSource.FindSet() then
//Check if the field exists in the destination table and if the criteria for the trasfer are satisfied
if FieldsTarget.GET(TargetTableID,FieldsSource."No.") then
if (FieldsTarget.Class = FieldsSource.Class) and
(FieldsTarget.Type = FieldsSource.Type) and
(FieldsTarget.ObsoleteState <> FieldsTarget.ObsoleteState::Removed)
//This field must be transferred
FieldsNoToTransfer.Number := FieldsSource."No.";
until FieldsSource.Next() = 0;
if FieldsNoToTransfer.IsEmpty then
exit; //There are no fields to transfer
//Execute the transferfields of the selected fields
if SourceRef.FindSet() THEN
FldRef := TargetRef.Field(FieldsNoToTransfer.Number);
FldRef.Value := SourceRef.Field(FieldsNoToTransfer.Number).Value;
until FieldsNoToTransfer.Next() = 0;
until SourceRef.Next() = 0;
Basically, the command checks all the fields to transfer, saves them in a temporary Integer table and then performs the transfer of these fields by using RecordRef and FieldRef objects.
You can use this “safe TRANSFERFIELDS” in your extensions in order to avoid errors. As said before, Microsoft should do something too…