Codeunit 44 ReportManagement
- App
- Base Application
- Namespace
- Microsoft.Foundation.Reporting
- Versions
- 17-28
Procedures, 9Events, 20Obsolete, 4
Versions171819202122232425262728
Source29
Source in 29
src/Layers/W1/BaseApp/Foundation/Reporting/ReportManagement.Codeunit.al434 lines, Copyright (c) Microsoft Corporation. MIT
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.Foundation.Reporting;
using Microsoft.Foundation.Company;
using System.Device;
using System.Environment;
using System.Environment.Configuration;
using System.Reflection;
using System.Text;
using System.Utilities;
codeunit 44 ReportManagement
{
SingleInstance = true;
trigger OnRun()
begin
end;
var
NotSupportedErr: Label 'The value is not supported.';
NoWritePermissionsErr: Label 'Unable to set the default printer. You need write (Insert, Modify and Delete) permission for the Printer Selection table.';
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'GetPrinterName', '', false, false)]
local procedure GetPrinterNameSubscriber(ReportID: Integer; var PrinterName: Text[250])
begin
GetPrinterName(ReportID, PrinterName);
end;
procedure GetPrinterName(ReportID: Integer; var PrinterName: Text[250])
var
PrinterSelection: Record "Printer Selection";
begin
Clear(PrinterSelection);
if PrinterSelection.ReadPermission then
if not PrinterSelection.Get(UserId, ReportID) then
if not PrinterSelection.Get('', ReportID) then
if not PrinterSelection.Get(UserId, 0) then
if PrinterSelection.Get('', 0) then;
PrinterName := PrinterSelection."Printer Name";
OnAfterGetPrinterName(ReportID, PrinterName, PrinterSelection);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Printer Setup", 'OnSetAsDefaultPrinter', '', false, false)]
local procedure OnSetAsDefaultPrinterForCurrentUser(PrinterID: Text; UserID: Text; var IsHandled: Boolean)
var
PrinterSelection: Record "Printer Selection";
[SecurityFiltering(SecurityFilter::Ignored)]
PrinterSelection2: Record "Printer Selection";
begin
if IsHandled then
exit;
if not PrinterSelection2.WritePermission then
Error(NoWritePermissionsErr);
if PrinterSelection.Get(UserID, 0) then begin
PrinterSelection."Printer Name" := CopyStr(PrinterID, 1, MaxStrLen((PrinterSelection."Printer Name")));
PrinterSelection.Modify(true);
end else begin
PrinterSelection.Validate("User ID", UserID);
PrinterSelection.Validate("Report ID", 0);
PrinterSelection."Printer Name" := CopyStr(PrinterID, 1, MaxStrLen((PrinterSelection."Printer Name")));
PrinterSelection.Insert(true);
end;
IsHandled := true;
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Printer Setup", 'GetPrinterSelectionsPage', '', false, false)]
procedure GetPrinterSelectionsPage(var PageID: Integer; var IsHandled: Boolean)
begin
if IsHandled then
exit;
PageID := Page::"Printer Selections";
IsHandled := true;
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'GetPaperTrayForReport', '', false, false)]
local procedure GetPaperTrayForReport(ReportID: Integer; var FirstPage: Integer; var DefaultPage: Integer; var LastPage: Integer)
begin
OnAfterGetPaperTrayForReport(ReportID, FirstPage, DefaultPage, LastPage)
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'HasCustomLayout', '', false, false)]
local procedure HasCustomLayout(ObjectType: Option "Report","Page"; ObjectID: Integer; var LayoutType: Option "None",RDLC,Word,Excel,Custom)
var
begin
if ObjectType <> ObjectType::Report then
Error(NotSupportedErr);
OnAfterHasCustomLayout(ObjectType, ObjectID, LayoutType);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'CustomDocumentMergerEx', '', false, false)]
local procedure CustomDocumentMergerEx(ObjectID: Integer; ReportAction: Option SaveAsPdf,SaveAsWord,SaveAsExcel,Preview,Print,SaveAsHtml; ObjectPayload: JsonObject; XmlData: InStream; LayoutData: InStream; var DocumentStream: OutStream; var Success: Boolean)
begin
if (Success) then
exit;
OnCustomDocumentMergerEx(ObjectID, ReportAction, ObjectPayload, XmlData, LayoutData, DocumentStream, Success);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'SelectedBuiltinLayoutType', '', false, false)]
local procedure SelectedBuiltinLayoutType(ObjectID: Integer; var LayoutType: Option "None",RDLC,Word,Excel,Custom)
var
ReportLayoutSelection: Record "Report Layout Selection";
begin
LayoutType := ReportLayoutSelection.SelectedBuiltinLayoutType(ObjectID);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'SubstituteReport', '', false, false)]
local procedure SubstituteReport(ReportId: Integer; RunMode: Option Normal,ParametersOnly,Execute,Print,SaveAs,RunModal; RequestPageXml: Text; RecordRef: RecordRef; var NewReportId: Integer)
begin
OnAfterSubstituteReport(ReportId, RunMode, RequestPageXml, RecordRef, NewReportId);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'OnDocumentPrintReady', '', false, false)]
local procedure OnDocumentPrintReady(ObjectType: Option "Report","Page"; ObjectID: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var Success: Boolean);
begin
if ObjectType <> ObjectType::Report then
Error(NotSupportedErr);
OnAfterDocumentPrintReady(ObjectType, ObjectId, ObjectPayload, DocumentStream, Success);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'SetupPrinters', '', true, true)]
procedure SetupPrinters(var Printers: Dictionary of [Text[250], JsonObject]);
begin
OnAfterSetupPrinters(Printers);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'OnIntermediateDocumentReady', '', false, false)]
local procedure OnIntermediateDocumentReady(ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var TargetStream: OutStream; var Success: Boolean)
begin
OnAfterIntermediateDocumentReady(ObjectId, ObjectPayload, DocumentStream, TargetStream, Success);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'OnDocumentReady', '', false, false)]
local procedure OnDocumentReady(ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var TargetStream: OutStream; var Success: Boolean)
begin
OnAfterDocumentReady(ObjectId, ObjectPayload, DocumentStream, TargetStream, Success);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'OnDocumentDownload', '', false, false)]
local procedure OnDocumentDownload(ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var Success: Boolean)
begin
OnAfterDocumentDownload(ObjectId, ObjectPayload, DocumentStream, Success);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'SelectReportLayoutCode', '', false, false)]
local procedure SelectReportLayoutCode(ObjectId: Integer; var LayoutCode: Text; var LayoutType: Option "None",RDLC,Word,Excel,Custom; var Success: Boolean)
var
CustomReportLayout: Record "Custom Report Layout";
FeatureKey: Record "Feature Key";
ReportLayoutSelection: Record "Report Layout Selection";
DesignTimeReportSelection: Codeunit "Design-time Report Selection";
AppLayoutType: Enum "Custom Report Layout Type";
SelectedLayoutName: Text[250];
SelectedAppID: Guid;
PlatformRenderingInPlatformTxt: Label 'RenderWordReportsInPlatform', Locked = true;
begin
OnSelectReportLayoutCode(ObjectId, LayoutCode, LayoutType, Success);
if Success then
exit;
LayoutType := LayoutType::None; // Unknown layout type
SelectedLayoutName := DesignTimeReportSelection.GetSelectedLayout();
SelectedAppID := DesignTimeReportSelection.GetSelectedAppID();
// Temporarily selected layout for Design-time report execution or for looping in batch report scenarios?
if SelectedLayoutName = '' then
// look in the app layout selection table for a selected layout for this report id.
if ReportLayoutSelection.Get(ObjectId, CompanyName) and
(ReportLayoutSelection.Type = ReportLayoutSelection.Type::"Custom Layout")
then
SelectedLayoutName := ReportLayoutSelection."Custom Report Layout Code";
if (SelectedLayoutName <> '') and (StrLen(SelectedLayoutName) <= MaxStrLen(CustomReportLayout."Code")) then
// The code field in Custom Report Layout table can have a maximum size of 20 characters.
if CustomReportLayout.Get(SelectedLayoutName.ToUpper()) then begin
LayoutCode := CustomReportLayout.Code;
AppLayoutType := CustomReportLayout.Type;
case AppLayoutType of
AppLayoutType::RDLC:
LayoutType := LayoutType::RDLC;
AppLayoutType::Word:
LayoutType := LayoutType::Word;
else
// Layout Type extensions
if (FeatureKey.Get(PlatformRenderingInPlatformTxt) and (FeatureKey.Enabled = FeatureKey.Enabled::"All Users")) then
// Platform rendering - The OnCustomDocumentMerger event will handle the rendering logic
LayoutType := LayoutType::Custom
else
// App rendering - The type will be treated like a word file and rendered by the app.
LayoutType := LayoutType::Word;
end;
Success := true;
exit;
end;
if SelectedLayoutName <> '' then begin
// A layout code is defined, but not found in application table. The layout type is not known and it's expected that the code refers to a layout in the platform.
// Return the layout code to platform for further processing.
if IsNullGuid(SelectedAppID) then
LayoutCode := SelectedLayoutName
else
LayoutCode := SelectedLayoutName + '::' + Format(SelectedAppID);
Success := true;
end;
DesignTimeReportSelection.ClearLayoutSelection();
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'FetchReportLayoutByCode', '', false, false)]
local procedure FetchReportLayoutByCode(ObjectId: Integer; LayoutCode: Text; var TargetStream: OutStream; var Success: Boolean)
var
CustomReportLayout: Record "Custom Report Layout";
TempBlobIn: codeunit "Temp Blob";
TempInStream: InStream;
begin
OnFetchReportLayoutByCode(ObjectId, LayoutCode, TargetStream, Success);
if Success then
exit;
if not CustomReportLayout.Get(LayoutCode) then
LayoutCode := '';
if LayoutCode <> '' then begin
CustomReportLayout.GetLayoutBlob(TempBlobIn);
TempBlobIn.CreateInStream(TempInStream);
CopyStream(TargetStream, TempInStream);
Success := true;
end;
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'SelectReportLayoutUI', '', false, false)]
local procedure SelectReportLayoutUI(ObjectID: Integer; var LayoutName: Text; var LayoutAppID: Guid; var Success: Boolean)
var
ReportLayoutList: Record "Report Layout List";
IsSelectionHandled: Boolean;
begin
ReportLayoutList.SetRange(ReportLayoutList."Report ID", ObjectID);
OnSelectReportLayout(ReportLayoutList, IsSelectionHandled);
if IsSelectionHandled and (ReportLayoutList."Report ID" = ObjectID) then begin
LayoutName := ReportLayoutList."Name";
LayoutAppID := ReportLayoutList."Application ID";
Success := true;
end;
OnAfterSelectReportLayoutUI(ObjectID, LayoutName, LayoutAppID, Success);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'GetFilename', '', false, false)]
local procedure GetFilename(ReportID: Integer; Caption: Text[250]; ObjectPayload: JsonObject; FileExtension: Text[30]; ReportRecordRef: RecordRef; var Filename: Text; var Success: Boolean)
begin
if Success then
exit;
OnGetFilename(ReportID, Caption, ObjectPayload, FileExtension, ReportRecordRef, Filename, Success);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'GetCompanyMetadata', '', false, false)]
local procedure GetCompanyMetadataSubscriber(ReportId: Integer; var CompanyMetadata: JsonObject)
begin
this.GetCompanyMetadata(CompanyMetadata);
end;
/// <summary>
/// Populates the shared CompanyMetadata payload from Company Information for the report layouts'
/// company block. Empty-safe: with no Company Information record the fields are emitted blank
/// rather than erroring. Public so it can be invoked/verified directly; extension and
/// localization fields are added by subscribing to the platform GetCompanyMetadata event
/// directly (the payload is additive), so no BaseApp OnAfter event is exposed here.
/// </summary>
/// <param name="CompanyMetadata">The JSON object to merge the company payload into; existing keys with the same names are overwritten.</param>
[InherentPermissions(PermissionObjectType::TableData, Database::"Company Information", 'r')]
procedure GetCompanyMetadata(var CompanyMetadata: JsonObject)
var
CompanyInfo: Record "Company Information";
CompanyMetadataBuilder: Codeunit "Company Metadata Builder";
begin
if not CompanyInfo.Get() then
CompanyInfo.Init();
// Display name and logo are not plain Company Information fields (a session display name and
// a Base64-encoded BLOB), so they are set here; the rest of the block is mapped by the builder.
CompanyMetadataBuilder.SetDisplayName(this.GetCompanyDisplayName());
CompanyMetadataBuilder.SetLogo(this.GetLogoBase64(CompanyInfo));
CompanyMetadataBuilder.PopulateFromCompanyInformation(CompanyInfo);
CompanyMetadataBuilder.WriteTo(CompanyMetadata);
end;
/// <summary>
/// Company display name, mirroring how the platform builds ReportRequest/CompanyDisplayName
/// (ReportRequestXmlRuntime): the tenant Company record's display name
/// (CompanyProperty.DisplayName() -> session.Company.CompanyDisplayName), falling back to the
/// company name when the display name is blank. NOT Company Information."Name 2".
/// </summary>
local procedure GetCompanyDisplayName(): Text
var
DisplayName: Text;
begin
DisplayName := CompanyProperty.DisplayName();
if DisplayName = '' then
DisplayName := CompanyName();
exit(DisplayName);
end;
local procedure GetLogoBase64(var CompanyInfo: Record "Company Information"): Text
var
Base64Convert: Codeunit "Base64 Convert";
InStr: InStream;
begin
// Use the Ok return: CalcFields on a BLOB re-reads the row and raises a runtime error when
// the record does not exist (the CompanyInfo.Init() path), which would break the empty-safe
// contract of GetCompanyMetadata.
if not CompanyInfo.CalcFields(Picture) then
exit('');
if not CompanyInfo.Picture.HasValue() then
exit('');
CompanyInfo.Picture.CreateInStream(InStr);
exit(Base64Convert.ToBase64(InStr));
end;
[IntegrationEvent(false, false)]
procedure OnSelectReportLayout(var ReportLayoutList: Record "Report Layout List"; var Handled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterSelectReportLayoutUI(ObjectID: Integer; var LayoutName: Text; var LayoutAppID: Guid; Success: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterGetPrinterName(ReportID: Integer; var PrinterName: Text[250]; PrinterSelection: Record "Printer Selection")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterHasCustomLayout(ObjectType: Option "Report","Page"; ObjectID: Integer; var LayoutType: Option "None",RDLC,Word,Excel,Custom)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterGetPaperTrayForReport(ReportID: Integer; var FirstPage: Integer; var DefaultPage: Integer; var LastPage: Integer)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterSubstituteReport(ReportId: Integer; RunMode: Option Normal,ParametersOnly,Execute,Print,SaveAs,RunModal; RequestPageXml: Text; RecordRef: RecordRef; var NewReportId: Integer)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterDocumentPrintReady(ObjectType: Option "Report","Page"; ObjectID: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var Success: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterSetupPrinters(var Printers: Dictionary of [Text[250], JsonObject]);
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterIntermediateDocumentReady(ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var TargetStream: OutStream; var Success: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterDocumentReady(ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var TargetStream: OutStream; var Success: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterDocumentDownload(ObjectId: Integer; ObjectPayload: JsonObject; DocumentStream: InStream; var Success: Boolean)
begin
end;
/// <summary>
/// Invoke the OnCustomDocumentMergeEx trigger, which handled user defiend report renders given a dataset and a layout. The Render must be implemented in AL and return the output stream as defined by the format given in ReportAction.
/// </summary>
/// <param name="ObjectId">The report object id.</param>
/// <param name="ReportAction">The report action, which can be one of SaveAsPdf, SaveAsWord, SaveAsExcel, Preview, Print or SaveAsHtml.</param>
/// <param name="ObjectPayload">The JSON payload that defines metadata for the present report run.</param>
/// <param name="XmlData">The xml data set in an input stream</param>
/// <param name="LayoutData">The layout input stream. The actual format stored in the stream is defined by the layoutmodel json property (custom formats by the layoutmimetype property in the payload).</param>
/// <param name="DocumentStream">Output stream that will contain the rendered output documents.</param>
/// <param name="IsHandled">Will be set to true if the procedure call handled the merge.</param>
[IntegrationEvent(false, false)]
local procedure OnCustomDocumentMergerEx(ObjectID: Integer; ReportAction: Option SaveAsPdf,SaveAsWord,SaveAsExcel,Preview,Print,SaveAsHtml; ObjectPayload: JsonObject; var XmlData: InStream; LayoutData: InStream; var DocumentStream: OutStream; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Fetch the currently selected layout code and layout type from application.
/// </summary>
/// <param name="ObjectId">The object id.</param>
/// <param name="LayoutCode">The report layout code if an application override has been set for the current run.</param>
/// <param name="LayoutType">The Layout type contained in the target stream.</param>
/// <param name="IsHandled">Will be set to true if the subscriber handled the action.</param>
/// <remarks>Internal event that will be removed when the report runtime API has been updated</remarks>
[IntegrationEvent(false, false)]
local procedure OnSelectReportLayoutCode(ObjectId: Integer; var LayoutCode: Text; var LayoutType: Option "None",RDLC,Word,Excel,Custom; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Fetch the currently selected layout code from application.
/// </summary>
/// <param name="ObjectId">The object id.</param>
/// <param name="LayoutCode">The report layout code if an application override has been set for the current run.</param>
/// <param name="TargetStream">The layout will be written to this stream.</param>
/// <param name="IsHandled">Will be set to true if the layout was found in the application database.</param>
/// <remarks>Internal event that will be removed when the report runtime API has been updated.</remarks>
[IntegrationEvent(false, false)]
local procedure OnFetchReportLayoutByCode(ObjectId: Integer; LayoutCode: Text; var TargetStream: OutStream; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnGetFilename(ReportID: Integer; Caption: Text[250]; ObjectPayload: JsonObject; FileExtension: Text[30]; ReportRecordRef: RecordRef; var Filename: Text; var Success: Boolean)
begin
end;
}