Dynamics 365 Business Central: Save a document as a Word attachment (Attach as Word) – Customization

Dynamics 365 Business Central

Hi, Readers.
Today I would like to talk about how to save a document as a Word attachment (Attach as Word) in Business Central.

Whenever you need to save a document as a file, you can use the Attach as PDF action to capture the current document content as a PDF file attached to the FactBox of the document. This is useful, for example, on the Posted Sales Invoices page, we can select multiple sales invoices, and then choose the Attach as PDF action. A PDF file with the current content of the sales invoice is added to the Attachments tab in the FactBox.

Test video: Save selected posted sales invoices as PDF attachments at the same time

Business Central short: Save selected posted sales invoices as PDF attachments at the same time

So here is a question, can we save documents (report) as word (Not pdf) to document attachment factbox like Attach as PDF action?
First let’s look at the standard processing:

This is hard-coded in the code.

If we want to modify the default behavior, this requires two steps:

  1. Change the format of the report file save to Word
  2. Change the extension name of the report file save to .docx

For the first point, we can use the Database::”Report Selections” -> OnBeforeSaveReportAsPDF event to skip the processing in IsHandled, then add the processing of requirements.

For example,

For the second point, we can use the Database::”Document Attachment” -> OnBeforeSaveAttachment event to rename the file.

For example,

Okay, let’s do a simple test.

Great.

Test video:

Of course you can also change the output format, such as Excel or XML.

Source code: Github (Please note that the source code is for reference only, you can improve it according to your own needs)

codeunit 50113 SaveAsWordHandler
{
    [EventSubscriber(ObjectType::Table, Database::"Report Selections", OnBeforeSaveReportAsPDF, '', false, false)]
    local procedure ReportSelections_OnBeforeSaveReportAsPDF(var ReportID: Integer; RecordVariant: Variant; var IsHandled: Boolean; var TempBlob: Codeunit "Temp Blob")
    var
        OutStream: OutStream;
        LastUsedParameters: Text;
        CustomLayoutReporting: Codeunit "Custom Layout Reporting";
    begin
        if ReportID = Report::"Standard Sales - Invoice" then begin
            TempBlob.CreateOutStream(OutStream);
            LastUsedParameters := CustomLayoutReporting.GetReportRequestPageParameters(ReportID);
            Report.SaveAs(ReportID, LastUsedParameters, ReportFormat::Word, OutStream, GetRecRef(RecordVariant));
            IsHandled := true;
        end;
    end;

    local procedure GetRecRef(RecVariant: Variant) RecRef: RecordRef
    begin
        if RecVariant.IsRecordRef() then
            exit(RecVariant);
        if RecVariant.IsRecord() then
            RecRef.GetTable(RecVariant);
    end;

    [EventSubscriber(ObjectType::Table, Database::"Document Attachment", OnBeforeSaveAttachment, '', false, false)]
    local procedure DocumentAttachment_OnBeforeSaveAttachment(var RecRef: RecordRef; var FileName: Text)
    var
        FileManagement: Codeunit "File Management";
        FileNameWithoutExtension: Text;
    begin
        if RecRef.RecordId.TableNo = Database::"Sales Invoice Header" then begin
            FileNameWithoutExtension := FileManagement.GetFileNameWithoutExtension(FileName);
            FileName := FileNameWithoutExtension + '.docx';
        end;
    end;
}

The above approach uses events and modifies the standard logic. Let’s look at another approach that adds a completely new action.

Since many standard methods are local and cannot be called directly, we need to rewrite some code based on the standard methods. However, this does not affect the standard logic and can be customized more freely.

Test video:

Great. Give it a try!!!😁

Source code: Github (Please note that the source code is for reference only, you can improve it according to your own needs)

pageextension 50115 PostedSalesInvoicesExt extends "Posted Sales Invoices"
{
    actions
    {
        addafter(AttachAsPDF_Promoted)
        {
            actionref(AttachAsWord_Promoted; AttachAsWord)
            {
            }
        }
        addafter(AttachAsPDF)
        {
            action(AttachAsWord)
            {
                ApplicationArea = All;
                Caption = 'Attach as Word';
                Image = PrintAttachment;
                ToolTip = 'Create a Word file and attach it to the document.';

                trigger OnAction()
                var
                    ReportSelection: Record "Report Selections";
                    SalesInvHeader: Record "Sales Invoice Header";
                    SalesInvHeader2: Record "Sales Invoice Header";
                    TempReportSelections: Record "Report Selections" temporary;
                    TempBlob: Codeunit "Temp Blob";
                    FileName: Text[50];
                    InS: InStream;
                    DocAttach: Record "Document Attachment";
                    ReportDistributionMgt: Codeunit "Report Distribution Management";
                    ReportCaption: Text[250];
                    DocumentLanguageCode: Code[10];
                begin
                    SalesInvHeader.Reset();
                    CurrPage.SetSelectionFilter(SalesInvHeader);
                    if SalesInvHeader.FindSet() then
                        repeat
                            SalesInvHeader2.Get(SalesInvHeader."No.");
                            SalesInvHeader2.SetRecFilter();
                            ReportSelection.FindReportUsageForCust(Enum::"Report Selection Usage"::"S.Invoice", SalesInvHeader2."Bill-to Customer No.", TempReportSelections);
                            Clear(TempBlob);
                            SaveReportAsPDFInTempBlob(TempBlob, TempReportSelections."Report ID", SalesInvHeader2, TempReportSelections."Custom Report Layout Code", Enum::"Report Selection Usage"::"S.Invoice", TempReportSelections);
                            TempBlob.CreateInStream(InS);
                            DocAttach.Init();
                            DocAttach.Validate("Table ID", SalesInvHeader2.RecordId.TableNo);
                            DocAttach.Validate("No.", SalesInvHeader2."No.");
                            ReportCaption := ReportDistributionMgt.GetReportCaption(TempReportSelections."Report ID", DocumentLanguageCode);
                            FileName := StrSubstNo('%1 %2 %3', TempReportSelections."Report ID", ReportCaption, SalesInvHeader2."No.");
                            DocAttach.Validate("File Name", FileName);
                            DocAttach.Validate("File Extension", 'docx');
                            DocAttach."Document Reference ID".ImportStream(InS, FileName);
                            DocAttach.Insert(true);
                        until SalesInvHeader.Next() = 0;
                end;
            }
        }
    }

    local procedure SaveReportAsPDFInTempBlob(var TempBlob: Codeunit "Temp Blob"; ReportID: Integer; RecordVariant: Variant; LayoutCode: Code[20]; ReportUsage: Enum "Report Selection Usage"; TempReportSelections: Record "Report Selections" temporary)
    var
        ReportLayoutSelectionLocal: Record "Report Layout Selection";
        CustomLayoutReporting: Codeunit "Custom Layout Reporting";
        CustomerStatementSub: codeunit "Customer Statement Subscr";
        LastUsedParameters: Text;
        IsHandled: Boolean;
        OutStream: OutStream;
    begin
        if TempReportSelections."Report Layout Name" <> '' then
            ReportLayoutSelectionLocal.SetTempLayoutSelectedName(TempReportSelections."Report Layout Name", TempReportSelections."Report Layout AppID")
        else
            ReportLayoutSelectionLocal.SetTempLayoutSelected(LayoutCode);
        BindSubscription(CustomerStatementSub);
        UnbindSubscription(CustomerStatementSub);

        if not IsHandled then begin
            TempBlob.CreateOutStream(OutStream);
            LastUsedParameters := CustomLayoutReporting.GetReportRequestPageParameters(ReportID);
            Report.SaveAs(ReportID, LastUsedParameters, ReportFormat::Word, OutStream, GetRecRef(RecordVariant));
        end;

        ReportLayoutSelectionLocal.ClearTempLayoutSelected();

        Commit();
    end;

    local procedure GetRecRef(RecVariant: Variant) RecRef: RecordRef
    begin
        if RecVariant.IsRecordRef() then
            exit(RecVariant);
        if RecVariant.IsRecord() then
            RecRef.GetTable(RecVariant);
    end;
}

END

Hope this will help.

Thanks for reading.

ZHU

コメント

Copied title and URL