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
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:
- Change the format of the report file save to Word
- 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
コメント