Report extension’s tips and tricks

Guidelines for Partners

Probably, most of you have already encountered the report extension object to add desired modifications to the standard report in Microsoft Dynamics 365 Business Central. Depending on the complexity of your cases, you may have come across a number of implementation restrictions in the extension. In this article, we will review solutions to some simple restriction cases in report extensions and provide you with a few valuable tips in the process.

1. Label modification in report extension

Let’s say we need to modify the label’s text. As you may know, we cannot directly modify standard variables in extensions. Fortunately, there’s a simple workaround that solves this problem for us.

For example, let’s take PageCaptionLbl label in Sales Invoice NA report:

PageCaptionLbl: Label ‘Page:’; and change it into: PageCaptionLbl: Label ‘Page number:’;

 

1. Create a new variable with the text modification.

PageCaptionLbl_COPY: Label ‘Page number:’;

Tip: We typically create an identical variable to the one we want to modify and append ‘_COPY’ at the end for better readability and clarity.

 

2. Locate a column where the standard label was applied in the standard report. Incorporate it into your report extension under the exact same data item, updating the column name and including your newly created variable.

For our example, the standard column was under PageLoop data item:

add(PageLoop)
        {
            column(PageCaption_COPY; PageCaptionLbl_COPY)
            {
            }
        }

Tip: The order of columns within a data item generally does not matter as long as they are defined under the correct data item. The order of columns is usually determined by the layout of the report. Therefore, there’s no need to worry that your created column (PageCaption_COPY) is not placed in the exact same spot as the original (PageCaption) column as long as it is under the identical data item.

 

3. Identify all standard column references in the report layout and replace them with your created column. Depending on your preference, you can modify the layout using Report Builder or open the RDLC file in your code editor, such as Visual Studio Code or another.

Let’s try a less common approach with RDLC file modification. For our example, there were only two places where the standard column name was referenced:

1) In textbox HeaderCompanyAdressInfo under the visibility parameter:

Cstr(Fields!PageCaption.Value)

Cstr(Fields!PageCaption_COPY.Value)

 

2) In dataset DataSet_Result under fields: 

        <Field Name=”PageCaption”> 

          <DataField>PageCaption</DataField> 

        </Field> 

There is no need to change the column’s reference in the dataset because when you publish the app, all newly created columns will be automatically added to the dataset.

Tip: Be aware that merely copying the label’s variable and its column is not always sufficient. Some labels are also utilized in standard code implementations, such as triggers or procedures, and not all of that code can be successfully replicated in an extension. In such cases, the making of report copy should be considered.

2. Array modification in report extension

Let’s explore a scenario where there’s a requirement to assign different variables to an array.

For example, let’s take CompanyAddress array in the Sales Credit Memo NA report and incorporate a custom code modification. If the 6th member of the array is empty, we will fill the remaining elements with some desired values:

if CompanyAddress[6] = “ ” then begin

   CompanyAddress[6] := ‘Unavailable Phone No’;

   CompanyAddress[7] := ‘Unavailable Fax No’;

   CompanyAddress [8] := ‘Unavailable Email Adress’;

end;

 

1. Create a new identical array variable.

CompanyAddress_COPY: array[8] of Text[100];

 

2. Locate a column(s) in the standard report where the standard array was applied. Incorporate it into your report extension under the exact same data item, updating the column name and including your newly created variable(s).

add(PageLoop)
        {
            column(CompanyAddress1_COPY; CompanyAddress_COPY [1])
            {
            }
            column(CompanyAddress2_COPY; CompanyAddress_COPY[2])
            {
            }
            column(CompanyAddress3_COPY; CompanyAddress_COPY[3])
            {
            }
            column(CompanyAddress4_COPY; CompanyAddress_COPY [4])
            {
            }
            column(CompanyAddress5_COPY; CompanyAddress_COPY [5])
            {
            }
            column(CompanyAddress6_COPY; CompanyAddress_COPY[6])
            {
            }
            column(CompanyAddress7_COPY; CompanyAddress_COPYY7])
            {
            }
            column(CompanyAddress8_COPY; CompanyAddress_COPY[8])
            {
            }
}

 

3. Add your custom code modification in the report extension.

       modify("Sales Cr.Memo Header")
       {
           trigger OnAfterAfterGetRecord()
           begin
                if CompanyAddress_COPY[6] = '' then begin
                    CompanyAddress_COPY[6] := 'Unavailable Phone No';
                    CompanyAddress_COPY[7] := 'Unavailable Fax No';
                    CompanyAddress_COPY[8] := 'Unavailable Email Adress';

                    CompressArray(CompanyAddress_COPY);
               end;
           end;
        }

Tip: In your specific case with arrays, you should consider the CompressArray function. This function efficiently shifts all non-empty strings in an array to the beginning. This becomes particularly beneficial when report layout columns are arranged sequentially (see the picture below). This means if CompanyAddress4 value is missing, all values after it will be seamlessly adjusted, eliminating any empty spaces in the generated document between the represented values.

Report extension

 

4. Search for references in triggers and/or procedures in the standard report for the original array variable. Once found, copy the code implementations to the report extension.

For our example, there were only two places where the standard array was referenced:

  • In data item’s Sales Cr.Memo Header trigger OnAfterGetRecord:

                if PrintCompany then
                    if RespCenter.Get(“Responsibility Center”) then begin
                        FormatAddress.RespCenter(CompanyAddress, RespCenter);
                        CompanyInfo.”Phone No.” := RespCenter.”Phone No.”;
                        CompanyInfo.”Fax No.” := RespCenter.”Fax No.”;
                    end;

Change to:

                if PrintCompany_COPY then
                    if RespCenter_COPY.Get(“Responsibility Center”) then begin
                        FormatAddress_COPY.RespCenter(CompanyAddress_COPY
                        RespCenter_COPY);
                        CompanyInformation_COPY.”Phone No.” := RespCenter_COPY.”Phone No.”;
                        CompanyInformation_COPY.”Fax No.” := RespCenter_COPY.”Fax No.”;
                    end;

Now, it’s time to create copies of any missing variables in the code, such as PrintCompany, RespCenter, FormatAddress, CompanyInformation. Depending on your specific case, be sure to check for references to these inaccessible variables in the report’s standard code as well. In our case, we only need to address CompanyInformation and PrintCompany. For CompanyInformation, we need to ensure that the variable is not empty and has a record, which we will accomplish in OnPreReport trigger. As for PrintCompany a copy of the variable’s field needs to be created on the request page:

addafter(NoCopies)
{
                  field(PrintCompanyAddress_COPY; PrintCompany_COPY)
                    {
                        ApplicationArea = Basic, Suite;
                        Caption = ‘Print Company Address’;
                        ToolTip = ‘Specifies if your company address is printed at the top of the sheet
                        because you do not use pre-printed paper. Leave this check box blank to omit your
                         company”s address.’;
                    }
}

Tip: When making a copy of the field, ensure you’ve added the Caption property with the standard field name or caption.

  • In trigger OnPreReport:

       if PrintCompany then
          FormatAddress.Company(CompanyAddress, CompanyInfo)
      else
         Clear(CompanyAddress);

Change to:

        CompanyInformation_COPY.Get;

if PrintCompany_COPY then
FormatAddress_COPY.Company(CompanyAddress_COPY,
CompanyInformation_COPY)
else
Clear(CompanyAddress_COPY);

Tip: Be aware that not all code can be successfully copied to the extension, especially considering local procedures, inaccessible variables, and other factors. In such cases, it’s advisable to consider making a full copy of the report. However, ensure that you check and leverage any available events and/or utilize single-instance variables if possible.

 

5. Identify all standard column(s) references in the report layout and replace them with your created column. Depending on your preference, you can modify the layout using Report Builder or open the RDLC file in your code editor, such as Visual Studio Code or another.

Let’s try a less common approach with RDLC file modification. For our example, there were only two places where the standard column name was referenced:

  • In the textbox LeftHeaderInfor under the value:

Cstr(Fields!CompanyAddress1.Value) + Chr(177) +

Cstr(Fields!CompanyAddress2.Value) + Chr(177) +

Cstr(Fields!CompanyAddress3.Value) + Chr(177) +

Cstr(Fields!CompanyAddress4.Value) + Chr(177) +

Cstr(Fields!CompanyAddress5.Value) + Chr(177) +

Cstr(Fields!CompanyAddress6.Value) + Chr(177) +

Cstr(Fields!CompanyAddress7.Value) + Chr(177) +

Cstr(Fields!CompanyAddress8.Value) + Chr(177) +

 

Change to:

Cstr(Fields!CompanyAddress1_COPY.Value) + Chr(177) +

Cstr(Fields!CompanyAddress2_COPY.Value) + Chr(177) +

Cstr(Fields!CompanyAddress3_COPY.Value) + Chr(177) +

Cstr(Fields!CompanyAddress4_COPY.Value) + Chr(177) +

Cstr(Fields!CompanyAddress5_COPY.Value) + Chr(177) +

Cstr(Fields!CompanyAddress6_COPY.Value) + Chr(177) +

Cstr(Fields!CompanyAddress7_COPY.Value) + Chr(177) +

Cstr(Fields!CompanyAddress8_COPY.Value) + Chr(177) +

 

  • In dataset DataSet_Result under fields:

As mentioned, there’s no need to modify the column’s reference. When you publish the app, all newly created columns will be automatically added to the dataset.

Tip: Be aware that if you made copies of inaccessible variables in step 3 and they had their columns in the dataset, you should also find their references in the report layout.

3. Report Layout Rendering

Finally, we would like to highlight the benefit rendering section in reports and report extensions. The Rendering Report Layout was released back in Business Central 2022. This feature allows you to have multiple different layouts of the same type.

The old properties for defining layout only let you add multiple report layouts of the different types. You can see the examples in all standard reports:

RDLCLayout = ‘./Local/Sales/Document/SalesQuoteNA.rdlc’;

Tip: You can still use the old property but cannot use them in combination with the rendering section. Use one or the other.

As we mentioned earlier, the rendering selection lets you add as many layouts of the same type (RDLC, Word, Excel) as you’d like:

rendering
    {
        layout(RDLCSalesQuoteForVendors)
        {
            Type = RDLC;
            LayoutFile = './Layout/SalesQuoteNA_Vedors.rdlc';
        }
        layout(RDLCSalesQuoteForCustomer)
        {
            Type = RDLC;
            Caption = ‘SalesQuoteForCustomer’;
            Summary = 'Sales Quote to provide for customers';
            LayoutFile = 'EmpSortedByLastName.xlsx';
        }
        layout(ExcelSalesQuoteForEmployees)
        {
            Type = Excel;
            LayoutFile = 'EmpSortedByLastName.xlsx';
        }
}

 

All you have to do is give the layout a name, select the type, and provide the path to the layout file. In addition, you can add a caption and a summary for better understanding.

When you are going to run the report, in the top section of the report’s request page, you’ll find two fields: Printer and Report Layout. If you click the three dots (circled in red by us), you’ll be presented with a list of your defined layouts in the rendering section.

Report extension

Conclusion

In summary, working with report extensions, especially when dealing with inaccessible variables such as labels, arrays, etc., can be challenging. However, as we discussed in the article, there are straightforward workarounds involving making copies of variables and their references that can effectively address these challenges. We hope you found these solutions and their additional tips useful.