Codeunit 18 Financial Report Mgt.
- App
- Base Application
- Namespace
- Microsoft.Finance.FinancialReports
- Versions
- 21-28
Procedures, 26Events, 1Obsolete, 1
Versions171819202122232425262728
Source29
Source in 29
src/Layers/W1/BaseApp/Finance/FinancialReports/FinancialReportMgt.Codeunit.al830 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.Finance.FinancialReports;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Foundation.Period;
using System.Environment;
using System.Environment.Configuration;
using System.IO;
using System.Telemetry;
/// <summary>
/// Central management codeunit for financial report operations including configuration package import/export,
/// report printing, date formula calculations, and user notification handling.
/// </summary>
/// <remarks>
/// Provides core functionality for financial report lifecycle management, XML exchange operations,
/// date filter calculations for account schedule lines, and integration with configuration packages
/// for template distribution and customization scenarios.
/// </remarks>
codeunit 18 "Financial Report Mgt."
{
TableNo = "Financial Report";
var
FinRepPrefixTxt: Label 'FIN.REP.', MaxLength = 10, Comment = 'Part of the name for the confguration package, stands for Financial Report';
TwoPosTxt: Label '%1%2', Locked = true;
PackageNameTxt: Label 'Financial Report - %1', MaxLength = 40, Comment = '%1 - financial report name';
PackageImportErr: Label 'The financial report could not be imported.';
RowsEditWarningNotificationMsg: Label 'Changes to this row definition will affect all financial reports using it.';
RowsNotificationIdTok: Label 'e6374e6b-dba0-43a0-9099-0ae20ee77f4b', Locked = true;
ColumnsEditWarningNotificationMsg: Label 'Changes to this column definition will affect all financial reports using it.';
ColumnsNotificationIdTok: Label '883e213e-08bd-4154-b929-87f689848f10', Locked = true;
DontShowAgainMsg: Label 'Don''t show again';
TelemetryEventTxt: Label 'Financial Report Definition %1: %2', Comment = '%1 = event type, %2 = report', Locked = true;
OpenFinancialReportsLbl: Label 'Open Financial Reports';
OpenWhereUsedLbl: Label 'Open Where-Used';
NotifyUpdateFinancialReportNameTxt: Label 'Notify about updating financial reports.';
NotifyUpdateFinancialReportDescTxt: Label 'Notify that financial reports should be updated after someone creates or changes a G/L account.';
UpdateFinancialReportMsg: Label 'You have created one or more G/L accounts and might need to update your financial reports. We recommend that you review your financial reports by choosing the Open Financial Reports action.';
UpdateFinancialReportNotificationIdTok: Label 'cc02b894-bef8-4945-8042-f177422f8906', Locked = true;
UpdateRowDefinitionMsg: Label 'You have changed one or more G/L accounts and might need to update your financial report row definitions. We recommend that you review your row definitions by choosing the Open Where-Used action.';
UpdateRowDefGLAccNoKeyTok: Label 'GLAccountNo', Locked = true;
StatusBlockedErr: Label 'The current %1 has a blocked status of %2.', Comment = '%1 = type, %2 = status';
internal procedure LaunchEditRowsWarningNotification()
var
MyNotifications: Record "My Notifications";
EditWarningNotification: Notification;
begin
if not MyNotifications.IsEnabled(RowsNotificationIdTok) then
exit;
EditWarningNotification.AddAction(DontShowAgainMsg, Codeunit::"Financial Report Mgt.", 'DisableRowsDefinitionNotification');
EditWarningNotification.Message := RowsEditWarningNotificationMsg;
EditWarningNotification.Scope := NotificationScope::LocalScope;
EditWarningNotification.Send();
end;
internal procedure LaunchEditColumnsWarningNotification()
var
MyNotifications: Record "My Notifications";
EditWarningNotification: Notification;
begin
if not MyNotifications.IsEnabled(ColumnsNotificationIdTok) then
exit;
EditWarningNotification.AddAction(DontShowAgainMsg, Codeunit::"Financial Report Mgt.", 'DisableColumnsDefinitionNotification');
EditWarningNotification.Message := ColumnsEditWarningNotificationMsg;
EditWarningNotification.Scope := NotificationScope::LocalScope;
EditWarningNotification.Send();
end;
internal procedure DisableColumnsDefinitionNotification(WarningNotification: Notification)
var
MyNotifications: Record "My Notifications";
begin
if not MyNotifications.Disable(ColumnsNotificationIdTok) then
MyNotifications.InsertDefault(ColumnsNotificationIdTok, ColumnsEditWarningNotificationMsg, '', false);
end;
internal procedure DisableRowsDefinitionNotification(WarningNotification: Notification)
var
MyNotifications: Record "My Notifications";
begin
if not MyNotifications.Disable(RowsNotificationIdTok) then
MyNotifications.InsertDefault(RowsNotificationIdTok, RowsEditWarningNotificationMsg, '', false);
end;
/// <summary>
/// Exports a financial report configuration as an XML package for distribution and deployment.
/// Creates a configuration package containing the financial report and its row/column definitions.
/// </summary>
/// <param name="FinancialReport">Financial report record to export</param>
/// <remarks>
/// Generates a complete export package including account schedule names, lines, and column layouts.
/// Enables template distribution and environment-to-environment migration of financial report definitions.
/// </remarks>
procedure XMLExchangeExport(FinancialReport: Record "Financial Report")
var
ConfigPackage: Record "Config. Package";
ConfigXMLExchange: Codeunit "Config. XML Exchange";
begin
AddFinancialReportToConfigPackage(FinancialReport.Name, ConfigPackage);
Commit();
ConfigXMLExchange.ExportPackage(ConfigPackage);
LogImportExportTelemetry(FinancialReport.Name, 'exported');
end;
local procedure AddFinancialReportToConfigPackage(FinancialReportName: Code[10]; var ConfigPackage: Record "Config. Package")
var
ConfigPackageTable: Record "Config. Package Table";
ConfigPackageFilter: Record "Config. Package Filter";
ConfigPackageField: Record "Config. Package Field";
FinancialReport: Record "Financial Report";
AccScheduleName: Record "Acc. Schedule Name";
ColumnLayoutName: Record "Column Layout Name";
ColumnLayout: Record "Column Layout";
ConfigPackageManagement: Codeunit "Config. Package Management";
PackageCode: Code[20];
ColumnGroupCode: Code[10];
begin
FinancialReport.Get(FinancialReportName);
AccScheduleName.Get(FinancialReport."Financial Report Row Group");
PackageCode := StrSubstNo(TwoPosTxt, FinRepPrefixTxt, FinancialReport.Name);
if ConfigPackage.Get(PackageCode) then
ConfigPackage.Delete(true);
ConfigPackageManagement.InsertPackage(ConfigPackage, PackageCode, StrSubstNo(PackageNameTxt, FinancialReport.Name), true);
ConfigPackageManagement.InsertPackageTable(ConfigPackageTable, PackageCode, Database::"Financial Report");
ConfigPackageManagement.InsertPackageFilter(ConfigPackageFilter, PackageCode, Database::"Financial Report", 0, FinancialReport.FieldNo(Name), FinancialReportName);
AccScheduleName.AddRowDefinitionToConfigPackage(FinancialReport."Financial Report Row Group", ConfigPackage, PackageCode);
if FinancialReport."Financial Report Column Group" <> '' then begin
ColumnGroupCode := FinancialReport."Financial Report Column Group";
ConfigPackageManagement.InsertPackageTable(ConfigPackageTable, PackageCode, Database::"Column Layout Name");
ConfigPackageManagement.InsertPackageFilter(ConfigPackageFilter, PackageCode, Database::"Column Layout Name", 0, ColumnLayoutName.FieldNo(Name), ColumnGroupCode);
ConfigPackageManagement.InsertPackageTable(ConfigPackageTable, PackageCode, Database::"Column Layout");
ConfigPackageManagement.InsertPackageFilter(ConfigPackageFilter, PackageCode, Database::"Column Layout", 0, ColumnLayout.FieldNo("Column Layout Name"), ColumnGroupCode);
if ConfigPackageField.Get(PackageCode, Database::"Column Layout Name", ColumnLayoutName.FieldNo("Analysis View Name")) then
ConfigPackageField.Delete();
end;
end;
/// <summary>
/// Imports a financial report configuration from an XML package file selected by the user.
/// Processes the imported configuration package and applies it to create the financial report.
/// </summary>
/// <param name="FinancialReport">Target financial report record for import context</param>
/// <remarks>
/// Provides complete import functionality including validation, dependency resolution,
/// and error handling for configuration package deployment scenarios.
/// </remarks>
procedure XMLExchangeImport(FinancialReport: Record "Financial Report")
var
ConfigXMLExchange: Codeunit "Config. XML Exchange";
PackageCode: Code[20];
begin
if ConfigXMLExchange.ImportPackageXMLFromClient() then begin
PackageCode := ConfigXMLExchange.GetImportedPackageCode();
Commit();
ApplyPackage(PackageCode);
end;
end;
/// <summary>
/// Applies a configuration package to create or update financial report definitions.
/// Processes package tables and validates data before applying changes to the database.
/// </summary>
/// <param name="PackageCode">Configuration package code to apply</param>
/// <remarks>
/// Handles package validation, dependency resolution, and error reporting during
/// financial report template deployment from configuration packages.
/// </remarks>
procedure ApplyPackage(PackageCode: Code[20])
var
ConfigPackage: Record "Config. Package";
ConfigPackageTable: Record "Config. Package Table";
ConfigPackageMgt: Codeunit "Config. Package Management";
NewName: Code[10];
begin
if not ConfigPackage.Get(PackageCode) then
Error(PackageImportErr);
NewName := GetPackageFinancialReportName(PackageCode);
if NewName = '' then
Error(PackageImportErr);
ConfigPackageTable.SetRange("Package Code", PackageCode);
ConfigPackageMgt.ApplyPackage(ConfigPackage, ConfigPackageTable, false);
LogImportExportTelemetry(NewName, 'imported');
end;
local procedure GetPackageFinancialReportName(PackageCode: Code[20]) NewFinancialReportName: Code[10]
var
NewColumnLayoutName: Code[10];
OldColumnLayoutName: Code[10];
NewAccountScheduleName: Code[10];
OldAccountScheduleName: Code[10];
OldFinancialReportName: Code[10];
AccScheduleExists: Boolean;
ColumnLayoutExists: Boolean;
FinancialReportExists: Boolean;
begin
NewFinancialReportName := GetFinancialReportName(PackageCode, FinancialReportExists);
if NewFinancialReportName = '' then
exit('');
NewAccountScheduleName := GetAccountScheduleName(PackageCode, AccScheduleExists);
if NewAccountScheduleName = '' then
exit('');
NewColumnLayoutName := GetColumnLayoutName(PackageCode, ColumnLayoutExists);
if not FinancialReportExists and not AccScheduleExists and not ColumnLayoutExists then
exit(NewFinancialReportName);
OldFinancialReportName := NewFinancialReportName;
OldAccountScheduleName := NewAccountScheduleName;
OldColumnLayoutName := NewColumnLayoutName;
if not GetNewFinancialReportName(NewFinancialReportName, NewAccountScheduleName, NewColumnLayoutName) then
exit('');
RenamePackageContents(PackageCode, OldFinancialReportName, NewFinancialReportName, OldAccountScheduleName, NewAccountScheduleName, OldColumnLayoutName, NewColumnLayoutName);
end;
local procedure GetNewFinancialReportName(var FinancialReportName: Code[10]; var AccScheduleName: Code[10]; var ColumnLayoutName: Code[10]): Boolean
var
NewFinancialReport: Page "New Financial Report";
begin
NewFinancialReport.Set(FinancialReportName, AccScheduleName, ColumnLayoutName);
if NewFinancialReport.RunModal() = Action::OK then begin
FinancialReportName := NewFinancialReport.GetFinancialReportName();
AccScheduleName := NewFinancialReport.GetAccSchedName();
ColumnLayoutName := NewFinancialReport.GetColumnLayoutName();
exit(true);
end;
exit(false)
end;
local procedure RenamePackageContents(PackageCode: Code[20]; OldFinancialReportName: Code[10]; NewFinancialReportName: Code[10]; OldAccScheduleName: Code[10]; NewAccScheduleName: Code[10]; OldColumnLayoutName: Code[10]; NewColumnLayoutName: Code[10])
var
AccScheduleName: Record "Acc. Schedule Name";
begin
if (OldFinancialReportName = NewFinancialReportName) and (OldAccScheduleName = NewAccScheduleName) and (OldColumnLayoutName = NewColumnLayoutName) then
exit;
RenameFinancialReportInPackage(PackageCode, OldFinancialReportName, NewFinancialReportName, OldAccScheduleName, NewAccScheduleName, OldColumnLayoutName, NewColumnLayoutName);
AccScheduleName.RenameAccountScheduleInPackage(PackageCode, OldAccScheduleName, NewAccScheduleName);
RenameColumnLayoutInPackage(PackageCode, OldColumnLayoutName, NewColumnLayoutName);
end;
local procedure RenameFinancialReportInPackage(PackageCode: Code[20]; OldFinancialReportName: Code[10]; NewFinancialReportName: Code[10]; OldAccScheduleName: Code[10]; NewAccScheduleName: Code[10]; OldColumnLayoutName: Code[10]; NewColumnLayoutName: Code[10])
var
FinancialReport: Record "Financial Report";
ConfigPackageData: Record "Config. Package Data";
begin
ConfigPackageData.SetLoadFields(Value);
ConfigPackageData.SetRange("Package Code", PackageCode);
if OldFinancialReportName <> NewFinancialReportName then begin
ConfigPackageData.SetRange("Table ID", Database::"Financial Report");
ConfigPackageData.SetRange("Field ID", FinancialReport.FieldNo(Name));
ConfigPackageData.SetRange(Value, OldFinancialReportName);
ConfigPackageData.ModifyAll(Value, NewFinancialReportName);
end;
if OldAccScheduleName <> NewAccScheduleName then begin
ConfigPackageData.SetRange("Table ID", Database::"Financial Report");
ConfigPackageData.SetRange("Field ID", FinancialReport.FieldNo("Financial Report Row Group"));
ConfigPackageData.SetRange(Value, OldAccScheduleName);
ConfigPackageData.ModifyAll(Value, NewAccScheduleName);
end;
if OldColumnLayoutName <> NewColumnLayoutName then begin
ConfigPackageData.SetRange("Table ID", Database::"Financial Report");
ConfigPackageData.SetRange("Field ID", FinancialReport.FieldNo("Financial Report Column Group"));
ConfigPackageData.SetRange(Value, OldColumnLayoutName);
ConfigPackageData.ModifyAll(Value, NewColumnLayoutName);
end;
end;
internal procedure RenameColumnLayoutInPackage(PackageCode: Code[20]; OldName: Code[10]; NewName: Code[10])
var
ColumnLayoutName: Record "Column Layout Name";
ColumnLayout: Record "Column Layout";
ConfigPackageData: Record "Config. Package Data";
begin
if OldName = NewName then
exit;
ConfigPackageData.SetLoadFields(Value);
ConfigPackageData.SetRange("Package Code", PackageCode);
ConfigPackageData.SetRange(Value, OldName);
ConfigPackageData.SetRange("Table ID", Database::"Column Layout Name");
ConfigPackageData.SetRange("Field ID", ColumnLayoutName.FieldNo(Name));
ConfigPackageData.ModifyAll(Value, NewName);
ConfigPackageData.SetRange("Table ID", Database::"Column Layout");
ConfigPackageData.SetRange("Field ID", ColumnLayout.FieldNo("Column Layout Name"));
ConfigPackageData.ModifyAll(Value, NewName);
end;
local procedure GetFinancialReportName(PackageCode: Code[20]; var FinancialReportExists: Boolean) Name: Code[10]
var
FinancialReport: Record "Financial Report";
ConfigPackageData: Record "Config. Package Data";
ConfigPackageField: Record "Config. Package Field";
begin
FinancialReportExists := false;
if not ConfigPackageField.Get(PackageCode, Database::"Financial Report", FinancialReport.FieldNo(Name)) then
exit('');
ConfigPackageData.SetLoadFields(Value);
ConfigPackageData.SetRange("Package Code", PackageCode);
ConfigPackageData.SetRange("Table ID", Database::"Financial Report");
ConfigPackageData.SetRange("Field ID", FinancialReport.FieldNo(Name));
if ConfigPackageData.FindFirst() then begin
Name := CopyStr(ConfigPackageData.Value, 1, MaxStrLen(FinancialReport.Name));
FinancialReportExists := FinancialReport.Get(Name);
end;
end;
local procedure GetAccountScheduleName(PackageCode: Code[20]; var AccScheduleExists: Boolean) Name: Code[10]
var
AccScheduleName: Record "Acc. Schedule Name";
ConfigPackageData: Record "Config. Package Data";
ConfigPackageField: Record "Config. Package Field";
begin
AccScheduleExists := false;
if not ConfigPackageField.Get(PackageCode, Database::"Acc. Schedule Name", AccScheduleName.FieldNo(Name)) then
exit('');
ConfigPackageData.SetLoadFields(Value);
ConfigPackageData.SetRange("Package Code", PackageCode);
ConfigPackageData.SetRange("Table ID", Database::"Acc. Schedule Name");
ConfigPackageData.SetRange("Field ID", AccScheduleName.FieldNo(Name));
if ConfigPackageData.FindFirst() then begin
Name := CopyStr(ConfigPackageData.Value, 1, MaxStrLen(AccScheduleName.Name));
AccScheduleExists := AccScheduleName.Get(Name);
end;
end;
local procedure GetColumnLayoutName(PackageCode: Code[20]; var ColumnLayoutExists: Boolean) Name: Code[10]
var
ColumnLayoutName: Record "Column Layout Name";
ConfigPackageData: Record "Config. Package Data";
ConfigPackageField: Record "Config. Package Field";
begin
ColumnLayoutExists := false;
if not ConfigPackageField.Get(PackageCode, Database::"Column Layout Name", ColumnLayoutName.FieldNo(Name)) then
exit('');
ConfigPackageData.SetLoadFields(Value);
ConfigPackageData.SetRange("Package Code", PackageCode);
ConfigPackageData.SetRange("Table ID", Database::"Column Layout Name");
ConfigPackageData.SetRange("Field ID", ColumnLayoutName.FieldNo(Name));
if ConfigPackageData.FindFirst() then begin
Name := CopyStr(ConfigPackageData.Value, 1, MaxStrLen(ColumnLayoutName.Name));
ColumnLayoutExists := ColumnLayoutName.Get(Name);
end;
end;
/// <summary>
/// Prints a financial report using the Account Schedule report with the specified financial report configuration.
/// Provides extensibility through OnBeforePrint integration event for custom print handling.
/// </summary>
/// <param name="FinancialReport">Financial report record to print</param>
/// <remarks>
/// Integrates with standard Account Schedule report engine for consistent financial report output.
/// Supports custom printing scenarios through event subscription patterns.
/// </remarks>
procedure Print(FinancialReport: Record "Financial Report")
var
AccountSchedule: Report "Account Schedule";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforePrint(FinancialReport, IsHandled);
if IsHandled then
exit;
AccountSchedule.SetFinancialReportName(FinancialReport.Name);
AccountSchedule.Run();
end;
/// <summary>
/// Provides lookup functionality for financial report names with user selection dialog.
/// Returns the selected financial report name through the EntrdSchedName parameter.
/// </summary>
/// <param name="FinancialReportName">Current financial report name for initial selection</param>
/// <param name="EntrdSchedName">Returns the selected financial report name</param>
/// <returns>True if user confirmed selection, false if cancelled</returns>
procedure LookupName(FinancialReportName: Code[10]; var EntrdSchedName: Text[10]): Boolean
var
FinancialReport: Record "Financial Report";
begin
if not FinancialReport.Get(FinancialReportName) then;
if PAGE.RunModal(0, FinancialReport) <> ACTION::LookupOK then
exit(false);
EntrdSchedName := FinancialReport.Name;
exit(true);
end;
/// <summary>
/// Initializes financial reports by creating financial report records from existing account schedule names.
/// Migrates legacy account schedule configurations to the new financial reports framework.
/// </summary>
/// <remarks>
/// Runs only when no financial reports exist, ensuring backward compatibility during system upgrades.
/// Creates default financial reports for each account schedule name to maintain existing functionality.
/// </remarks>
procedure Initialize()
var
FinancialReport: Record "Financial Report";
AccScheduleName: Record "Acc. Schedule Name";
begin
if not FinancialReport.IsEmpty() then
exit;
if AccScheduleName.FindSet() then
repeat
CreateFinancialReportFromRowDefinition(AccScheduleName);
until AccScheduleName.Next() = 0;
end;
local procedure CreateFinancialReportFromRowDefinition(AccScheduleName: Record "Acc. Schedule Name")
var
FinancialReport: Record "Financial Report";
begin
FinancialReport.Init();
FinancialReport.Name := AccScheduleName.Name;
FinancialReport.Description := AccScheduleName.Description;
FinancialReport."Financial Report Row Group" := AccScheduleName.Name;
FinancialReport.Insert();
end;
internal procedure NotifyUpdateFinancialReport(var GLAccount: Record "G/L Account")
var
MyNotification: Record "My Notifications";
NotificationLifecycleMgt: Codeunit "Notification Lifecycle Mgt.";
UpdateFinancialReportNotification: Notification;
begin
if not GuiAllowed() then
exit;
if GLAccount.IsTemporary() then
exit;
if not MyNotification.IsEnabled(GetUpdateFinancialReportNotificationId()) then
exit;
UpdateFinancialReportNotification.Id := GetUpdateFinancialReportNotificationId();
UpdateFinancialReportNotification.Message := UpdateFinancialReportMsg;
UpdateFinancialReportNotification.AddAction(
OpenFinancialReportsLbl, Codeunit::"Financial Report Mgt.", 'OpenFinancialReports');
UpdateFinancialReportNotification.AddAction(
DontShowAgainMsg, Codeunit::"Financial Report Mgt.", 'HideUpdateFinancialReportNotification');
NotificationLifecycleMgt.SendNotification(UpdateFinancialReportNotification, GLAccount.RecordId);
end;
/// <summary>
/// Opens the Financial Reports page in response to a user notification action.
/// Provides direct navigation to financial reports management from notification messages.
/// </summary>
/// <param name="UpdateFinancialReportNotification">Notification context triggering the action</param>
procedure OpenFinancialReports(UpdateFinancialReportNotification: Notification)
begin
Page.Run(Page::"Financial Reports");
end;
internal procedure NotifyUpdateRowDefinition(var GLAccount: Record "G/L Account")
var
AccScheduleLine: Record "Acc. Schedule Line";
MyNotification: Record "My Notifications";
NotificationLifecycleMgt: Codeunit "Notification Lifecycle Mgt.";
UpdateRowDefinitionNotification: Notification;
begin
if not GuiAllowed() then
exit;
if GLAccount.IsTemporary() then
exit;
if not MyNotification.IsEnabled(GetUpdateFinancialReportNotificationId()) then
exit;
FilterAccScheduleLineByGLAccount(AccScheduleLine, GLAccount."No.");
if AccScheduleLine.IsEmpty() then
exit;
UpdateRowDefinitionNotification.Id := GetUpdateFinancialReportNotificationId();
UpdateRowDefinitionNotification.Message := UpdateRowDefinitionMsg;
UpdateRowDefinitionNotification.AddAction(
OpenWhereUsedLbl, Codeunit::"Financial Report Mgt.", 'OpenWhereUsed');
UpdateRowDefinitionNotification.AddAction(
DontShowAgainMsg, Codeunit::"Financial Report Mgt.", 'HideUpdateFinancialReportNotification');
UpdateRowDefinitionNotification.SetData(UpdateRowDefGLAccNoKeyTok, GLAccount."No.");
NotificationLifecycleMgt.SendNotification(UpdateRowDefinitionNotification, GLAccount.RecordId);
end;
#if not CLEAN28
[Obsolete('This function has been replaced by OpenWhereUsed and will be removed in a future release.', '28.0')]
procedure OpenRowDefinitions(UpdateFinancialReportNotification: Notification)
begin
Page.Run(Page::"Account Schedule Names");
end;
#endif
procedure OpenWhereUsed(UpdateFinancialReportNotification: Notification)
var
GLAccount: Record "G/L Account";
TempGLAccWhereUsed: Record "G/L Account Where-Used" temporary;
GLAccNo: Text;
begin
if not UpdateFinancialReportNotification.HasData(UpdateRowDefGLAccNoKeyTok) then
exit;
GLAccNo := UpdateFinancialReportNotification.GetData(UpdateRowDefGLAccNoKeyTok);
if GLAccNo = '' then
exit;
if FindGLAccountWhereUsedInAccScheduleLine(CopyStr(GLAccNo, 1, MaxStrLen(GLAccount."No.")), TempGLAccWhereUsed) then
Page.RunModal(0, TempGLAccWhereUsed);
end;
procedure FindGLAccountWhereUsedInAccScheduleLine(GLAccNo: Code[20]; var TempGLAccWhereUsed: Record "G/L Account Where-Used" temporary): Boolean
var
AccScheduleLine: Record "Acc. Schedule Line";
begin
FilterAccScheduleLineByGLAccount(AccScheduleLine, GLAccNo);
if AccScheduleLine.FindSet() then begin
TempGLAccWhereUsed."Table ID" := Database::"Acc. Schedule Line";
TempGLAccWhereUsed."Table Name" := CopyStr(AccScheduleLine.TableCaption(), 1, MaxStrLen(TempGLAccWhereUsed."Table Name"));
TempGLAccWhereUsed."Field Name" := CopyStr(AccScheduleLine.FieldCaption(Totaling), 1, MaxStrLen(TempGLAccWhereUsed."Field Name"));
repeat
TempGLAccWhereUsed."Key 1" := AccScheduleLine."Schedule Name";
TempGLAccWhereUsed."Key 2" := Format(AccScheduleLine."Line No.");
TempGLAccWhereUsed.Line := CopyStr(
StrSubstNo('%1=%2, %3=%4',
AccScheduleLine.FieldCaption("Schedule Name"), AccScheduleLine."Schedule Name",
AccScheduleLine.FieldCaption("Row No."), AccScheduleLine."Row No."),
1, MaxStrLen(TempGLAccWhereUsed.Line));
TempGLAccWhereUsed."Entry No." += 1;
TempGLAccWhereUsed.Insert();
until AccScheduleLine.Next() = 0;
exit(true);
end;
end;
local procedure FilterAccScheduleLineByGLAccount(var AccScheduleLine: Record "Acc. Schedule Line"; GLAccNo: Code[20])
begin
AccScheduleLine.SetFilter("Totaling Type", '%1|%2', AccScheduleLine."Totaling Type"::"Posting Accounts", AccScheduleLine."Totaling Type"::"Total Accounts");
AccScheduleLine.SetRange(Totaling, GLAccNo);
end;
procedure FindGLAccountWhereUsedInColumnLayout(GLAccNo: Code[20]; var TempGLAccWhereUsed: Record "G/L Account Where-Used" temporary): Boolean
var
ColumnLayout: Record "Column Layout";
begin
FilterColumnLayoutByGLAccount(ColumnLayout, GLAccNo);
if not ColumnLayout.FindSet() then
exit(false);
TempGLAccWhereUsed."Table ID" := Database::"Column Layout";
TempGLAccWhereUsed."Table Name" := CopyStr(ColumnLayout.TableCaption(), 1, MaxStrLen(TempGLAccWhereUsed."Table Name"));
TempGLAccWhereUsed."Field Name" := CopyStr(ColumnLayout.FieldCaption("G/L Account Totaling"), 1, MaxStrLen(TempGLAccWhereUsed."Field Name"));
repeat
TempGLAccWhereUsed."Key 1" := ColumnLayout."Column Layout Name";
TempGLAccWhereUsed."Key 2" := Format(ColumnLayout."Line No.");
TempGLAccWhereUsed.Line := CopyStr(
StrSubstNo('%1=%2, %3=%4',
ColumnLayout.FieldCaption("Column Layout Name"), ColumnLayout."Column Layout Name",
ColumnLayout.FieldCaption("Column No."), ColumnLayout."Column No."),
1, MaxStrLen(TempGLAccWhereUsed.Line));
TempGLAccWhereUsed."Entry No." += 1;
TempGLAccWhereUsed.Insert();
until ColumnLayout.Next() = 0;
exit(true);
end;
local procedure FilterColumnLayoutByGLAccount(var ColumnLayout: Record "Column Layout"; GLAccNo: Code[20])
begin
ColumnLayout.SetRange("G/L Account Totaling", GLAccNo);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Calc. G/L Acc. Where-Used", OnAfterCheckPostingGroups, '', false, false)]
local procedure OnAfterCheckPostingGroups(GLAccNo: Code[20]; var TempGLAccountWhereUsed: Record "G/L Account Where-Used" temporary)
begin
FindGLAccountWhereUsedInAccScheduleLine(GLAccNo, TempGLAccountWhereUsed);
FindGLAccountWhereUsedInColumnLayout(GLAccNo, TempGLAccountWhereUsed);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Calc. G/L Acc. Where-Used", OnShowExtensionPage, '', false, false)]
local procedure OnShowExtensionPage(GLAccountWhereUsed: Record "G/L Account Where-Used")
var
AccScheduleLine: Record "Acc. Schedule Line";
ColumnLayout: Record "Column Layout";
AccountSchedule: Page "Account Schedule";
ColumnLayoutPage: Page "Column Layout";
begin
case GLAccountWhereUsed."Table ID" of
Database::"Acc. Schedule Line":
begin
AccScheduleLine."Schedule Name" := CopyStr(GLAccountWhereUsed."Key 1", 1, MaxStrLen(AccScheduleLine."Schedule Name"));
if Evaluate(AccScheduleLine."Line No.", GLAccountWhereUsed."Key 2") then;
AccScheduleLine.Find();
AccountSchedule.SetAccSchedName(AccScheduleLine."Schedule Name");
AccountSchedule.SetRecord(AccScheduleLine);
AccountSchedule.Run();
end;
Database::"Column Layout":
begin
ColumnLayout."Column Layout Name" := CopyStr(GLAccountWhereUsed."Key 1", 1, MaxStrLen(ColumnLayout."Column Layout Name"));
if Evaluate(ColumnLayout."Line No.", GLAccountWhereUsed."Key 2") then;
ColumnLayout.Find();
ColumnLayoutPage.SetColumnLayoutName(ColumnLayout."Column Layout Name");
ColumnLayoutPage.SetRecord(ColumnLayout);
ColumnLayoutPage.Run();
end;
end;
end;
/// <summary>
/// Disables the financial report update notification permanently for the current user.
/// Prevents future notifications about financial report configuration updates.
/// </summary>
/// <param name="UpdateFinancialReportNotification">Notification to disable</param>
procedure HideUpdateFinancialReportNotification(UpdateFinancialReportNotification: Notification)
var
MyNotifications: Record "My Notifications";
begin
if not MyNotifications.Disable(UpdateFinancialReportNotification.Id) then
MyNotifications.InsertDefault(UpdateFinancialReportNotification.Id,
NotifyUpdateFinancialReportNameTxt, NotifyUpdateFinancialReportDescTxt, false);
end;
local procedure LogImportExportTelemetry(Name: Text; Action: Text)
var
EnvironmentInfo: Codeunit "Environment Information";
FeatureTelemetry: Codeunit "Feature Telemetry";
TelemetryDimensions: Dictionary of [Text, Text];
begin
if not EnvironmentInfo.IsSaaS() then
exit;
TelemetryDimensions.Add('ReportDefinitionCode', Name);
FeatureTelemetry.LogUsage('0000ONR', 'Financial Report', StrSubstNo(TelemetryEventTxt, Action, Name), TelemetryDimensions);
end;
/// <summary>
/// Returns the unique identifier for financial report update notifications.
/// Provides consistent notification ID for user preference management and notification control.
/// </summary>
/// <returns>GUID identifier for financial report update notifications</returns>
procedure GetUpdateFinancialReportNotificationId(): Guid
begin
exit(UpdateFinancialReportNotificationIdTok);
end;
procedure SetAccScheduleFilter(FinancialReport: Record "Financial Report"; var AccountSchedule: Report "Account Schedule")
var
AccScheduleLine: Record "Acc. Schedule Line";
begin
CalcAccScheduleLineDateFilter(FinancialReport, AccScheduleLine);
AccountSchedule.SetFinancialReportName(FinancialReport.Name);
AccountSchedule.SetDimPerspectiveName(FinancialReport.DimPerspective);
AccountSchedule.SetFilters(
AccScheduleLine.GetFilter("Date Filter"),
FinancialReport.GLBudgetFilter,
FinancialReport.CostBudgetFilter,
'',
FinancialReport.Dim1Filter,
FinancialReport.Dim2Filter,
FinancialReport.Dim3Filter,
FinancialReport.Dim4Filter,
FinancialReport.CashFlowFilter,
FinancialReport.GetEffectiveNegativeAmountFormat());
end;
procedure SetAccScheduleLineFilter(FinancialReport: Record "Financial Report"; var AccScheduleLine: Record "Acc. Schedule Line")
begin
AccScheduleLine.SetRange("Schedule Name", FinancialReport."Financial Report Row Group");
AccScheduleLine.SetFilter("Date Filter", FinancialReport.DateFilter);
AccScheduleLine.SetFilter("G/L Budget Filter", FinancialReport.GLBudgetFilter);
AccScheduleLine.SetFilter("Cost Budget Filter", FinancialReport.CostBudgetFilter);
AccScheduleLine.SetFilter("Business Unit Filter", '');
AccScheduleLine.SetFilter("Dimension 1 Filter", FinancialReport.Dim1Filter);
AccScheduleLine.SetFilter("Dimension 2 Filter", FinancialReport.Dim2Filter);
AccScheduleLine.SetFilter("Dimension 3 Filter", FinancialReport.Dim3Filter);
AccScheduleLine.SetFilter("Dimension 4 Filter", FinancialReport.Dim4Filter);
AccScheduleLine.SetFilter("Cash Flow Forecast Filter", FinancialReport.CashFlowFilter);
if FinancialReport.CostCenterFilter <> '' then
AccScheduleLine.SetFilter("Cost Center Filter", FinancialReport.CostCenterFilter);
end;
/// <summary>
/// Calculates and applies date filters to account schedule lines based on financial report date configuration.
/// Supports both explicit start/end date formulas and period formula expressions for flexible date range handling.
/// </summary>
/// <param name="FinancialReport">Financial report containing date filter configuration</param>
/// <param name="AccScheduleLine">Account schedule line record to apply date filters to</param>
/// <remarks>
/// Prioritizes start/end date formulas over period formulas. Handles period formula parsing
/// with localization support and fallback to standard date filter processing.
/// </remarks>
procedure CalcAccScheduleLineDateFilter(FinancialReport: Record "Financial Report"; var AccScheduleLine: Record "Acc. Schedule Line")
var
AccSchedManagement: Codeunit AccSchedManagement;
PeriodFormulaParser: Codeunit "Period Formula Parser";
PeriodError: Boolean;
EndDate: Date;
StartDate: Date;
begin
if (Format(FinancialReport.StartDateFilterFormula) <> '') or
(Format(FinancialReport.EndDateFilterFormula) <> '')
then begin
StartDate := CalcDate(FinancialReport.StartDateFilterFormula, WorkDate());
EndDate := CalcDate(FinancialReport.EndDateFilterFormula, WorkDate());
AccScheduleLine.SetRange("Date Filter", StartDate, EndDate);
exit;
end;
if FinancialReport.DateFilterPeriodFormula <> '' then
if PeriodFormulaParser.TryCalculatePeriodStartEnd(
FinancialReport.DateFilterPeriodFormula, FinancialReport.DateFilterPeriodFormulaLID,
WorkDate(), StartDate, EndDate, PeriodError)
then
if not PeriodError then begin
AccScheduleLine.SetRange("Date Filter", StartDate, EndDate);
exit;
end;
if FinancialReport.DateFilter <> '' then
if TrySetAccScheduleLineDateFilter(FinancialReport.DateFilter, AccScheduleLine) then
exit;
AccSchedManagement.FindPeriod(AccScheduleLine, '', FinancialReport.GetEffectivePeriodType());
end;
[TryFunction]
local procedure TrySetAccScheduleLineDateFilter(DateFilter: Text; var AccScheduleLine: Record "Acc. Schedule Line")
begin
AccScheduleLine.SetFilter("Date Filter", DateFilter);
end;
/// <summary>
/// Sets date filters on account schedule lines using explicit start and end date formulas.
/// Calculates the date range relative to work date and applies it as a date filter.
/// </summary>
/// <param name="AccScheduleLine">Account schedule line record to filter</param>
/// <param name="StartDateFormula">Start date formula relative to work date</param>
/// <param name="EndDateFormula">End date formula relative to work date</param>
/// <returns>True if date filter was applied, false if both formulas are empty</returns>
procedure SetAccScheduleLineStartEndDateFormula(var AccScheduleLine: Record "Acc. Schedule Line"; StartDateFormula: DateFormula; EndDateFormula: DateFormula): Boolean
var
EndDate: Date;
StartDate: Date;
begin
if (Format(StartDateFormula) = '') and (Format(EndDateFormula) = '')
then
exit(false);
StartDate := CalcDate(StartDateFormula, WorkDate());
EndDate := CalcDate(EndDateFormula, WorkDate());
AccScheduleLine.SetRange("Date Filter", StartDate, EndDate);
exit(true);
end;
/// <summary>
/// Sets date filters on account schedule lines using period formula expressions with localization support.
/// Parses period formulas and applies calculated date ranges to account schedule line filtering.
/// </summary>
/// <param name="AccScheduleLine">Account schedule line record to filter</param>
/// <param name="PeriodFormula">Period formula expression to parse and apply</param>
/// <param name="LanguageId">Language identifier for localized period formula parsing</param>
/// <returns>True if period formula was successfully parsed and applied, false otherwise</returns>
procedure SetAccScheduleLinePeriodFormula(var AccScheduleLine: Record "Acc. Schedule Line"; PeriodFormula: Code[20]; LanguageId: Integer): Boolean
var
PeriodFormulaParser: Codeunit "Period Formula Parser";
PeriodError: Boolean;
EndDate: Date;
StartDate: Date;
begin
if PeriodFormula = '' then
exit(false);
if PeriodFormulaParser.TryCalculatePeriodStartEnd(
PeriodFormula, LanguageId,
WorkDate(), StartDate, EndDate, PeriodError)
then begin
if PeriodError then
exit(false);
AccScheduleLine.SetRange("Date Filter", StartDate, EndDate);
exit(true);
end;
end;
procedure GetDefaultStatus(): Code[10]
var
GLSetup: Record "General Ledger Setup";
begin
GLSetup.Get();
if GLSetup.DefaultFinancialReportStatus <> '' then
exit(GLSetup.DefaultFinancialReportStatus);
end;
procedure CheckStatus(Type: Text; StatusCode: Code[10])
var
FinancialReportStatus: Record "Financial Report Status";
NotificationLifecycleMgt: Codeunit "Notification Lifecycle Mgt.";
StatusBlockedNotify: Notification;
begin
if StatusCode = '' then
exit;
if not FinancialReportStatus.Get(StatusCode) then
exit;
if not FinancialReportStatus.Blocked then
exit;
if FinancialReportStatus.WritePermission() then begin
StatusBlockedNotify.Message := StrSubstNo(StatusBlockedErr, Type, FinancialReportStatus.Code);
NotificationLifecycleMgt.RecallNotificationsForRecord(FinancialReportStatus.RecordId, false);
NotificationLifecycleMgt.SendNotification(StatusBlockedNotify, FinancialReportStatus.RecordId);
end else
Error(StatusBlockedErr, Type, FinancialReportStatus.Code);
end;
/// <summary>
/// Integration event raised before printing a financial report.
/// Enables custom print processing and allows bypassing standard print functionality.
/// </summary>
/// <param name="FinancialReport">Financial report being printed</param>
/// <param name="IsHandled">Set to true to skip standard print processing</param>
[IntegrationEvent(false, false)]
local procedure OnBeforePrint(var FinancialReport: Record "Financial Report"; var IsHandled: Boolean)
begin
end;
}