Codeunit 1509 Notification Entry Dispatcher
- App
- Base Application
- Namespace
- System.Environment.Configuration
- Versions
- 17-28
Versions171819202122232425262728
Source29
Source in 29
src/Layers/W1/BaseApp/System/Notifications/NotificationEntryDispatcher.Codeunit.al489 lines, Copyright (c) Microsoft Corporation. MIT
namespace System.Environment.Configuration;
using Microsoft.Foundation.Reporting;
using Microsoft.Utilities;
using System.Automation;
using System.Email;
using System.Reflection;
using System.Security.User;
using System.Threading;
using System.Utilities;
codeunit 1509 "Notification Entry Dispatcher"
{
Permissions = TableData "User Setup" = r,
TableData "Notification Entry" = rimd,
TableData "Sent Notification Entry" = rimd,
TableData "Email Item" = rimd;
TableNo = "Job Queue Entry";
trigger OnRun()
begin
if Rec."Parameter String" = '' then
DispatchInstantNotifications()
else
DispatchNotificationTypeForUser(Rec."Parameter String");
end;
var
NotificationManagement: Codeunit "Notification Management";
HtmlBodyTempBlob: Codeunit "Temp Blob";
NotificationMailSubjectTxt: Label 'Notification overview';
NoEmailAccountsErr: Label 'Cannot send the email. No email accounts have been added.';
EmailBodyFailedToGenerateErr: Label 'Notification (%1)''s email body failed to generate due to: %2', Comment = '%1 = Notification Entry ID, %2 = Error message';
EmailFailedToSendErr: Label 'Notification (%1)''s email failed to send due to: %2', Comment = '%1 = Notification Entry ID, %2 = Error message';
NoteFailedToAddErr: Label 'Notification (%1)''s note failed to add due to: %2', Comment = '%1 = Notification Entry ID, %2 = Error message';
local procedure DispatchInstantNotifications()
var
UserSetup: Record "User Setup";
TempNotificationEntryFromTo: Record "Notification Entry" temporary;
ErrorMessageHandler: Codeunit "Error Message Handler";
ErrorContextElement: Codeunit "Error Context Element";
ErrorMessageMgt: Codeunit "Error Message Management";
UserIdWithError: Code[50];
begin
GetTempNotificationEntryFromTo(TempNotificationEntryFromTo);
TempNotificationEntryFromTo.Reset();
if TempNotificationEntryFromTo.FindSet() then begin
ErrorMessageMgt.Activate(ErrorMessageHandler);
ErrorMessageMgt.PushContext(ErrorContextElement, TempNotificationEntryFromTo, 0, '');
repeat
if not UserSetup.Get(TempNotificationEntryFromTo."Recipient User ID") then
UserIdWithError := TempNotificationEntryFromTo."Recipient User ID"
else
if ScheduledInstantly(UserSetup."User ID", TempNotificationEntryFromTo.Type) then
DispatchForNotificationType(TempNotificationEntryFromTo.Type, UserSetup, TempNotificationEntryFromTo."Sender User ID")
until TempNotificationEntryFromTo.Next() = 0;
Commit();
ErrorMessageMgt.Finish(ErrorMessageHandler);
end;
if UserIdWithError <> '' then
UserSetup.Get(UserIdWithError);
end;
local procedure DispatchNotificationTypeForUser(Parameter: Text)
var
UserSetup: Record "User Setup";
NotificationEntry: Record "Notification Entry";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeDispatchNotificationTypeForUser(Parameter, IsHandled);
if IsHandled then
exit;
NotificationEntry.SetView(Parameter);
UserSetup.Get(NotificationEntry.GetRangeMax("Recipient User ID"));
DispatchForNotificationType(NotificationEntry.GetRangeMax(Type), UserSetup, CopyStr(UserId(), 1, 50));
end;
local procedure DispatchForNotificationType(NotificationType: Enum "Notification Entry Type"; UserSetup: Record "User Setup"; SenderUserID: Code[50])
var
NotificationEntry: Record "Notification Entry";
NotificationSetup: Record "Notification Setup";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeDispatchForNotificationType(NotificationType, UserSetup, SenderUserID, IsHandled);
if not IsHandled then begin
NotificationEntry.SetRange("Recipient User ID", UserSetup."User ID");
NotificationEntry.SetRange(Type, NotificationType);
if SenderUserID <> '' then
NotificationEntry.SetRange("Sender User ID", SenderUserID);
DeleteOutdatedApprovalNotificationEntires(NotificationEntry);
if not NotificationEntry.FindFirst() then
exit;
FilterToActiveNotificationEntries(NotificationEntry);
NotificationSetup.GetNotificationTypeSetupForUser(NotificationType, NotificationEntry."Recipient User ID");
CreateAndDispatch(NotificationSetup, NotificationEntry, UserSetup);
end;
OnAfterDispatchForNotificationType(NotificationSetup, NotificationEntry);
end;
local procedure CreateAndDispatch(NotificationSetup: Record "Notification Setup"; var NotificationEntry: Record "Notification Entry"; UserSetup: Record "User Setup")
begin
OnBeforeCreateAndDispatch(NotificationSetup, NotificationEntry);
case NotificationSetup."Notification Method" of
NotificationSetup."Notification Method"::Email:
CreateMailAndDispatch(NotificationEntry, UserSetup."E-Mail");
NotificationSetup."Notification Method"::Note:
CreateNoteAndDispatch(NotificationEntry);
end;
end;
local procedure ScheduledInstantly(RecipientUserID: Code[50]; NotificationType: Enum "Notification Entry Type"): Boolean
var
NotificationSchedule: Record "Notification Schedule";
begin
if not NotificationSchedule.Get(RecipientUserID, NotificationType) then
exit(true); // No rules are set up, send immediately
exit(NotificationSchedule.Recurrence = NotificationSchedule.Recurrence::Instantly);
end;
local procedure CreateMailAndDispatch(var NotificationEntry: Record "Notification Entry"; Email: Text)
var
NotificationSetup: Record "Notification Setup";
MailManagement: Codeunit "Mail Management";
DocumentMailing: Codeunit "Document-Mailing";
ErrorMessageMgt: Codeunit "Error Message Management";
TempBlob: Codeunit "Temp Blob";
SourceReference: RecordRef;
DocumentRecRef: RecordRef;
BodyText: Text;
MailSubject: Text;
ErrorText: Text;
IsEmailedSuccessfully: Boolean;
MaximumRelatedEntriesReached: Boolean;
IsHandled: Boolean;
AttachmentStream: InStream;
SourceTables, SourceRelationTypes : List of [Integer];
SourceIDs: List of [Guid];
begin
if not GetHTMLBodyText(NotificationEntry, BodyText) then
exit;
MailSubject := NotificationMailSubjectTxt;
IsHandled := false;
OnBeforeCreateMailAndDispatch(NotificationEntry, MailSubject, Email, IsHandled);
if IsHandled then
exit;
TempBlob.CreateInStream(AttachmentStream);
if NotificationEntry.FindSet() then
repeat
// Add "Primary Source" Related Record (Notification Entry)
SourceReference.GetTable(NotificationEntry);
SourceReference.GetBySystemId(NotificationEntry.SystemId);
SourceTables.Add(SourceReference.Number());
SourceIDs.Add(SourceReference.Field(SourceReference.SystemIdNo()).Value());
SourceRelationTypes.Add(Enum::"Email Relation Type"::"Primary Source".AsInteger());
// Add "Related Entity" Related Record (Document)
GetTargetRecRef(NotificationEntry, DocumentRecRef);
SourceTables.Add(DocumentRecRef.Number());
SourceIDs.Add(DocumentRecRef.Field(DocumentRecRef.SystemIdNo()).Value());
SourceRelationTypes.Add(Enum::"Email Relation Type"::"Related Entity".AsInteger());
// Limit how many related entities will be added to avoid edge cases with large number of notification entries.
MaximumRelatedEntriesReached := SourceIDs.Count() >= MaximumRelatedEntries();
until MaximumRelatedEntriesReached or (NotificationEntry.Next() = 0);
IsEmailedSuccessfully := DocumentMailing.EmailFile(
AttachmentStream, '', HtmlBodyTempBlob, MailSubject, Email, true, Enum::"Email Scenario"::"Notification", SourceTables, SourceIDs, SourceRelationTypes);
if IsEmailedSuccessfully then
NotificationManagement.MoveNotificationEntryToSentNotificationEntries(
NotificationEntry, BodyText, true, NotificationSetup."Notification Method"::Email.AsInteger())
else begin
IsHandled := false;
OnCreateMailAndDispatchOnBeforeLogError(NotificationEntry, Email, BodyText, IsHandled);
if not IsHandled then begin
ErrorText := GetLastErrorText();
if ErrorText = '' then
if not MailManagement.IsEnabled() then
ErrorText := NoEmailAccountsErr;
NotificationEntry."Error Message" := CopyStr(ErrorText, 1, MaxStrLen(NotificationEntry."Error Message"));
NotificationEntry.Modify(true);
ErrorMessageMgt.LogError(NotificationEntry, StrSubstNo(EmailFailedToSendErr, NotificationEntry.ID, ErrorText), '');
end;
end;
OnAfterCreateMailAndDispatch(NotificationEntry, Email, IsEmailedSuccessfully);
end;
local procedure CreateNoteAndDispatch(var NotificationEntry: Record "Notification Entry")
var
NotificationSetup: Record "Notification Setup";
BodyText: Text;
begin
repeat
if AddNote(NotificationEntry, BodyText) then
NotificationManagement.MoveNotificationEntryToSentNotificationEntries(
NotificationEntry, BodyText, false, NotificationSetup."Notification Method"::Note.AsInteger());
until NotificationEntry.Next() = 0;
end;
local procedure AddNote(var NotificationEntry: Record "Notification Entry"; var Body: Text): Boolean
var
RecordLink: Record "Record Link";
PageManagement: Codeunit "Page Management";
RecordLinkManagement: Codeunit "Record Link Management";
ErrorMessageMgt: Codeunit "Error Message Management";
RecRefLink: RecordRef;
Link: Text;
begin
RecordLink.Init();
RecordLink."Link ID" := 0;
GetTargetRecRef(NotificationEntry, RecRefLink);
if not RecRefLink.HasFilter then
RecRefLink.SetRecFilter();
RecordLink."Record ID" := RecRefLink.RecordId;
Link := GetUrl(DefaultClientType, CompanyName, OBJECTTYPE::Page, PageManagement.GetPageID(RecRefLink), RecRefLink, true);
OnAddNoteOnAfterGetUrl(Link, NotificationEntry, RecRefLink);
RecordLink.URL1 := CopyStr(Link, 1, MaxStrLen(RecordLink.URL1));
RecordLink.Description := CopyStr(Format(NotificationEntry."Triggered By Record"), 1, 250);
RecordLink.Type := RecordLink.Type::Note;
CreateNoteBody(NotificationEntry, Body);
RecordLinkManagement.WriteNote(RecordLink, Body);
RecordLink.Created := CurrentDateTime;
RecordLink."User ID" := NotificationEntry."Created By";
RecordLink.Company := CopyStr(CompanyName, 1, MaxStrLen(RecordLink.Company));
RecordLink.Notify := true;
RecordLink."To User ID" := NotificationEntry."Recipient User ID";
if not RecordLink.Insert(true) then begin
ErrorMessageMgt.LogError(NotificationEntry, StrSubstNo(NoteFailedToAddErr, NotificationEntry.ID, GetLastErrorText()), '');
exit(false);
end;
exit(true);
end;
local procedure FilterToActiveNotificationEntries(var NotificationEntry: Record "Notification Entry")
begin
repeat
NotificationEntry.Mark(true);
until NotificationEntry.Next() = 0;
NotificationEntry.MarkedOnly(true);
NotificationEntry.FindSet();
end;
local procedure ConvertHtmlFileToText(var TempBlob: Codeunit "Temp Blob"; var BodyText: Text)
var
BlobInStream: InStream;
begin
TempBlob.CreateInStream(BlobInStream);
BlobInStream.ReadText(BodyText);
end;
local procedure CreateNoteBody(var NotificationEntry: Record "Notification Entry"; var Body: Text)
var
RecRef: RecordRef;
DocumentType: Text;
DocumentNo: Text;
DocumentName: Text;
ActionText: Text;
begin
GetTargetRecRef(NotificationEntry, RecRef);
NotificationManagement.GetDocumentTypeAndNumber(RecRef, DocumentType, DocumentNo);
DocumentName := DocumentType + ' ' + DocumentNo;
ActionText := NotificationManagement.GetActionTextFor(NotificationEntry);
OnCreateNoteBodyOnAfterGetActionTextFor(NotificationEntry, ActionText);
Body := DocumentName + ' ' + ActionText;
end;
[Scope('OnPrem')]
procedure GetHTMLBodyText(var NotificationEntry: Record "Notification Entry"; var BodyTextOut: Text) Result: Boolean
var
ReportLayoutSelection: Record "Report Layout Selection";
ErrorMessageMgt: Codeunit "Error Message Management";
RecRef: RecordRef;
HtmlBodyOutStream: OutStream;
IsHandled: Boolean;
TempLayoutCode: Code[20];
begin
IsHandled := false;
OnBeforeGetHTMLBodyText(NotificationEntry, BodyTextOut, Result, IsHandled);
if IsHandled then
exit(Result);
TempLayoutCode := '';
Clear(HtmlBodyTempBlob);
OnGetHTMLBodyTextOnAfterSetTempLayoutCode(NotificationEntry, BodyTextOut, TempLayoutCode);
ReportLayoutSelection.SetTempLayoutSelected(TempLayoutCode);
RecRef.GetTable(NotificationEntry);
HtmlBodyTempBlob.CreateOutStream(HtmlBodyOutStream, TextEncoding::UTF8);
if not Report.SaveAs(Report::"Notification Email", '', ReportFormat::Html, HtmlBodyOutStream, RecRef) then begin
NotificationEntry."Error Message" := CopyStr(GetLastErrorText(), 1, MaxStrLen(NotificationEntry."Error Message"));
NotificationEntry.Modify(true);
ErrorMessageMgt.LogError(NotificationEntry, StrSubstNo(EmailBodyFailedToGenerateErr, NotificationEntry.ID, GetLastErrorText()), '');
ClearLastError();
exit(false);
end;
ConvertHtmlFileToText(HtmlBodyTempBlob, BodyTextOut);
exit(true);
end;
procedure GetTargetRecRef(var NotificationEntry: Record "Notification Entry"; var TargetRecRefOut: RecordRef)
var
ApprovalEntry: Record "Approval Entry";
OverdueApprovalEntry: Record "Overdue Approval Entry";
DataTypeManagement: Codeunit "Data Type Management";
RecRef: RecordRef;
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeGetTargetRecRef(NotificationEntry, TargetRecRefOut, IsHandled);
if IsHandled then
exit;
DataTypeManagement.GetRecordRef(NotificationEntry."Triggered By Record", RecRef);
case NotificationEntry.Type of
NotificationEntry.Type::"New Record":
TargetRecRefOut := RecRef;
NotificationEntry.Type::Approval:
begin
RecRef.SetTable(ApprovalEntry);
TargetRecRefOut.Get(ApprovalEntry."Record ID to Approve");
end;
NotificationEntry.Type::Overdue:
begin
RecRef.SetTable(OverdueApprovalEntry);
TargetRecRefOut.Get(OverdueApprovalEntry."Record ID to Approve");
end;
end;
end;
local procedure DeleteOutdatedApprovalNotificationEntires(var NotificationEntry: Record "Notification Entry")
begin
if NotificationEntry.FindFirst() then
repeat
if ApprovalNotificationEntryIsOutdated(NotificationEntry) then
NotificationEntry.Delete();
until NotificationEntry.Next() = 0;
end;
local procedure ApprovalNotificationEntryIsOutdated(var NotificationEntry: Record "Notification Entry"): Boolean
var
ApprovalEntry: Record "Approval Entry";
OverdueApprovalEntry: Record "Overdue Approval Entry";
DataTypeManagement: Codeunit "Data Type Management";
RecRef: RecordRef;
IsOutdated, IsHandled : Boolean;
begin
IsOutdated := false;
IsHandled := false;
OnBeforeApprovalNotificationEntryIsOutdated(NotificationEntry, IsOutdated, IsHandled);
if IsHandled then
exit(IsOutdated);
if not DataTypeManagement.GetRecordRef(NotificationEntry."Triggered By Record", RecRef) then
exit(true); // if no approval entry in RecRef, mark entry as outdated
case NotificationEntry.Type of
NotificationEntry.Type::Approval:
begin
RecRef.SetTable(ApprovalEntry);
if not RecRef.Get(ApprovalEntry."Record ID to Approve") then
exit(true);
end;
NotificationEntry.Type::Overdue:
begin
RecRef.SetTable(OverdueApprovalEntry);
if not RecRef.Get(OverdueApprovalEntry."Record ID to Approve") then
exit(true);
end;
end;
end;
local procedure GetTempNotificationEntryFromTo(var TempNotificationEntryFromTo: Record "Notification Entry" temporary)
var
NotificationEntry: Record "Notification Entry";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeGetTempNotificationEntryFromTo(NotificationEntry, TempNotificationEntryFromTo, IsHandled);
if IsHandled then
exit;
NotificationEntry.SetRange("Sender User ID", UserId());
if NotificationEntry.FindSet() then
repeat
TempNotificationEntryFromTo.SetRange("Sender User ID", NotificationEntry."Sender User ID");
TempNotificationEntryFromTo.SetRange("Recipient User ID", NotificationEntry."Recipient User ID");
if TempNotificationEntryFromTo.IsEmpty() then begin
TempNotificationEntryFromTo := NotificationEntry;
TempNotificationEntryFromTo.Insert();
end;
until NotificationEntry.Next() = 0;
end;
local procedure MaximumRelatedEntries(): Integer
begin
exit(100);
end;
[IntegrationEvent(false, false)]
local procedure OnAfterCreateMailAndDispatch(var NotificationEntry: Record "Notification Entry"; Email: Text; IsEmailedSuccessfully: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterDispatchForNotificationType(NotificationSetup: Record "Notification Setup"; var NotificationEntry: Record "Notification Entry")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAddNoteOnAfterGetUrl(var Link: Text; NotificationEntry: Record "Notification Entry"; RecRefLink: RecordRef)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeCreateMailAndDispatch(var NotificationEntry: Record "Notification Entry"; var MailSubject: Text; var Email: Text; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeCreateAndDispatch(NotificationSetup: Record "Notification Setup"; var NotificationEntry: Record "Notification Entry")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeGetTargetRecRef(var NotificationEntry: Record "Notification Entry"; var TargetRecRefOut: RecordRef; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeGetHTMLBodyText(var NotificationEntry: Record "Notification Entry"; var BodyTextOut: Text; var Result: Boolean; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnCreateNoteBodyOnAfterGetActionTextFor(var NotificationEntry: Record "Notification Entry"; var ActionText: Text)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeDispatchNotificationTypeForUser(Parameter: Text; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeGetTempNotificationEntryFromTo(var NotificationEntry: Record "Notification Entry"; var TempNotificationEntryFromTo: Record "Notification Entry" temporary; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnGetHTMLBodyTextOnAfterSetTempLayoutCode(var NotificationEntry: Record "Notification Entry"; var BodyTextOut: Text; var TempLayoutCode: Code[20])
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeApprovalNotificationEntryIsOutdated(var NotificationEntry: Record "Notification Entry"; var IsOutdated: Boolean; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnCreateMailAndDispatchOnBeforeLogError(var NotificationEntry: Record "Notification Entry"; Email: Text; BodyText: Text; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeDispatchForNotificationType(NotificationType: Enum "Notification Entry Type"; UserSetup: Record "User Setup"; SenderUserID: Code[50]; var IsHandled: Boolean)
begin
end;
}