Forecloud Technologies Pvt. Ltd. - Business Central Appointment Scheduling Automation

In this blog post, we’ll break down how you can automate customer appointment scheduling in Microsoft Dynamics 365 Business Central by generating and sending emails with ICS (iCalendar) file attachme

 · 5 min read

Introduction:

In this blog post, we’ll break down how you can automate customer appointment scheduling in Microsoft Dynamics 365 Business Central by generating and sending emails with ICS (iCalendar) file attachment


Pre-requisites:

Email Setup is required in business central

Email Account

Business Central Environment


Solution:

Key Features of the Solution

1. Table Extension to Add Appointment Fields

We will extend the standard Customer table to include two new fields: AppointmentDate and StartTime. These fields will store the appointment date and start time for each customer.

Code:

-------------------------------------------------------------------------------------------------------

tableextension 50100 CustomerExt extends Customer

{

  fields

  {

    field(50101; AppointmentDate; Date)

    {

      Caption = 'Appointment Date';

    }

    field(50102; StartTime; Time)

    {

      Caption = 'Start Time';

      InitValue = 090000T;

    }

  }

}


Explanation-

  1. AppointmentDate: A Date field to store the date of the appointment.
  2. StartTime: A Time field to store the start time of the appointment. The initial value is set to 9:00 AM (090000T).


2. Page Extension to Display Appointment Fields

Next, we will extend the Customer Card page to display the Appointment Date and Start Time fields. This allows users to see and edit appointment details directly on the customer card.

Code:


pageextension 50100 CustomerCardExt extends "Customer Card"

{

  layout

  {

    addafter(General)

    {

      group(Appointment)

      {

        field(AppointmentDate; Rec.AppointmentDate)

        {

          Caption = 'Appointment Date';

          ApplicationArea = All;

        }

        field(StartTime; Rec.StartTime)

        {

          Caption = 'Start Time';

          ApplicationArea = All;

        }

      }

    }

  }

  actions

  {

    addafter("F&unctions")

    {

      action("Schedule Appointment")

      {

        Image = "1099Form";

        ApplicationArea = All;

        Caption = 'Schedule Appointment';

        trigger OnAction()

        var

          CuCustomerAppointments: Codeunit "Customer Appointments";

          AppointmentDateTime: DateTime;

        begin

          Clear(AppointmentDateTime);

          AppointmentDateTime := CreateDateTime(Rec.AppointmentDate, Rec.StartTime);


          if ((AppointmentDateTime >= CurrentDateTime)) then begin

            CuCustomerAppointments.CreateAndSend(Rec);

          end;


          if (AppointmentDateTime < CurrentDateTime) then begin

            Error('Appointments should be set for the upcoming date and time.');

          end;

        end;

      }

    }

  }

}


Explanation-

  1. The Appointment group is added after the General group in the customer card layout. This group contains two fields: Appointment Date and Start Time.
  2. The "Schedule Appointment" action is added under the Functions group. When the user clicks on this action, the CreateAndSend procedure from the Customer Appointments codeunit is called.
  3. The OnAction trigger checks if the scheduled appointment time is in the future. If the appointment time is valid, the system sends the appointment email. If the appointment time is in the past, an error message is shown, preventing the user from scheduling past appointments.



3. Codeunit for Appointment Email Creation and Sending


3.1 Automated Email Creation and Sending

  1. Procedure: CreateAndSend(RecCustomer: Record "Customer")
  2. This procedure is responsible for sending an appointment email to the customer.
  3. It checks if the customer’s email address is available, then creates the email with the appropriate ICS file attachment.
  4. The email is sent using the Send method with the default email scenario.

Code:


  var

    ProdIDTxt: Label '//Microsoft Corporation//Outlook 16.0 MIMEDIR//EN';

    DateTimeFormatTxt: Label '<Year4><Month,2><Day,2>T<Hours24,2><Minutes,2><Seconds,2>', Locked = true;


  procedure CreateAndSend(RecCustomer: Record "Customer")

  var

    TempEmailItem: Record "Email Item" temporary;

  begin

    if CreateRequest(TempEmailItem, RecCustomer) then begin

       TempEmailItem.Send(true, Enum::"Email Scenario"::Default);

    end;

  end;


Explanation-

  1. The procedure first verifies the customer’s email address and if present, proceeds to create the request.
  2. The email is sent once the ICS attachment is successfully generated


3.2. Email Request Creation

  1. Procedure: CreateRequest(var TempEmailItem: Record "Email Item" temporary; RecCustomer: Record "Customer"): Boolean
  2. This function verifies whether the customer’s email is present. If the email is available, it generates the email request.
  3. If the customer has no email, it raises an error message.

Code:


  procedure CreateRequest(var TempEmailItem: Record "Email Item" temporary; RecCustomer: Record "Customer"): Boolean

  var

    Email: Text;

  begin

    Email := RecCustomer."E-Mail";


    if Email <> '' then begin

       GenerateEmail(TempEmailItem, Email, RecCustomer);

      exit(true);

    end

    else begin

       Error('Please provide email to schedule appointment');

    end;

  end;


3.3. ICS File Generation (Calendar Invitation)

  1. Procedure: GenerateICS(RecCustomer: Record "Customer") ICS: Text
  2. This is the core procedure that generates the actual .ics calendar invitation file.
  3. The ICS format is a standard used by calendar applications (like Outlook and Google Calendar) to represent events.
  4. The procedure constructs the ICS file based on customer details like name, appointment date, location, and description.
  5. Key Data Included in ICS:
  6. Summary: The appointment subject, which includes customer details.
  7. Start/End Time: The start and end dates/times for the appointment, formatted in the correct way.
  8. Location: Customer’s address and city, integrated into the ICS file.
  9. Description: A personalized description of the appointment, which can include custom data.

Code:

local procedure GenerateICS(RecCustomer: Record "Customer") ICS: Text

   var

     TextBuilder: TextBuilder;

     Location: Text;

     Summary: Text;

     Status: Text;

     Method: Text;

     Description: Text;

     Organizer: Text;

     Filename: Text;

    AttachmentInfo: Dictionary of [Integer, Text];

     Id: Integer;

     Base64: Text;

   begin

     Summary := StrSubstNo('%1 : %2', RecCustomer."No.", RecCustomer.Name);

     Method := 'REQUEST';

     Status := 'BUSY';

     Organizer := GetOrganizer();

 

 Description := GetDescription(RecCustomer);


     TextBuilder.AppendLine('BEGIN:VCALENDAR');

     TextBuilder.AppendLine('PRODID:-' + ProdIDTxt);

     TextBuilder.AppendLine('VERSION:2.0');

     TextBuilder.AppendLine('METHOD:' + Method);

     TextBuilder.AppendLine('X-MS-OLK-FORCEINSPECTOROPEN:TRUE');

     TextBuilder.AppendLine('BEGIN:VEVENT');

     TextBuilder.AppendLine('CLASS:PUBLIC');

     TextBuilder.AppendLine('UID:' + DelChr(CreateGuid(), '<>', '{}'));

     TextBuilder.AppendLine('ORGANIZER:MAILTO:' + Organizer);

     TextBuilder.AppendLine('LOCATION:' + RecCustomer.Address + RecCustomer.City + RecCustomer."Country/Region Code");

     TextBuilder.AppendLine('DTSTART:' + GetStartDate(RecCustomer));

     TextBuilder.AppendLine('DTEND:' + GetEndDate(RecCustomer));

     TextBuilder.AppendLine('PRIORITY:5');

     TextBuilder.AppendLine('SEQUENCE:0');

     TextBuilder.AppendLine('SUMMARY:' + Summary);

     TextBuilder.AppendLine('TRANSP:OPAQUE');

     TextBuilder.AppendLine('DESCRIPTION:' + Description);

     TextBuilder.AppendLine('X-ALT-DESC;FMTTYPE=' + GetHtmlDescription(Description));

     TextBuilder.AppendLine('X-MICROSOFT-CDO-BUSYSTATUS:BUSY');

     TextBuilder.AppendLine('X-MICROSOFT-CDO-IMPORTANCE:1');

     TextBuilder.AppendLine('X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY');

     TextBuilder.AppendLine('X-MICROSOFT-DISALLOW-COUNTER:TRUE');

     TextBuilder.AppendLine('X-MS-OLK-CONFTYPE:0');

     TextBuilder.AppendLine('STATUS:' + Status);

     TextBuilder.AppendLine('BEGIN:VALARM');

     TextBuilder.AppendLine('TRIGGER:-PT15M');

     TextBuilder.AppendLine('ACTION:DISPLAY');

     TextBuilder.AppendLine('DESCRIPTION:Reminder');

     TextBuilder.AppendLine('END:VALARM');

     TextBuilder.AppendLine('END:VEVENT');

     TextBuilder.AppendLine('END:VCALENDAR');


     ICS := TextBuilder.ToText();

   end;


Explanation-

  1. TextBuilder is used to dynamically create the content of the ICS file. This allows for flexibility, including various fields such as the event’s summary, start/end times, and description.



3.4. ICS Attachment and Email Generation

  1. Procedure: GenerateEmail(var TempEmailItem: Record "Email Item" temporary; RecipientEmail: Text; RecCustomer: Record "Customer")
  2. This procedure generates the email, attaches the .ics file, and prepares it for sending.
  3. The ICS content is written to a temporary stream using the TempBlob codeunit.
  4. The email’s subject, body, and recipient are set dynamically based on the customer’s information.

Code:

-------------------------------------------------------------------------------------------------------

local procedure GenerateEmail(var TempEmailItem: Record "Email Item" temporary; RecipientEmail: Text; RecCustomer: Record "Customer")

   var

     TempBlob: Codeunit "Temp Blob";

     Stream: OutStream;

     InStream: Instream;

     ICS, Filename : Text;

     MaxSize: Integer;

   begin

     Clear(Filename);

     Clear(ICS);


     MaxSize := 10485760;

     ICS := GenerateICS(RecCustomer);

     if StrLen(ICS) > MaxSize then

       Error('The ICS file exceeds the maximum size limit of 10MB.')

     else begin

       TempBlob.CreateOutStream(Stream, TextEncoding::UTF8);

       Stream.Write(ICS);

       TempBlob.CreateInStream(InStream);

       TempEmailItem.Initialize();


       if RecCustomer.Name <> '' then begin

         Filename := RecCustomer.Name;

         TempEmailItem.Subject := StrSubstNo('Appointment - %1 %2', RecCustomer."No.", RecCustomer.Name);

       end

       else begin

         Filename := RecCustomer."No.";

         TempEmailItem.Subject := StrSubstNo('Appointment - %1', RecCustomer."No.");

       end;


       TempEmailItem.AddAttachment(InStream, StrSubstNo('%1.ics', Filename));

       TempEmailItem."Send to" := RecipientEmail;

     end;

  end;


Explanation-

  1. Attachment Handling: The .ics file is added as an attachment using the AddAttachment method.
  2. Subject: The subject is dynamically generated based on the customer's details.



3.5. Date and Time Handling

  1. Get Start and End Date/Time:
  2. Procedure: GetStartDate(RecCustomer: Record "Customer") StartDateTime: Text and GetEndDate(RecCustomer: Record "Customer") EndDateTime: Text
  3. These procedures convert the customer's AppointmentDate and StartTime into a formatted string for the ICS file.
  4. The end time is calculated based on a fixed duration of 8 hours (can be adjusted as needed).

Code (Start Date Example):


  procedure GetStartDate(RecCustomer: Record "Customer") StartDateTime: Text

   var

     StartDate: DateTime;

     StartTime: Time;

   begin

     StartDate := CreateDateTime(RecCustomer.AppointmentDate, RecCustomer.StartTime);

     Evaluate(StartTime, Format(8));

     StartDateTime := Format(StartDate, 0, DateTimeFormatTxt);

   end;


  procedure GetEndDate(RecCustomer: Record "Customer") EndDateTime: Text

   var

     EndDate: DateTime;

     StartTime: Time;

     Duration: Integer;

   begin

     EndDate := CreateDateTime(RecCustomer.AppointmentDate, RecCustomer.StartTime);

     Evaluate(StartTime, Format(8));

     Duration := 8 * 3600000;

     EndDateTime := Format((EndDate + Duration), 0, DateTimeFormatTxt);

   end;


3.7. Organizer Email Retrieval

  1. Procedure: GetOrganizer() Text
  2. This procedure retrieves the organizer’s email address for the appointment. It first checks the User record for a valid email and then falls back to a default email address if none is found.

Code-

  local procedure GetOrganizer(): Text

   var

     ProjectManagerUser: Record User;

     EmailAccount: Record "Email Account";

     EmailScenario: Codeunit "Email Scenario";

   begin

     ProjectManagerUser.SetRange("User Name", UserId());

     ProjectManagerUser.SetFilter("Authentication Email", '<>%1', '');

     if ProjectManagerUser.FindFirst() then

       exit(ProjectManagerUser."Authentication Email");


     EmailScenario.GetEmailAccount(Enum::"Email Scenario"::Default, EmailAccount);

     exit(EmailAccount."Email Address");

   end;


3.8. Generating HTML Description for ICS File

  1. Procedure: GetHtmlDescription(Description: Text) HtmlAppointDescription: Text
  2. This procedure is responsible for generating an HTML-formatted description for the ICS file.
  3. It uses a Regex codeunit to replace newline characters (\n) and carriage returns (\r) with appropriate HTML tags (<br>). This ensures the appointment description is properly formatted when viewed in calendar applications that support HTML content.
  4. The GetHtmlDescription procedure takes the plain-text description of the appointment and transforms it into an HTML-compatible format.
  5. It removes unnecessary carriage returns and converts

Code-

  local procedure GetHtmlDescription(Description: Text) HtmlAppointDescription: Text

   var

     Regex: Codeunit Regex;

   begin

     HtmlAppointDescription := Regex.Replace(Description, '\\r', '');

     HtmlAppointDescription := Regex.Replace(HtmlAppointDescription, '\\n', '<br>');

     HtmlAppointDescription := 'text/html:<html><body>' + HtmlAppointDescription + '</html></body>';

   end;


Functional Details


1. Open the Customer Card and enter the relevant details.


2. Navigate to the Actions menu and click on "Schedule Appointment."


3. Check the Outlook inbox for the appointment email.


4. Add the appointment to your calendar.


5. The appointment will now be scheduled in your calendar.



Conclusion

This AL code demonstrates how to automate customer appointment invitations in Business Central. By leveraging .ics files and email integration, businesses can streamline scheduling and improve communication with customers. If you're looking to enhance your system with similar functionality, this approach provides a solid foundation.

Feel free to adapt the code to meet your specific requirements!


R
RP

No comments yet.

Add a comment
Ctrl+Enter to add comment