Have you ever tried to get the attachments of an E-Mail which have been sent inside of Business Central?
The Main Question is: Do you want to store all Sales Quotes which have been sent to your customers/partners? Your employees are sending many mails with different versions of Quotes and a centralized storage would be nice?
Our goal is to download or even upload the body of our mail and all attachments to Sharepoint Online by using our Beyond Cloud Connector (which can be extended by any developer).
Let's step back and understand the implementation:
I am using Business Central v21 and Codeunit "Mail Management" is full of Mailing procedures to make the "Big Thing" run.
For our scenario there is an Event, which is very helpful:
OnSendViaEmailModuleOnAfterEmailSend(Message, TempEmailItem, MailSent, Cancelled, HideEmailSendingError);
Message is declared as Codeunit "Email Message" and holds the Body of your Mail and all Attachments.
GetBody() returns a String, so we need to use an InStream to download or upload it later:
if Message.GetBody() <> '' then begin Attachment.CreateOutStream(OutStr, TextEncoding::UTF8); OutStr.Write(Message.GetBody()); Attachment.CreateInStream(BodyInStream, TextEncoding::UTF8); UploadToBeyondCloudConnectorOrDownload(BodyInStream, 'Mail.html', CloudStorageSalesHeader, SalesHeader); end;
Let's iterate also through all Attachments:
if Message.Attachments_First() then repeat Message.Attachments_GetContent(AttachmentStream); UploadToBeyondCloudConnectorOrDownload(AttachmentStream, Message.Attachments_GetName(), CloudStorageSalesHeader, SalesHeader); until Message.Attachments_Next() = 0;
TempEmailItem is declared as Record "Email Item"and is aware of some background information. Like the the Source Tables, System IDs and Documents:
TempEmailItem.GetSourceDocuments(SourceTableIDs, SourceIDs, SourceRelationTypes);
We need to check if this Mail has been sent from our Sales Quote, right?
By returning a List, we need to get the first entry:
if SourceTableIDs.Get(1) <> Database::"Sales Header" then exit; if SourceRelationTypes.Get(1) <> Enum::"Sales Document Type"::Quote.AsInteger() then exit;
Also if we can still get that Sales Header:
if not SalesHeader.GetBySystemId(SourceIDs.Get(1)) then exit;
And then let's download or upload our InStream:
local procedure UploadToBeyondCloudConnectorOrDownload
The User should answer that question by using a StrMenu:
local procedure UploadToBeyondCloudConnectorOrDownload(var AttachmentInStream: InStream; Filename: Text; CloudStorageSalesHeader: Record "BYD Cloud Storage"; SalesHeader: Record "Sales Header")
case selected of 1: OK := DownloadFromStream(AttachmentInStream, 'Download', '', '', NewFileName); 2: begin Clear(CloudStorageMgt); CloudStorageMgt.PerformChunkedFileUpload(AttachmentInStream, CloudStorageSalesHeader, SalesHeader.RecordId(), SalesHeader.TableCaption(), CloudStorageMgt.GetPrimaryKeyText(SalesHeader.RecordId()), NewFileName, FileManagement.GetFileNameMimeType(NewFileName)); end; end;
And let's add a timestamp to all attachments which will work as a "Version":
FileManagement: Codeunit "File Management"; TypeHelper: Codeunit "Type Helper";
TodayFormatted := TypeHelper.GetFormattedCurrentDateTimeInUserTimeZone(DateTimeFormatLbl); NewFileName := StrSubstNo(FileNameLbl, FileManagement.GetFileNameWithoutExtension(FileName), TodayFormatted, FileManagement.GetExtension(FileName));
Let's give it a try and see how it works:
Happy coding :)!