Dynamics 365 Business Central: TRANSFERFIELDS and Obsolete fields

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
1 Field1 Code[20]
2 Field2 Text[100]
3 Field3 Integer Removed
4 Field4 Decimal

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
1 Field1 Code[20]
2 Field2 Text[100]
3 Field3 Code[20]
4 Field4 Decimal

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.

16 Comments

    1. 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.

      Like

  1. 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.

    Record.TRANSFERFIELD(FromSource[,InitPrimaryKeyFields,SkipObsoleteRemovedFields]);

    Liked by 1 person

  2. Hi Stefano, Symbols in BC V15 Doesn’t exist Integer Table. How was possible definitions in your function? Maybe it’s lower than V15?

    Like

      1. 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?

        Best Regards,

        Like

      2. Thanks Stefano,
        Please tell us how to do. Trying to set Integer under V15 symbols show error: Table ‘Integer’ is missing.

        Thanks

        Like

    1. Thanks Stefano for your answer,
      Unfortunately, it’s not working for us. As you can see in the image below.
      https://ibb.co/MDWfW9G
      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.

      Kind regards,
      Diego.

      Like

      1. You have a Microsoft app missing on your symbols. Symbols should be the following:
        Microsoft_Base Application_15.0.36560.0.app
        Microsoft_System Application_15.0.36560.0.app
        Microsoft_System_15.0.36510.0.app

        Like

      2. Great!
        Is needed to set “platform” parameter on app.json file.

        Thanks so much Stefano.!

        Kind regards.

        Like

  3. 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.

    Like

    1. 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).

      Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.