Skip to content

Commit 1e787d4

Browse files
committed
Add shared CompanyMetadata data source for report layouts
Work item: AB#640703 Subscribe to the platform GetCompanyMetadata business event in ReportManagement and populate a shared company block (name, formatted address, phone/fax/email/ home page, bank, VAT/registration, giro, logo) from Company Information, so report layouts can bind one shared source instead of hand-coding company fields. - New Company Metadata Builder codeunit: typed setters centralize the wire keys (the subscriber never hand-writes raw key strings), including the address-line repeater and paired field captions. - CompanyDisplayName is sourced from the Company record (CompanyProperty.DisplayName, fallback to CompanyName) to match how the platform builds ReportRequest, not Company Information "Name 2". - Empty-safe: absent Company Information fields emit present-but-empty values. - Add Tests-Report coverage for values, captions, display-name source, empty-safe behavior, and the address repeater.
1 parent b69a150 commit 1e787d4

3 files changed

Lines changed: 503 additions & 0 deletions

File tree

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// ------------------------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License. See License.txt in the project root for license information.
4+
// ------------------------------------------------------------------------------------------------
5+
namespace Microsoft.Foundation.Reporting;
6+
7+
/// <summary>
8+
/// Builds the CompanyMetadata JSON payload the platform reads at report run time. Typed setters
9+
/// centralize the wire keys so the subscriber never hand-writes raw key strings (a silent key
10+
/// mismatch would just yield blank fields). Keys mirror the platform ReportInformationStrings and
11+
/// are scoped by the &lt;CompanyMetadata&gt; container emitted into BCReportInformation.
12+
/// </summary>
13+
codeunit 9666 "Company Metadata Builder"
14+
{
15+
Access = Internal;
16+
InherentEntitlements = X;
17+
InherentPermissions = X;
18+
19+
var
20+
Payload: JsonObject;
21+
AddressLines: JsonArray;
22+
23+
procedure SetName(Value: Text)
24+
begin
25+
Put('CompanyName', Value);
26+
end;
27+
28+
procedure SetDisplayName(Value: Text)
29+
begin
30+
Put('CompanyDisplayName', Value);
31+
end;
32+
33+
procedure AddAddressLine(Value: Text)
34+
begin
35+
// Empty lines are skipped by the platform repeater, but keep the array clean here too.
36+
if Value <> '' then
37+
AddressLines.Add(Value);
38+
end;
39+
40+
procedure SetPhone(Value: Text)
41+
begin
42+
Put('CompanyPhone', Value);
43+
end;
44+
45+
procedure SetPhoneCaption(Value: Text)
46+
begin
47+
Put('CompanyPhoneCaption', Value);
48+
end;
49+
50+
procedure SetFaxNo(Value: Text)
51+
begin
52+
Put('CompanyFaxNo', Value);
53+
end;
54+
55+
procedure SetFaxNoCaption(Value: Text)
56+
begin
57+
Put('CompanyFaxNoCaption', Value);
58+
end;
59+
60+
procedure SetEmail(Value: Text)
61+
begin
62+
Put('CompanyEmail', Value);
63+
end;
64+
65+
procedure SetEmailCaption(Value: Text)
66+
begin
67+
Put('CompanyEmailCaption', Value);
68+
end;
69+
70+
procedure SetHomePage(Value: Text)
71+
begin
72+
Put('CompanyHomePage', Value);
73+
end;
74+
75+
procedure SetHomePageCaption(Value: Text)
76+
begin
77+
Put('CompanyHomePageCaption', Value);
78+
end;
79+
80+
procedure SetLogo(Base64Value: Text)
81+
begin
82+
Put('CompanyLogo', Base64Value);
83+
end;
84+
85+
procedure SetVATRegistrationNo(Value: Text)
86+
begin
87+
Put('CompanyVATRegistrationNo', Value);
88+
end;
89+
90+
procedure SetVATRegistrationNoCaption(Value: Text)
91+
begin
92+
Put('CompanyVATRegistrationNoCaption', Value);
93+
end;
94+
95+
procedure SetRegistrationNo(Value: Text)
96+
begin
97+
Put('CompanyRegistrationNo', Value);
98+
end;
99+
100+
procedure SetRegistrationNoCaption(Value: Text)
101+
begin
102+
Put('CompanyRegistrationNoCaption', Value);
103+
end;
104+
105+
procedure SetBankName(Value: Text)
106+
begin
107+
Put('CompanyBankName', Value);
108+
end;
109+
110+
procedure SetBankNameCaption(Value: Text)
111+
begin
112+
Put('CompanyBankNameCaption', Value);
113+
end;
114+
115+
procedure SetBankAccountNo(Value: Text)
116+
begin
117+
Put('CompanyBankAccountNo', Value);
118+
end;
119+
120+
procedure SetBankAccountNoCaption(Value: Text)
121+
begin
122+
Put('CompanyBankAccountNoCaption', Value);
123+
end;
124+
125+
procedure SetBankBranchNo(Value: Text)
126+
begin
127+
Put('CompanyBankBranchNo', Value);
128+
end;
129+
130+
procedure SetBankBranchNoCaption(Value: Text)
131+
begin
132+
Put('CompanyBankBranchNoCaption', Value);
133+
end;
134+
135+
procedure SetIBAN(Value: Text)
136+
begin
137+
Put('CompanyIBAN', Value);
138+
end;
139+
140+
procedure SetIBANCaption(Value: Text)
141+
begin
142+
Put('CompanyIBANCaption', Value);
143+
end;
144+
145+
procedure SetBankSWIFT(Value: Text)
146+
begin
147+
Put('CompanyBankSWIFT', Value);
148+
end;
149+
150+
procedure SetBankSWIFTCaption(Value: Text)
151+
begin
152+
Put('CompanyBankSWIFTCaption', Value);
153+
end;
154+
155+
procedure SetGiroNo(Value: Text)
156+
begin
157+
Put('CompanyGiroNo', Value);
158+
end;
159+
160+
procedure SetGiroNoCaption(Value: Text)
161+
begin
162+
Put('CompanyGiroNoCaption', Value);
163+
end;
164+
165+
/// <summary>
166+
/// Merges the built payload (including the address-line repeater) into the JsonObject the
167+
/// platform passed to the subscriber. Modifies the passed object in place.
168+
/// </summary>
169+
procedure WriteTo(var CompanyMetadata: JsonObject)
170+
var
171+
JToken: JsonToken;
172+
KeyText: Text;
173+
begin
174+
// Idempotent: safe even if WriteTo is called more than once on the same builder instance.
175+
if Payload.Contains('CompanyAddressLines') then
176+
Payload.Replace('CompanyAddressLines', AddressLines)
177+
else
178+
Payload.Add('CompanyAddressLines', AddressLines);
179+
foreach KeyText in Payload.Keys() do begin
180+
Payload.Get(KeyText, JToken);
181+
if CompanyMetadata.Contains(KeyText) then
182+
CompanyMetadata.Replace(KeyText, JToken)
183+
else
184+
CompanyMetadata.Add(KeyText, JToken);
185+
end;
186+
end;
187+
188+
local procedure Put(KeyText: Text; Value: Text)
189+
begin
190+
if Payload.Contains(KeyText) then
191+
Payload.Replace(KeyText, Value)
192+
else
193+
Payload.Add(KeyText, Value);
194+
end;
195+
}

src/Layers/W1/BaseApp/Foundation/Reporting/ReportManagement.Codeunit.al

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
// ------------------------------------------------------------------------------------------------
55
namespace Microsoft.Foundation.Reporting;
66

7+
using Microsoft.Foundation.Address;
8+
using Microsoft.Foundation.Company;
79
using System.Device;
810
using System.Environment;
911
using System.Environment.Configuration;
1012
using System.Reflection;
13+
using System.Text;
1114
using System.Utilities;
1215

1316
codeunit 44 ReportManagement
@@ -264,6 +267,94 @@ codeunit 44 ReportManagement
264267
OnGetFilename(ReportID, Caption, ObjectPayload, FileExtension, ReportRecordRef, Filename, Success);
265268
end;
266269

270+
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Reporting Triggers", 'GetCompanyMetadata', '', false, false)]
271+
local procedure GetCompanyMetadataSubscriber(ReportId: Integer; var CompanyMetadata: JsonObject)
272+
begin
273+
GetCompanyMetadata(CompanyMetadata);
274+
end;
275+
276+
/// <summary>
277+
/// Populates the shared CompanyMetadata payload from Company Information for the report layouts'
278+
/// company block. Empty-safe: with no Company Information record the fields are emitted blank
279+
/// rather than erroring. Public so it can be invoked/verified directly; extension and
280+
/// localization fields are added by subscribing to the platform GetCompanyMetadata event
281+
/// directly (the payload is additive), so no BaseApp OnAfter event is exposed here.
282+
/// </summary>
283+
procedure GetCompanyMetadata(var CompanyMetadata: JsonObject)
284+
var
285+
CompanyInfo: Record "Company Information";
286+
FormatAddress: Codeunit "Format Address";
287+
CompanyMetadataBuilder: Codeunit "Company Metadata Builder";
288+
AddrArray: array[8] of Text[100];
289+
Index: Integer;
290+
begin
291+
if not CompanyInfo.Get() then
292+
CompanyInfo.Init();
293+
294+
CompanyMetadataBuilder.SetName(CompanyInfo.Name);
295+
CompanyMetadataBuilder.SetDisplayName(GetCompanyDisplayName());
296+
297+
FormatAddress.Company(AddrArray, CompanyInfo);
298+
for Index := 1 to ArrayLen(AddrArray) do
299+
CompanyMetadataBuilder.AddAddressLine(AddrArray[Index]);
300+
301+
CompanyMetadataBuilder.SetPhone(CompanyInfo."Phone No.");
302+
CompanyMetadataBuilder.SetPhoneCaption(CompanyInfo.FieldCaption("Phone No."));
303+
CompanyMetadataBuilder.SetFaxNo(CompanyInfo."Fax No.");
304+
CompanyMetadataBuilder.SetFaxNoCaption(CompanyInfo.FieldCaption("Fax No."));
305+
CompanyMetadataBuilder.SetEmail(CompanyInfo."E-Mail");
306+
CompanyMetadataBuilder.SetEmailCaption(CompanyInfo.FieldCaption("E-Mail"));
307+
CompanyMetadataBuilder.SetHomePage(CompanyInfo."Home Page");
308+
CompanyMetadataBuilder.SetHomePageCaption(CompanyInfo.FieldCaption("Home Page"));
309+
CompanyMetadataBuilder.SetLogo(GetLogoBase64(CompanyInfo));
310+
CompanyMetadataBuilder.SetVATRegistrationNo(CompanyInfo."VAT Registration No.");
311+
CompanyMetadataBuilder.SetVATRegistrationNoCaption(CompanyInfo.FieldCaption("VAT Registration No."));
312+
CompanyMetadataBuilder.SetRegistrationNo(CompanyInfo."Registration No.");
313+
CompanyMetadataBuilder.SetRegistrationNoCaption(CompanyInfo.FieldCaption("Registration No."));
314+
CompanyMetadataBuilder.SetBankName(CompanyInfo."Bank Name");
315+
CompanyMetadataBuilder.SetBankNameCaption(CompanyInfo.FieldCaption("Bank Name"));
316+
CompanyMetadataBuilder.SetBankAccountNo(CompanyInfo."Bank Account No.");
317+
CompanyMetadataBuilder.SetBankAccountNoCaption(CompanyInfo.FieldCaption("Bank Account No."));
318+
CompanyMetadataBuilder.SetBankBranchNo(CompanyInfo."Bank Branch No.");
319+
CompanyMetadataBuilder.SetBankBranchNoCaption(CompanyInfo.FieldCaption("Bank Branch No."));
320+
CompanyMetadataBuilder.SetIBAN(CompanyInfo.IBAN);
321+
CompanyMetadataBuilder.SetIBANCaption(CompanyInfo.FieldCaption(IBAN));
322+
CompanyMetadataBuilder.SetBankSWIFT(CompanyInfo."SWIFT Code");
323+
CompanyMetadataBuilder.SetBankSWIFTCaption(CompanyInfo.FieldCaption("SWIFT Code"));
324+
CompanyMetadataBuilder.SetGiroNo(CompanyInfo."Giro No.");
325+
CompanyMetadataBuilder.SetGiroNoCaption(CompanyInfo.FieldCaption("Giro No."));
326+
327+
CompanyMetadataBuilder.WriteTo(CompanyMetadata);
328+
end;
329+
330+
/// <summary>
331+
/// Company display name, mirroring how the platform builds ReportRequest/CompanyDisplayName
332+
/// (ReportRequestXmlRuntime): the tenant Company record's display name
333+
/// (CompanyProperty.DisplayName() -> session.Company.CompanyDisplayName), falling back to the
334+
/// company name when the display name is blank. NOT Company Information."Name 2".
335+
/// </summary>
336+
local procedure GetCompanyDisplayName(): Text
337+
var
338+
DisplayName: Text;
339+
begin
340+
DisplayName := CompanyProperty.DisplayName();
341+
if DisplayName = '' then
342+
DisplayName := CompanyName();
343+
exit(DisplayName);
344+
end;
345+
346+
local procedure GetLogoBase64(var CompanyInfo: Record "Company Information"): Text
347+
var
348+
Base64Convert: Codeunit "Base64 Convert";
349+
InStr: InStream;
350+
begin
351+
CompanyInfo.CalcFields(Picture);
352+
if not CompanyInfo.Picture.HasValue() then
353+
exit('');
354+
CompanyInfo.Picture.CreateInStream(InStr);
355+
exit(Base64Convert.ToBase64(InStr));
356+
end;
357+
267358
[IntegrationEvent(false, false)]
268359
procedure OnSelectReportLayout(var ReportLayoutList: Record "Report Layout List"; var Handled: Boolean)
269360
begin

0 commit comments

Comments
 (0)