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:
|Field ID||Field Name||Field Type||ObsoleteState|
Here, Field3 was declared with ObsoleteState = Removed (this field will never be used).
Now, consider a DESTINATION table with the following fields:
|Field ID||Field Name||Field Type||ObsoleteState|
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:
- Transfers only fields where ObsoleteState is not set as Removed.
- Checks the matching data type between source and destination fields (for not throwing errors)
procedure SafeTransferFields(SourceTableID: Integer;TargetTableID: Integer); var SourceRef: RecordRef; TargetRef: RecordRef; FldRef: FieldRef; FieldsSource: Record Field; FieldsTarget: Record Field; FieldsNoToTransfer: Record Integer temporary; begin FieldsSource.SetRange(TableNo,SourceTableID); FieldsSource.SetRange(Class,FieldsSource.Class::Normal); FieldsSource.SetRange(Enabled,true); FieldsSource.SetFilter(ObsoleteState,'<>%1',FieldsSource.ObsoleteState::Removed); IF FieldsSource.FindSet() then repeat //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) then begin //This field must be transferred FieldsNoToTransfer.Number := FieldsSource."No."; FieldsNoToTransfer.Insert(); end; until FieldsSource.Next() = 0; if FieldsNoToTransfer.IsEmpty then exit; //There are no fields to transfer //Execute the transferfields of the selected fields SourceRef.Open(SourceTableID); TargetRef.Open(TargetTableID); if SourceRef.FindSet() THEN repeat FieldsNoToTransfer.FindSet(); repeat FldRef := TargetRef.Field(FieldsNoToTransfer.Number); FldRef.Value := SourceRef.Field(FieldsNoToTransfer.Number).Value; until FieldsNoToTransfer.Next() = 0; TargetRef.Insert(); until SourceRef.Next() = 0; end;
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…
UPDATE: it seems that Microsoft after this post has fixed the bug. Official response: “A new overload has been added to the TransferFields method that takes a 3rd parameter that allows you to SkipFieldsNotMatchingType. It will be available with 2019 Wave 2 CU2 and with the 2020 Wave 1 releases. The default behavior for any of the existing transferfields methods and new overload will be to skip the RemoveObsolete parameter.
The new overload will allow to skip not matching fields by type.“
The code provided might have issues with BLOB and Media type fields ?
Good question, not tested with BLOB and Media. But if I remember correctly from NAV 2009 (at least) you can do something like:
BlobFieldRef.VALUE := BlobFieldRef2.VALUE
to transfer a BLOB field.
This is a nice workaround. I hope Microsoft changes the TRANSFERFIELD method to consider ObsoleteState property. Could be a good plan to add a parameter to the TRANSFERFIELD method to get control about this scenario.
LikeLiked by 1 person
Hi Stefano, Symbols in BC V15 Doesn’t exist Integer Table. How was possible definitions in your function? Maybe it’s lower than V15?
Integer record still exists. However, this bug is now solved in the next cumulative update.
Thanks Stefano for your quick reply,
Yes, exists. But not in symbol. Then is like not for extensions.
Finally the function is under lower than BC V15 ok?
You can use Integer record for extensions too…
Please tell us how to do. Trying to set Integer under V15 symbols show error: Table ‘Integer’ is missing.
This piece of code works on version 15:
local procedure TEST()
IntegerTable: Record Integer;
Thanks Stefano for your answer,
Unfortunately, it’s not working for us. As you can see in the image below.
Please, check the image. Our Version: 15.0.36626. Is impossible to set var Integer.
Would it be possible to share with us a screenshot with an extension compiled?
Including all the tree with the symbols.
I hope it’s not an inconvinient for you.
You have a Microsoft app missing on your symbols. Symbols should be the following:
Is needed to set “platform” parameter on app.json file.
Thanks so much Stefano.!
if Sales Line has a table extension and Sales Inv Line has a table extension with same fields as sales Line extension. Will transferfields also populate the table extensions.
If the field ID is the same, yes.
I have used this piece of code and when I run it I get the following error:
Table Integer is read only.
Could you give me some advice to fix it.
This post is old. Now Microsoft has changed things and TransferFields supports transferring only not obsoleted fields (see the update of the post at the bottom).