How to use the Excel Buffer in Business Central cloud

18 Jan

A week ago, my friend Daniel asked a question on Twitter how to use the Excel Buffer to export data with Business Central online. Many functions on the Excel Buffer are only available for on-prem. When you change the target in app.json to ‘Cloud’ then those functions become unavailable.

Luckily I had a code example available so I decided to put that on GitHub. But I thought it would make sense to write a blog post to explain the code.

It contains two examples: the first example exports the Customer list to a new Excel file and the second exports a Sales Order to a prepared and uploaded Excel file. Both options offer to download or to email the created Excel file.

The example to export the Customer List can be found on the Customers page under Actions. The export Sales Order is on the Sales Order list under Actions.

Basic flow

The basic flow of creating a new Excel file with the Excel Buffer table can be found in Codeunit Export Customer 2 Excel, function CreateAndFillExcelBuffer.

local procedure CreateAndFillExcelBuffer(var TempExcelBuf: Record "Excel Buffer" temporary)
begin
    TempExcelBuf.CreateNewBook(SheetNameTxt);
    FillExcelBuffer(TempExcelBuf);
    TempExcelBuf.WriteSheet(HeaderTxt, CompanyName(), UserId());
    TempExcelBuf.CloseBook();
end;

All of this happens in memory. The CreateNewBook function creates a temporary server file and initializes the .Net components to write to that file. There is an overload function for CreateNewBook that accepts a filename parameter. Obviously, that function isn’t available for the cloud, because you don’t have direct access to server files. After this, you fill the Excel Buffer with data in the usual way.

local procedure FillExcelRow(
    var TempExcelBuf: Record "Excel Buffer" temporary;
    Customer: Record Customer)
begin
    with Customer do begin
        TempExcelBuf.NewRow();
        TempExcelBuf.AddColumn("No.", false, '', false, false, false, '', TempExcelBuf."Cell Type"::Text);
        TempExcelBuf.AddColumn(Name, false, '', false, false, false, '', TempExcelBuf."Cell Type"::Text);
        TempExcelBuf.AddColumn(Address, false, '', false, false, false, '', TempExcelBuf."Cell Type"::Text);
        TempExcelBuf.AddColumn("Post Code", false, '', false, false, false, '', TempExcelBuf."Cell Type"::Text);
        TempExcelBuf.AddColumn(City, false, '', false, false, false, '', TempExcelBuf."Cell Type"::Text);
        TempExcelBuf.AddColumn("Country/Region Code", false, '', false, false, false, '', TempExcelBuf."Cell Type"::Text);
    end;
end;

The WriteSheet function uses the data of the Excel Buffer to write to the temporary server file. Finally, the Close function clears the .Net components that are used to write to the file. Now you end up with a temporary server file. The next step is to get the file, either as a download to the client or into a stream so you can store it in a blob or attach it to an email.

Getting the file

To download the file to the client, you can only use the function OpenExcel. Other functions are blocked for the cloud. Below are the three functions in the Excel Buffer table, and I believe these functions could be reduced to just two functions. The function OpenExcel could be removed and the OnPrem limitation on function DownloadAndOpenExcel could be removed. As far as I can see, it would result in one function to be used for both cloud and on-premises with exactly the same behaviour. And no code duplication between OpenExcel and OpenExcelWithName.

    procedure OpenExcel()
    begin
        if OpenUsingDocumentService('') then
            exit;
        FileManagement.DownloadHandler(FileNameServer, '', '', Text034, GetFriendlyFilename);
    end;
    [Scope('OnPrem')]
    procedure DownloadAndOpenExcel()
    begin
        OpenExcelWithName(GetFriendlyFilename);
    end;
    [Scope('OnPrem')]
    procedure OpenExcelWithName(FileName: Text)
    begin
        if FileName = '' then
            Error(Text001);
        if OpenUsingDocumentService(FileName) then
            exit;
        FileManagement.DownloadHandler(FileNameServer, '', '', Text034, FileName);
    end;

Anyway… the only thing that I wanted to add is the FriendlyFileName. So I have created a function DownloadAndOpenExcel that does that and then calls the function OpenExcel. As a result, the web client will download the file to the local file system.

local procedure DownloadAndOpenExcel(var TempExcelBuf: Record "Excel Buffer" temporary)
begin
    TempExcelBuf.SetFriendlyFilename(BookNameTxt);
    TempExcelBuf.OpenExcel();
end;

Attach the Excel to an email

The other possibility is to get the file into a stream variable and attach it to an email. I have added a function EmailExcelFile to the Excel Buffer table with a tableextension. In the Codeunit EmailExcelFileImpl you can find the implementation of this function. The code below retrieves the Excel file into a stream and adds it as an attachment to the email.

local procedure AddAttachment(
    var SMTPMail: Codeunit "SMTP Mail";
    var TempExcelBuf: Record "Excel Buffer" temporary;
    BookName: Text)
var
    TempBlob: Codeunit "Temp Blob";
    InStr: InStream;
begin
    ExportExcelFileToBlob(TempExcelBuf, TempBlob);
    TempBlob.CreateInStream(InStr);
    SMTPMail.AddAttachmentStream(InStr, BookName);
end;
local procedure ExportExcelFileToBlob(
    var TempExcelBuf: Record "Excel Buffer" temporary;
    var TempBlob: Codeunit "Temp Blob")
var
    OutStr: OutStream;
begin
    TempBlob.CreateOutStream(OutStr);
    TempExcelBuf.SaveToStream(OutStr, true);
end;

As you can see, I’m using the new Temp Blob storage module of version 15. If you have the data in the Temp Blob Codeunit, then you could also store the file into a blob field in a table or send it to a web service.

Updating an existing Excel file

The other example is to export data to an already existing Excel file. It is basically updating that file without removing any of the existing data. An example Excel file is added to the repository on GitHub. You can import that file with the Excel Template page. The Excel Template table doesn’t store the Excel file in a blob field in the table, but instead, it uses the persistent blob feature of the Blob Storage module in v15.

The code for the Excel Buffer to load this file instead of creating a new file is in the function InitExcelBuffer in Codeunit Export Sales Order 2 Excel. It uses the function UpdateBookStream of the Excel Buffer. This function saves the stream to a temporary file on the server and then opens it.

local procedure InitExcelBuffer(var TempExcelBuf: Record "Excel Buffer" temporary): Boolean
var
    ExcelTemplate: Record "Excel Template";
    TempBlob: Codeunit "Temp Blob";
    InStr: InStream;
begin
    ExcelTemplate.FindFirst();
    if not ExcelTemplate.GetTemplateFileAsTempBlob(TempBlob) then
        exit;
    
    TempBlob.CreateInStream(InStr);
    TempExcelBuf.UpdateBookStream(InStr, SheetNameTxt, true);
    exit(true);
end;

Instead of adding new rows to the Excel Buffer, we have now to enter data directly into cells. This is an existing feature and nothing different from the past. Take a look at function FillHeaderData and FillLineData in the Codeunit for an example. Finally you need to call the function WriteAllToCurrentSheet to write the data into the Excel file. The rest of the code is similar to the previous example.

24 thoughts on “How to use the Excel Buffer in Business Central cloud

  1. HI. I have tried this one, but the excel look messy, is there any way to adjust cell width? Merge cell? Thank you.

  2. This is titled “How to use the Excel Buffer in Business Central cloud”, but then half way down you note: “Obviously, that function isn’t available for the cloud, because you don’t have direct access to server files” – so I think for the help of future visitors perhaps rename the blog title to Business Central On Prem.

    On the other hand, do you have an example of using the new STREAM functions to actually create an excel file from BC Cloud?

    Thanks!
    Matt

    • That note is only for the overload function that accepts a filename. But apart from the side note everything in this blog post is applicable for the the cloud version. Please grab the code and try it out in your online sandbox.

  3. “Temp Blob” did not exist as a codeunit for me, but TempBlob does exist as a record with all the functions in BC16 – was this a recent change?

      • Thanks – I found it – I was looking for the al source.
        Another question – if I need to create multiple sheets (cloud version), can I do this with a new book, or do I have to save it and then use the update stream method to add more sheets?

  4. Hi Arend-Jan, I used your solution, and it works fine except for one thing. When I reopen Business Central after I have created the excel sheet I get an error and I cannot login to Business Central anymore. The error is “Something went wrong. An error has occured, Date and Time xxx , Azure AD tenant xxx, Environment xxx, Operation xxx. When I delete my browser history (Chrome and Edge) I can open Business Central again. Do you perhaps know what the cause is of this? It looks like it is still downloading the excel sheet.

  5. Hi Arend-Jan, big thank you for this examples. The only issue i have is when i populate my template with values the initial formatting disappears from populated cells. Maybe you know how the cell could be populated with values leaving the initial formatting of this cell?

  6. What is the latest use case for GiveUserControl() method which was there in NAV, Any help?

  7. Hola,

    Estoy usando ExcelBuffer para exportar 2 tablas. Necesito que cada una de ellas vayan a una hoja del Excel distinta (Ejemplo. Tabla Clientes, Tabla proveedores). Hoja1= clientes / Hoja2= Proveedores. De momento lo que hace es que la Hoja1 la muestra bien y la Hoja2 muestra el contenido de la Hoja1 y seguidamente el de la Hoja2. Estoy intentando usar ExcelBuf.DeleteAll(), al cambiar de hoja para vaciar el buffer pero entonces en la Hoja2 no muestra nada. Podéis darme un ejemplo de como hacer esto?

      • Hello,

        First, excuse my English.

        I am using ExcelBuffer to export 2 tables. I need each of them to go to a different Excel sheet (Example: Customers Table, Vendors Table). Sheet1= customers / Sheet2= vendors. At the moment,
        Sheet1 shows customers.
        Sheet2 shows customers + vendors.

        I’m trying to use DeleteAll() to delete the buffer after painting sheet1 but the result is:

        Sheet1: shows customers.
        Sheet2: Does not show anything

        Can you give me an example of how to do this?

        Thanks.

        • I have never done that, and I’ve no example code available.
          But in general, I believe the flow should be like:

          * NewRow()
          * AddColumn()
          * WriteSheet()
          * DeleteAll()
          * SelectOrAddSheet()
          * NewRow()
          * AddColumn()
          * WriteSheet()

          But maybe you have that already?

  8. Thank you for the nice solution. I am trying to insert the picture in the template by sing Insert -> Pictures -> Place over cells …
    Im getting this error when attempting to do the Export to Excel on sales orders:

    If requesting support, please provide the following details to help troubleshooting:

    Error message:
    A call to Microsoft.Dynamics.Nav.OpenXml.Spreadsheet.WorkbookWriter.ValidateDocument failed with this message: The workbook is corrupted. The following error occurred: The element has unexpected child element ‘http://schemas.openxmlformats.org/spreadsheetml/2006/main:pageSetup’..

    Internal session ID:
    ce117871-a928-43eb-97f7-ea0e0de7e94a

    Application Insights session ID:
    d3a4ac2b-1bc1-4b39-8702-f34fec849a67

    Client activity id:
    2e6f54c0-39c9-4b1f-a7ae-7b2d1a86b7ff

    Time stamp on error:
    2025-03-03T17:18:34.2820073Z

    User telemetry id:
    0f07869c-e110-4646-80b3-ffc3d7cf787d

    AL call stack:
    “Excel Buffer”(Table 370).CloseBook line 4 – Base Application by Microsoft
    ExportSalesOrder2Excel(CodeUnit 50125).CreateAndFillExcelBuffer line 8 – Flash Global Customization by RSM US LLP
    ExportSalesOrder2Excel(CodeUnit 50125).Export2Excel line 4 – Flash Global Customization by RSM US LLP
    ExportSalesOrder2Excel(CodeUnit 50125).OnRun(Trigger) line 2 – Flash Global Customization by RSM US LLP
    SalesOrderList(PageExtension 50130).”RSMUSExport2Excel – OnAction”(Trigger) line 6 – Flash Global Customization by RSM US LLP

    Any ideas of how we can get this solved?
    Otherwise the solution if super nice … love it.

    Thank you,
    Silviu Virlan

  9. This post offers a clear and practical walkthrough of using the Excel Buffer in Business Central Cloud! The reference to the built‑in Export Customer 2 Excel Codeunit provides a helpful real‑world example of initializing, populating, and exporting data using the Excel Buffer table. Thanks for laying out the workflow so systematically! Regard Sains Data

Leave a Reply

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