How to the Outlook Appointment series master from a single occurence - outlook

I need to get the master appointment of the meeting series, when an appointment instance is opened.
I have tried the following (currentAppointment variable is of type AppointmentItem)
DateTime sd = currentAppointment.GetRecurrencePattern().PatternStartDate;
DateTime st = currentAppointment.GetRecurrencePattern().StartTime;
AppointmentItem ai = currentAppointment.GetRecurrencePattern().GetOccurrence(sd+st.TimeOfDay);
However, while this gets me the first appointment in the series, it has a RecurrenceState of olApptOccurrence.
How can I get a reference to the olApptMaster - ie the meeting series?

AppointmentItem.Parent will return the parent AppointmentItem for the recurrence instances and exceptions.

I have a method to create an appointment item with recurrence, but it´s almost the same as modifying one, tell me if that helps you and if you need further information.
Here is the code in C#
private void CreateNewRecurringAppointment(Outlook._Application OutlookApp)
{
Outlook.AppointmentItem appItem = null;
Outlook.RecurrencePattern pattern = null;
try
{
appItem = OutlookApp.CreateItem(Outlook.OlItemType.olAppointmentItem)
as Outlook.AppointmentItem;
// create a recurrence
pattern = appItem.GetRecurrencePattern();
pattern.RecurrenceType = Outlook.OlRecurrenceType.olRecursWeekly;
pattern.StartTime = DateTime.Parse("9:00:00 AM");
pattern.EndTime = DateTime.Parse("10:00:00 AM");
// we can specify the duration instead of using the EndTime property
// pattern.Duration = 60;
pattern.PatternStartDate = DateTime.Parse("11/11/2011");
pattern.PatternEndDate = DateTime.Parse("12/25/2011");
appItem.Subject = "Meeting with the Boss";
appItem.Save();
appItem.Display(true);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
if (pattern != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(pattern);
if (appItem != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(appItem);
}
}
source: http://www.add-in-express.com/creating-addins-blog/2011/11/07/outlook-recurring-appointment/

Related

CRM 2013 : How can I Schedule Concurrent Appointments (using Appointment & RecurringAppointmentMaster entities)?

We have a plugin that uses the BookRequest & RescheduleRequest Methods to schedule Appointment & RecurringAppointmentMaster entities.
Recently I was tasked with implementing the ability to schedule multiple appts in a given timeslot.
So in researching this, I found some posts referring to resource capacity (in work hours) & setting the Effort field of the ActivityParty to 1.0 in the Appointment.
I thought, great this will be easy.
So I changed the plugin to store the effort:
activityParty = new Entity("activityparty");
activityParty["partyid"] = new EntityReference("systemuser", apptCaregiverId);
activityParty["effort"] = (Double)1.0;
But when I ran the code, the BookRequest returned this error that confused me: ErrorCode.DifferentEffort
I searched for ErrorCode.DifferentEffort, 2139095040, BookRequest, you name it found nothing useful.
What exactly does this error mean?
Why is it so difficult to schedule concurrent appointments in CRM?
This is what I learned the hard way, so maybe it will spare someone else some frustration.
I looked in the database & noticed that the activityparty entities associated with appointments all had the effort field set to 2139095040 and I wondered where that number was coming from? In a post unrelated to CRM, I found out the 2139095040 means 'positive infinity'.
I revisited the posts where they talk about setting the effort to 1.0 & realized that they were all referring to the ServiceAppointment entity (not Appointment) and then I finally stumbled on the list of error codes
Scheduling Error Codes
DifferentEffort = The required capacity of this service does not match the capacity of resource {resource name}.
Not exactly the truth, but whatever.
The real issue is the Appointment entity does not reference a Service, therefore the capacity of the non-existent service is ‘Positive Infinity’ (2139095040).
Setting the Effort equal to anything but 2139095040 throws this error.
It isn’t really checking the capacity of the resource here, it’s just saying that the non-existent service capacity must be = 2139095040
Anyway to get around this I removed the logic that sets the effort = 1.0 and when BookRequest or RescheduleRequest returns ErrorCode.ResourceBusy, I check the Capacity vs the # appts scheduled in that timeslot & if there is capacity left over, I force it to overbook by using Create or Update.
private Guid BookAppointment(Entity appointment, bool setState, out List<string> errors)
{
Guid apptId = Guid.Empty;
try
{
BookRequest request = new BookRequest
{
Target = appointment
};
BookResponse booked = (BookResponse)this.orgService.Execute(request);
apptId = ParseValidationResult(booked.ValidationResult, setState, appointment, true, out errors);
}
catch (Exception ex)
{
errors = new List<string> { ex.GetBaseException().Message };
}
return apptId;
}
private Guid RescheduleAppointment(Entity appointment, out List<string> errors)
{ // used to reschedule non-recurring appt or appt in recurrence
Guid apptId = Guid.Empty;
try
{
RescheduleRequest request = new RescheduleRequest
{
Target = appointment
};
RescheduleResponse rescheduled = (RescheduleResponse)this.orgService.Execute(request);
apptId = ParseValidationResult(rescheduled.ValidationResult, false, appointment, false, out errors);
}
catch (Exception ex)
{
errors = new List<string> { ex.GetBaseException().Message };
}
return apptId;
}
private Guid ParseValidationResult(ValidationResult result, bool setState, Entity appointment, Boolean addNew, out List<string> errors)
{
Guid apptId = result.ActivityId;
errors = new List<string>();
if (result.ValidationSuccess == true)
{
if (setState == true)
{
SetStateRequest state = new SetStateRequest();
state.State = new OptionSetValue(3); // Scheduled
state.Status = new OptionSetValue(5); // Busy
state.EntityMoniker = new EntityReference("appointment", apptId);
SetStateResponse stateSet = (SetStateResponse)this.orgService.Execute(state);
}
}
else
{
String error;
String errortxt;
Boolean overbookAppt = true;
foreach (var errorInfo in result.TraceInfo.ErrorInfoList)
{
bool unavailable = false;
if (errorInfo.ErrorCode == "ErrorCode.ResourceNonBusinessHours")
{
errortxt = "{0} is being scheduled outside work hours";
}
else if (errorInfo.ErrorCode == "ErrorCode.ResourceBusy")
{
errortxt = "{0} is unavailable at this time";
unavailable = true;
}
else
{
errortxt = "failed to schedule {0}, error code = " + errorInfo.ErrorCode;
}
Dictionary<Guid, String> providers;
Dictionary<Guid, String> resources;
DateTime start = DateTime.Now, end = DateTime.Now;
Guid[] resourceIds = errorInfo.ResourceList.Where(r => r.EntityName == "equipment").Select(r => r.Id).ToList().ToArray();
if (unavailable == true)
{
if (appointment.LogicalName == "recurringappointmentmaster")
{
start = (DateTime)appointment["starttime"];
end = (DateTime)appointment["endtime"];
}
else
{
start = (DateTime)appointment["scheduledstart"];
end = (DateTime)appointment["scheduledend"];
}
Dictionary<Guid, Boolean> availability = GetAvailabilityOfResources(resourceIds, start, end);
resourceIds = availability.Where(a => a.Value == false).Select(t => t.Key).ToArray(); // get ids of all unavailable resources
if (resourceIds.Count() == 0)
{ // all resources still have capacity left at this timeslot - overbook appt timeslot
overbookAppt = true;
} // otherwise at least some resources are booked up in this timeslot - return error
}
if (errortxt.Contains("{0}"))
{ // include resource name in error msg
if (resourceIds.Count() > 0)
{
LoadProviderAndResourceInfo(resourceIds, out providers, out resources);
foreach (var resource in providers)
{
error = String.Format(errortxt, resource.Value);
errors.Add(error);
}
foreach (var resource in resources)
{
error = String.Format(errortxt, resource.Value);
errors.Add(error);
}
}
}
else
{ // no place for name in msg - just store it
errors.Add(errortxt);
break;
}
}
if (overbookAppt == true && errors.Count() == 0)
{ // all resources still have capacity left at this timeslot & no other errors have been returned - create appt anyway
if (addNew)
{
appointment.Attributes.Remove("owner"); // Create message does not like when owner field is specified
apptId = this.orgService.Create(appointment);
if (setState == true)
{
SetStateRequest state = new SetStateRequest();
state.State = new OptionSetValue(3); // Scheduled
state.Status = new OptionSetValue(5); // Busy
state.EntityMoniker = new EntityReference("appointment", apptId);
SetStateResponse stateSet = (SetStateResponse)this.orgService.Execute(state);
}
}
else
{
this.orgService.Update(appointment);
}
}
}
return apptId;
}
private Dictionary<Guid, Boolean> GetAvailabilityOfResources(Guid[] resourceIds, DateTime start, DateTime end)
{
Dictionary<Guid, Boolean> availability = new Dictionary<Guid, Boolean>();
QueryMultipleSchedulesRequest scheduleRequest = new QueryMultipleSchedulesRequest();
scheduleRequest.ResourceIds = resourceIds;
scheduleRequest.Start = start;
scheduleRequest.End = end;
// TimeCode.Unavailable - gets appointments
// TimeCode.Filter - gets resource capacity
scheduleRequest.TimeCodes = new TimeCode[] { TimeCode.Unavailable, TimeCode.Filter };
QueryMultipleSchedulesResponse scheduleResponse = (QueryMultipleSchedulesResponse)this.orgService.Execute(scheduleRequest);
int index = 0;
TimeInfo[][] timeInfo = new TimeInfo[scheduleResponse.TimeInfos.Count()][];
foreach (var schedule in scheduleResponse.TimeInfos)
{
TimeInfo resourceCapacity = schedule.Where(s => s.SubCode == SubCode.ResourceCapacity).FirstOrDefault();
Int32 capacity = (resourceCapacity != null) ? (Int32)resourceCapacity.Effort : 1;
Int32 numAppts = schedule.Where(s => s.SubCode == SubCode.Appointment).Count();
// resource is available if capacity is more than # appts in timeslot
availability.Add(resourceIds[index++], (capacity > numAppts) ? true : false);
}
return availability;
}
This is really a better solution than setting the Effort = 1 anyway because now we don’t need a one-time on-demand workflow to update existing data.
I hope this helps save you some time if you ever need to do this.

Validation for textbox with two sets of phone numbers

I am trying to do a validation on a textbox that can allow the input of one or more phone number in a single textbox. What I am trying to do is to send an message to the phone numbers included in the textbox.
I have no problem when I enter just one set of number into the textbox and the message can be sent.
However, whenever I type two sets of digit into the same textbox, my validation error will appear.
I am using user controls and putting the user control in a listview.
Here are my codes:
private ObservableCollection<IFormControl> formFields;
internal ObservableCollection<IFormControl> FormFields
{
get
{
if (formFields == null)
{
formFields = new ObservableCollection<IFormControl>(new List<IFormControl>()
{
new TextFieldInputControlViewModel(){ColumnWidth = new GridLength(350) ,HeaderName = "Recipient's mobile number *" , IsMandatory = true, MatchingPattern = #"^[\+]?[1-9]{1,3}\s?[0-9]{6,11}$", Tag="phone", ContentHeight = 45, ErrorMessage = "Please enter recipient mobile number. "},
});
}
return formFields;
}
}
And here is the codes for the button click event:
private void OkButton_Click(object sender, RoutedEventArgs e)
{
MessageDialog clickMessage;
UICommand YesBtn;
int result = 0;
//Fetch Phone number
var phoneno = FormFields.FirstOrDefault(x => x.Tag?.ToLower() == "phone").ContentToStore;
string s = phoneno;
string[] numbers = s.Split(';');
foreach (string number in numbers)
{
int parsedValue;
if (int.TryParse(number, out parsedValue) && number.Length.Equals(8))
{
result++;
}
else
{ }
}
if (result.Equals(numbers.Count()))
{
try
{
for (int i = 0; i < numbers.Count(); i++)
{
Class.SMS sms = new Class.SMS();
sms.sendSMS(numbers[i], #"Hi, this is a message from Nanyang Polytechnic School of IT. The meeting venue is located at Block L." + Environment.NewLine + "Click below to view the map " + Environment.NewLine + location);
clickMessage = new MessageDialog("The SMS has been sent to the recipient.");
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
YesBtn = new UICommand("Ok", delegate (IUICommand command)
{
timer.Stop();
idleTimer.Stop();
var rootFrame = (Window.Current.Content as Frame);
rootFrame.Navigate(typeof(HomePage));
rootFrame.BackStack.Clear();
});
clickMessage.Commands.Add(YesBtn);
clickMessage.ShowAsync();
}
}
catch (Exception ex)
{ }
}
}
I am trying to separate the two numbers with ";" sign.... and I am wondering if that is the problem. Or maybe it is the matchingpattern that I have placed in.
The answer is quite simple, create a bool property in your TextFieldInputControlViewModel something like
public bool AcceptMultiple {get;set;}
and to keep things dynamic, create a char property as a separator like below:
public char Separator {get;set;}
Now, modify your new TextFieldInputControlViewModel() code statement by adding values to your new fields like below:
new TextFieldInputControlViewModel(){Separator = ';', AcceptMultiple = true, ColumnWidth = new GridLength(350) ,HeaderName = "Recipient's mobile number *" , IsMandatory = true, MatchingPattern = #"^[\+]?[1-9]{1,3}\s?[0-9]{6,11}$", Tag="phone", ContentHeight = 45, ErrorMessage = "Please enter recipient mobile number. "},
Once it's done, now in your checkValidation() function (or where you check the validation or pattern match) can be replaced with something like below:
if(AcceptMultiple)
{
if(Separator == null)
throw new ArgumentNullException("You have to provide a separator to accept multiple entries.");
string[] textItems = textField.Split(Separator);
if(textItems?.Length < 1)
{
ErrorMessage = "Please enter recipient mobile number." //assuming that this is your field for what message has to be shown.
IsError = true; //assuming this is your bool field that shows all the errors
return;
}
//do a quick check if the pattern matching is mandatory. if it's not, just return.
if(!IsMandatory)
return;
//your Matching Regex Pattern
Regex rgx = new Regex(MatchingPattern);
//loop through every item in the array to find the first entry that's invalid
foreach(var item in textItems)
{
//only check for an invalid input as the valid one's won't trigger any thing.
if(!rgx.IsMatch(item))
{
ErrorMessage = $"{item} is an invalid input";
IsError = true;
break; //this statement will prevent the loop from continuing.
}
}
}
And that'll do it.
I've taken a few variable names as an assumption as the information was missing in the question. I've mentioned it in the comments about them. Make sure you replace them.

CRM SDK 2013 Activity doesn't exist just after creating it

I have a custom comment activity which I'm updating in code and this used to work but has started failing recently. It creates the activity but when I try to retrieve it or execute SetStateResponse on it, I get "tk_comment With Id = 9a1686d1-7d9d-e611-80e3-00155d001104 Does Not Exist" - which doesn't make sense as I've just created it! The activity record shows up against the account but I can't click on it there or do anything (Record is unavailable - The requested record was not found or you do not have sufficient permissions to view it.).
This is the code I'm using. I'd love you to tell me I've made some simple mistake :)
using (_serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig))
{
_serviceProxy.EnableProxyTypes();
try
{
tk_comment comment = new tk_comment();
int maxLength = 190; //subject has a max length of 200 characters
if (subject.Length > maxLength)
{
comment.Subject = subject.Substring(0, maxLength);
comment.Description = subject.Substring(maxLength, subject.Length - maxLength);
}
else
{
comment.Subject = subject;
}
comment.RegardingObjectId = entity.ToEntityReference();
comment.ActualStart = CommentDate;
comment.ActualEnd = CommentDate;
comment.ScheduledStart = CommentDate;
comment.ScheduledEnd = CommentDate;
Guid commentID = _serviceProxy.Create(comment);
try
{
tk_comment aComment = (tk_comment)_serviceProxy.Retrieve(tk_comment.EntityLogicalName, commentID, new ColumnSet(allColumns: true));
}
catch (Exception ex)
{
SingletonLogger.Instance.Error("Always an error here " + ex.Message);
}
Account test = (Account) _serviceProxy.Retrieve(Account.EntityLogicalName, entity.Id, new ColumnSet(allColumns: true));
// tk_comment newComment = (tk_comment)_serviceProxy.Retrieve(tk_comment.EntityLogicalName, commentID, new ColumnSet(allColumns: true));
SetStateRequest request = new SetStateRequest();
request.EntityMoniker = new EntityReference(tk_comment.EntityLogicalName, commentID);
request.State = new OptionSetValue((int) tk_commentState.Completed); //completed
request.Status = new OptionSetValue(2); //completed
SetStateResponse response = (SetStateResponse)_serviceProxy.Execute(request); //always an error here too
}
Appreciate any suggestions
Cheers, Mick
It seems like you don't have proper read permission of this custom activity entity.
OR You need to validate all the permissions of this custom entity.

EWS The server cannot service this request right now

I am seeing errors while exporting email in office 365 account using ews managed api, "The server cannot service this request right now. Try again later." Why is that error occurring and what can be done about it?
I am using the following code for that work:-
_GetEmail = (EmailMessage)item;
bool isread = _GetEmail.IsRead;
sub = _GetEmail.Subject;
fold = folder.DisplayName;
historicalDate = _GetEmail.DateTimeSent.Subtract(folder.Service.TimeZone.GetUtcOffset(_GetEmail.DateTimeSent));
props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(_source, item.Id, props);
bytes = new byte[email.MimeContent.Content.Length];
fs = new MemoryStream(bytes, 0, email.MimeContent.Content.Length, true);
fs.Write(email.MimeContent.Content, 0, email.MimeContent.Content.Length);
Demail = new EmailMessage(_destination);
Demail.MimeContent = new MimeContent("UTF-8", bytes);
// 'SetExtendedProperty' used to maintain historical date of items
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(57, MapiPropertyType.SystemTime), historicalDate);
// PR_MESSAGE_DELIVERY_TIME
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(3590, MapiPropertyType.SystemTime), historicalDate);
if (isread == false)
{
Demail.IsRead = isread;
}
if (_source.RequestedServerVersion == flagVersion && _destination.RequestedServerVersion == flagVersion)
{
Demail.Flag = _GetEmail.Flag;
}
_lstdestmail.Add(Demail);
_objtask = new TaskStatu();
_objtask.TaskId = _taskid;
_objtask.SubTaskId = subtaskid;
_objtask.FolderId = Convert.ToInt64(folderId);
_objtask.SourceItemId = Convert.ToString(_GetEmail.InternetMessageId.ToString());
_objtask.DestinationEmail = Convert.ToString(_fromEmail);
_objtask.CreatedOn = DateTime.UtcNow;
_objtask.IsSubFolder = false;
_objtask.FolderName = fold;
_objdbcontext.TaskStatus.Add(_objtask);
try
{
if (counter == countGroup)
{
Demails = new EmailMessage(_destination);
Demails.Service.CreateItems(_lstdestmail, _destinationFolder.Id, MessageDisposition.SaveOnly, SendInvitationsMode.SendToNone);
_objdbcontext.SaveChanges();
counter = 0;
_lstdestmail.Clear();
}
}
catch (Exception ex)
{
ClouldErrorLog.CreateError(_taskid, subtaskid, ex.Message + GetLineNumber(ex, _taskid, subtaskid), CreateInnerException(sub, fold, historicalDate));
counter = 0;
_lstdestmail.Clear();
continue;
}
This error occurs only if try to export in office 365 accounts and works fine in case of outlook 2010, 2013, 2016 etc..
Usually this is the case when exceed the EWS throttling in Exchange. It is explain in here.
Make sure you already knew throttling policies and your code comply with them.
You can find throttling policies using Get-ThrottlingPolicy if you have the server.
One way to solve the throttling issue you are experiencing is to implement paging instead of requesting all items in one go. You can refer to this link.
For instance:
using Microsoft.Exchange.WebServices.Data;
static void PageSearchItems(ExchangeService service, WellKnownFolderName folder)
{
int pageSize = 5;
int offset = 0;
// Request one more item than your actual pageSize.
// This will be used to detect a change to the result
// set while paging.
ItemView view = new ItemView(pageSize + 1, offset);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.Traversal = ItemTraversal.Shallow;
bool moreItems = true;
ItemId anchorId = null;
while (moreItems)
{
try
{
FindItemsResults<Item> results = service.FindItems(folder, view);
moreItems = results.MoreAvailable;
if (moreItems && anchorId != null)
{
// Check the first result to make sure it matches
// the last result (anchor) from the previous page.
// If it doesn't, that means that something was added
// or deleted since you started the search.
if (results.Items.First<Item>().Id != anchorId)
{
Console.WriteLine("The collection has changed while paging. Some results may be missed.");
}
}
if (moreItems)
view.Offset += pageSize;
anchorId = results.Items.Last<Item>().Id;
// Because you’re including an additional item on the end of your results
// as an anchor, you don't want to display it.
// Set the number to loop as the smaller value between
// the number of items in the collection and the page size.
int displayCount = results.Items.Count > pageSize ? pageSize : results.Items.Count;
for (int i = 0; i < displayCount; i++)
{
Item item = results.Items[i];
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Id: {0}\n", item.Id.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while paging results: {0}", ex.Message);
}
}
}

Custom Plugin Does Not Execute -- Need Assistance With Some Basic Understanding

I am new to CRM and I have created a new AutoNumber plugin (actually modified an existing plugin).
I am having issues getting this plugin to work on the CRM side.
I have created the plugin, and I have created a CREATE step. I am confused with the IMAGE creation, and how I need to go about doing this.
Also, I am using the localContext.Trace and I am not sure where to view this information.
Can anyone help me with understanding the exact steps I need to follow to implement this plugin. I will include my code here in case I am doing something wrong. Again, this was a working plugin and I just modified it. I tried to follow the pattern used by the previous developer.
FYI -- I have purchased the CRM Solution Manager utility to help with the deployment process, but still not having any luck.
Thanks in advance for your time.
Here is the code..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IccPlugin.ProxyClasses;
using IccPlugin.ProxyClasses.ProxyClasses;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
namespace IccPlugin {
public class ProgramReportAutoNumber : PluginBase {
private readonly string imageAlias = "ProgramReport";
private new_programreport preImage { get; set; }
private new_programreport postImage { get; set; }
private new_programreport targetEntity { get; set; }
private readonly string imageAlias_program = "Program";
private new_program preImage_program { get; set; }
private new_program postImage_program { get; set; }
private new_program targetEntity_program { get; set; }
public ProgramReportAutoNumber(string unsecure, string secure)
: base(typeof(ProgramReportAutoNumber), unsecure, secure) {
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>((int)CrmPluginStepStage.PreOperation, "Create", "new_programreport", new Action<LocalPluginContext>(Execute)));
//base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>((int)CrmPluginStepStage.PostOperation, "Update", "new_programreport", new Action<LocalPluginContext>(Execute)));
//base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>((int)CrmPluginStepStage.PostOperation, "Delete", "new_programreport", new Action<LocalPluginContext>(Execute)));
}
protected void Execute(LocalPluginContext localContext) {
if (localContext == null) {
throw new ArgumentNullException("localContext");
}
IPluginExecutionContext context = localContext.PluginExecutionContext;
if (context.PreEntityImages.Contains(imageAlias) && (context.PreEntityImages[imageAlias] is Entity)) {
preImage = new new_programreport((Entity)context.PreEntityImages[imageAlias]);
}
if (context.PostEntityImages.Contains(imageAlias) && (context.PostEntityImages[imageAlias] is Entity)) {
postImage = new new_programreport((Entity)context.PostEntityImages[imageAlias]);
}
if (context.PreEntityImages.Contains(imageAlias_program) && (context.PreEntityImages[imageAlias_program] is Entity)) {
preImage_program = new new_program((Entity)context.PreEntityImages[imageAlias_program]);
}
if (context.PostEntityImages.Contains(imageAlias_program) && (context.PostEntityImages[imageAlias_program] is Entity)) {
postImage_program = new new_program((Entity)context.PostEntityImages[imageAlias_program]);
}
if (context.InputParameters.Contains("Target") && (context.InputParameters["Target"] is Entity)) {
targetEntity = new new_programreport((Entity)context.InputParameters["Target"]);
}
switch (context.MessageName) {
case "Create":
HandleCreate(localContext);
break;
case "Update":
HandleUpdate(localContext);
break;
case "Delete":
HandleDelete(localContext);
break;
default:
throw new ArgumentException("Invalid Message Name: " + context.MessageName);
}
}
private void HandleDelete(LocalPluginContext localContext) {
localContext.Trace("START - IccPlugin.ProgramReport.AutoNumber.HandleDelete");
try {
if (preImage == null) {
throw new Exception("IccPlugin.ProgramReport.AutoNumber.HandleDelete: preImage is null, unable to process the delete message.");
}
// TODO: Add code here to implement delete message.
} catch (Exception ex) {
localContext.Trace(String.Format("IccPlugin.ProgramReport.AutoNumber.HandleDelete: Exception while processing the delete message, Error Message: {0}", ex.Message), ex);
throw ex;
} finally {
localContext.Trace("END - IccPlugin.ProgramReport.AutoNumber.HandleDelete");
}
return;
}
private void HandleUpdate(LocalPluginContext localContext) {
localContext.Trace("START - IccPlugin.ProgramReport.AutoNumber.HandleUpdate");
if (preImage == null) {
string msg = "IccPlugin.ProgramReport.AutoNumber.HandleUpdate : The Update step is not registered correctly. Unable to retrieve the pre-operation image using alias" + imageAlias;
localContext.Trace(msg);
throw new Exception(msg);
}
if (postImage == null) {
string msg = "IccPlugin.ProgramReport.AutoNumber.HandleUpdate : The Update step is not registered correctly. Unable to retrieve the post-operation image using alias" + imageAlias;
localContext.Trace(msg);
throw new Exception(msg);
}
if (preImage_program == null) {
string msg = "IccPlugin.Program.AutoNumber.HandleUpdate : The Update step is not registered correctly. Unable to retrieve the pre-operation image using alias" + imageAlias_program;
localContext.Trace(msg);
throw new Exception(msg);
}
if (postImage_program == null) {
string msg = "IccPlugin.Program.AutoNumber.HandleUpdate : The Update step is not registered correctly. Unable to retrieve the post-operation image using alias" + imageAlias_program;
localContext.Trace(msg);
throw new Exception(msg);
}
try {
// TODO: Add code here to implement update message.
} catch (Exception ex) {
localContext.Trace(String.Format("IccPlugin.ProgramReport.AutoNumber.HandleUpdate: Exception while processing the update message, Error Message: {0}", ex.Message), ex);
throw ex;
} finally {
localContext.Trace("END - IccPlugin.ProgramReport.AutoNumber.HandleUpdate");
}
return;
}
private void HandleCreate(LocalPluginContext localContext) {
localContext.Trace("START - IccPlugin.ProgramReport.AutoNumber.HandleCreate");
if (targetEntity == null) {
string msg = "IccPlugin.ProgramReport.AutoNumber.HandleCreate : The Create step is not registered correctly. Unable to retrieve the target entity using alias Target.";
localContext.Trace(msg);
throw new Exception(msg);
}
try {
// if the target entity does not have the new_filenumber attribute set we will set it now.
if (targetEntity.new_filenumber != null && targetEntity.new_filenumber != "") {
// log a warning message and do not change this value.
localContext.Trace("The Program Report being created already has a value for File Number, skipping the auto number assignment for this field.");
} else {
SetFileNumber(localContext);
}
if (targetEntity.new_name != null && targetEntity.new_name != "") {
localContext.Trace("The Program Report being created already has a value for Report Number, skipping the auto number assignment for this field.");
} else {
SetReportNumber(localContext);
}
} catch (Exception ex) {
localContext.Trace(String.Format("IccPlugin.ProgramReport.AutoNumber.HandleCreate: Exception while processing the create message, Error Message: {0}", ex.Message), ex);
throw ex;
} finally {
localContext.Trace("END - IccPlugin.ProgramReport.AutoNumber.HandleCreate");
}
return;
}
private void SetFileNumber(LocalPluginContext localContext) {
localContext.Trace("START - IccPlugin.ProgramReport.AutoNumber.SetFileNumber");
string s_new_filenumberformat = string.Empty;
string s_new_reportnumberprefix = string.Empty;
string s_new_filenumbercode = string.Empty;
try {
IOrganizationService service = localContext.OrganizationService;
string fileNumberValue = "";
emds_autonumbersequence fileNumberSequence = null;
// ##################################################################################################
// 05/02/2013 -- BEP -- Code added for the following change to the auto-number for file numbering
// ##################################################################################################
// 1 - Year/Month/Sequence
// [Year]-[Month]-[Sequence] = [Year] is the current year / [Month] is the current month / [Sequence] is a number series for each Year & Month and resets to 1 when the Month changes
// 2 - Year/PMG/Sequence - PMG
// [Year]-[PMGProductType][Sequence] = [Year] is the current year / [PMGProductType] is 1st letter from the PMG Product Type on the Program Report / [Sequence] is a single number series for this Format
// 3 - Year/Letter/Sequence - ESL,VAR
// [Year]-[FileCode][Sequence] = [Year] is the current year / [FileCode] is from a new field on the Program entity / [Sequence] is a number series for each Format & File Code
// ##################################################################################################
localContext.Trace("Look at the File Number Format to determine which format to use for the Auto-Number, will default to 1 if not set");
if (targetEntity_program.new_filenumberformat.ToString() != "") {
localContext.Trace("A value was set for the new_filenumberformat field, so we will be using this value.");
s_new_filenumberformat = targetEntity_program.new_filenumberformat.ToString();
} else {
localContext.Trace("A value was NOT set for the new_filenumberformat field, so we will be using 1 as the default.");
s_new_filenumberformat = "1";
}
localContext.Trace("File Number Format Being Used = " + s_new_filenumberformat);
switch (s_new_filenumberformat) {
case "1":
#region File Format #1
fileNumberValue = String.Format("{0}-{1}", DateTime.Now.ToString("yy"), DateTime.Now.ToString("MM"));
localContext.Trace("Building QueryExpression to retrieve FileNumber Sequence record.");
QueryExpression qeFileNumberSequence_1 = new QueryExpression(BaseProxyClass.GetLogicalName<emds_autonumbersequence>());
qeFileNumberSequence_1.ColumnSet = new ColumnSet(true);
qeFileNumberSequence_1.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_entitylogicalname, ConditionOperator.Equal, BaseProxyClass.GetLogicalName<new_programreport>());
qeFileNumberSequence_1.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_attributelogicalname, ConditionOperator.Equal, new_programreport.Properties.new_filenumber);
qeFileNumberSequence_1.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_prefix, ConditionOperator.Equal, fileNumberValue);
localContext.Trace("Getting FileNumber sequence record.");
List<emds_autonumbersequence> lFileNumberSequences_1 = service.RetrieveProxies<emds_autonumbersequence>(qeFileNumberSequence_1);
if (lFileNumberSequences_1 == null || lFileNumberSequences_1.Count == 0) {
localContext.Trace("No FileNumber sequence record was returned, creatign a new one.");
// no matching sequence records. Lets start a new sequence index record for this month and year.
fileNumberSequence = new emds_autonumbersequence();
fileNumberSequence.emds_attributelogicalname = new_programreport.Properties.new_filenumber;
fileNumberSequence.emds_entitylogicalname = BaseProxyClass.GetLogicalName<new_programreport>();
fileNumberSequence.emds_index = 1;
fileNumberSequence.emds_prefix = fileNumberValue;
fileNumberSequence.emds_name = String.Format("File Number Sequence For: {0}", fileNumberValue);
fileNumberSequence.Create(service);
} else {
localContext.Trace("A FileNumber sequence record was found, using it.");
// a file number sequence record was returned. Even if there happen to be multiple we are going to just use the first one returned.
fileNumberSequence = lFileNumberSequences_1[0];
}
// ###############################################################################
// 05/02/2013 -- BEP -- Changed the format from "###" to be "##" for seq number
// ###############################################################################
fileNumberValue = String.Format("{0}-{1:00}", fileNumberValue, fileNumberSequence.emds_index);
fileNumberSequence.emds_index++;
fileNumberSequence.Update(service);
#endregion
break;
case "2":
#region File Format #2
if (targetEntity_program.new_reportnumberprefix != null && targetEntity_program.new_reportnumberprefix != "") {
localContext.Trace("A value was set for the new_reportnumberprefix field, so we will be using this value.");
s_new_reportnumberprefix = targetEntity_program.new_reportnumberprefix;
} else {
localContext.Trace("A value was NOT set for the new_reportnumberprefix field, so we will be using P as the default.");
s_new_reportnumberprefix = "P";
}
fileNumberValue = String.Format("{0}-{1}", DateTime.Now.ToString("yy"), s_new_reportnumberprefix);
localContext.Trace("Building QueryExpression to retrieve FileNumber Sequence record.");
QueryExpression qeFileNumberSequence_2 = new QueryExpression(BaseProxyClass.GetLogicalName<emds_autonumbersequence>());
qeFileNumberSequence_2.ColumnSet = new ColumnSet(true);
qeFileNumberSequence_2.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_entitylogicalname, ConditionOperator.Equal, BaseProxyClass.GetLogicalName<new_programreport>());
qeFileNumberSequence_2.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_attributelogicalname, ConditionOperator.Equal, new_programreport.Properties.new_filenumber);
qeFileNumberSequence_2.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_prefix, ConditionOperator.Equal, "PMG");
localContext.Trace("Getting FileNumber sequence record.");
List<emds_autonumbersequence> lFileNumberSequences_2 = service.RetrieveProxies<emds_autonumbersequence>(qeFileNumberSequence_2);
if (lFileNumberSequences_2 == null || lFileNumberSequences_2.Count == 0) {
localContext.Trace("No FileNumber sequence record was returned, creatign a new one.");
// no matching sequence records. Lets start a new sequence index record for this month and year.
fileNumberSequence = new emds_autonumbersequence();
fileNumberSequence.emds_attributelogicalname = new_programreport.Properties.new_filenumber;
fileNumberSequence.emds_entitylogicalname = BaseProxyClass.GetLogicalName<new_programreport>();
fileNumberSequence.emds_index = 1;
fileNumberSequence.emds_prefix = "PMG";
fileNumberSequence.emds_name = String.Format("File Number Sequence For: {0}", fileNumberValue);
fileNumberSequence.Create(service);
} else {
localContext.Trace("A FileNumber sequence record was found, using it.");
// a file number sequence record was returned. Even if there happen to be multiple we are going to just use the first one returned.
fileNumberSequence = lFileNumberSequences_2[0];
}
fileNumberValue = String.Format("{0}-{1:0000}", fileNumberValue, fileNumberValue + fileNumberSequence.emds_index.ToString());
fileNumberSequence.emds_index++;
fileNumberSequence.Update(service);
#endregion
break;
case "3":
#region File Format #3
if (targetEntity_program.new_filenumbercode != null && targetEntity_program.new_filenumbercode != "") {
localContext.Trace("A value was set for the new_filenumbercode field, so we will be using this value.");
s_new_filenumbercode = targetEntity_program.new_filenumbercode;
} else {
localContext.Trace("A value was NOT set for the new_filenumbercode field, so we will be using L as the default.");
s_new_filenumbercode = "l";
}
fileNumberValue = String.Format("{0}-{1}", DateTime.Now.ToString("yy"), s_new_filenumbercode);
localContext.Trace("Building QueryExpression to retrieve FileNumber Sequence record.");
QueryExpression qeFileNumberSequence_3 = new QueryExpression(BaseProxyClass.GetLogicalName<emds_autonumbersequence>());
qeFileNumberSequence_3.ColumnSet = new ColumnSet(true);
qeFileNumberSequence_3.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_entitylogicalname, ConditionOperator.Equal, BaseProxyClass.GetLogicalName<new_programreport>());
qeFileNumberSequence_3.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_attributelogicalname, ConditionOperator.Equal, new_programreport.Properties.new_filenumber);
qeFileNumberSequence_3.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_prefix, ConditionOperator.Equal, fileNumberValue);
localContext.Trace("Getting FileNumber sequence record.");
List<emds_autonumbersequence> lFileNumberSequences_3 = service.RetrieveProxies<emds_autonumbersequence>(qeFileNumberSequence_3);
if (lFileNumberSequences_3 == null || lFileNumberSequences_3.Count == 0) {
localContext.Trace("No FileNumber sequence record was returned, creatign a new one.");
// no matching sequence records. Lets start a new sequence index record for this month and year.
fileNumberSequence = new emds_autonumbersequence();
fileNumberSequence.emds_attributelogicalname = new_programreport.Properties.new_filenumber;
fileNumberSequence.emds_entitylogicalname = BaseProxyClass.GetLogicalName<new_programreport>();
fileNumberSequence.emds_index = 1;
fileNumberSequence.emds_prefix = fileNumberValue;
fileNumberSequence.emds_name = String.Format("File Number Sequence For: {0}", fileNumberValue);
fileNumberSequence.Create(service);
} else {
localContext.Trace("A FileNumber sequence record was found, using it.");
// a file number sequence record was returned. Even if there happen to be multiple we are going to just use the first one returned.
fileNumberSequence = lFileNumberSequences_3[0];
}
fileNumberValue = String.Format("{0}-{1:0000}", fileNumberValue, fileNumberValue + fileNumberSequence.emds_index.ToString());
fileNumberSequence.emds_index++;
fileNumberSequence.Update(service);
#endregion
break;
default:
break;
}
targetEntity.new_filenumber = fileNumberValue;
} catch (Exception ex) {
localContext.Trace(String.Format("IccPlugin.ProgramReport.AutoNumber.SetFileNumber: Exception while setting the File Number value, Error Message: {0}", ex.Message), ex);
throw ex;
} finally {
localContext.Trace("END - IccPlugin.ProgramReport.AutoNumber.SetFileNumber");
}
}
private void SetReportNumber(LocalPluginContext localContext) {
localContext.Trace("START - IccPlugin.ProgramReport.AutoNumber.SetReportNumber");
string s_new_reportnumberprefix = string.Empty;
try {
IOrganizationService service = localContext.OrganizationService;
string reportNumberValue = "";
emds_autonumbersequence reportNumberSequence = null;
// ##################################################################################################
// 05/02/2013 -- BEP -- Code added for the following change to the auto-number for file numbering
// ##################################################################################################
// Currently the plugin uses the GP Class Id as the prefix for the Report Number.
// It now needs to use the Report Number Prefix field.
// ##################################################################################################
if (targetEntity_program.new_reportnumberprefix != null && targetEntity_program.new_reportnumberprefix != "") {
localContext.Trace("A value was set for the new_reportnumberprefix field, so we will be using this value.");
s_new_reportnumberprefix = targetEntity_program.new_reportnumberprefix;
} else {
localContext.Trace("A value was NOT set for the new_reportnumberprefix field, so we will be using P as the default.");
s_new_reportnumberprefix = "P";
}
localContext.Trace("Building QueryExpression to retrieve parent new_program record.");
// #################################################################################
// 05/02/2013 -- BEP -- The above code replaces the need to pull the GP Class ID
// #################################################################################
//new_program program = targetEntity.new_programid.RetrieveProxy<new_program>(service, new ColumnSet(true));
// going to assume that we were able to get the parent program record. If not an exception will be thrown.
// could add a check here and throw our own detailed exception if needed.
reportNumberValue = String.Format("{0}", s_new_reportnumberprefix); // using Trim just to be safe.
// now lets get the sequence record for this Report Number Prefix
QueryExpression qeReportNumberSequence = new QueryExpression(BaseProxyClass.GetLogicalName<emds_autonumbersequence>());
qeReportNumberSequence.ColumnSet = new ColumnSet(true);
qeReportNumberSequence.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_entitylogicalname, ConditionOperator.Equal, BaseProxyClass.GetLogicalName<new_programreport>());
qeReportNumberSequence.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_attributelogicalname, ConditionOperator.Equal, new_programreport.Properties.new_name);
qeReportNumberSequence.Criteria.AddCondition(emds_autonumbersequence.Properties.emds_prefix, ConditionOperator.Equal, reportNumberValue);
localContext.Trace("Getting Report Number sequence record.");
List<emds_autonumbersequence> lReportNumberSequences = service.RetrieveProxies<emds_autonumbersequence>(qeReportNumberSequence);
if (lReportNumberSequences == null || lReportNumberSequences.Count == 0) {
localContext.Trace("No Report Number sequence record was returned, creatign a new one.");
// no matching sequence records. Lets start a new sequence index record for this month and year.
reportNumberSequence = new emds_autonumbersequence();
reportNumberSequence.emds_attributelogicalname = new_programreport.Properties.new_name;
reportNumberSequence.emds_entitylogicalname = BaseProxyClass.GetLogicalName<new_programreport>();
reportNumberSequence.emds_index = 1;
reportNumberSequence.emds_prefix = reportNumberValue;
reportNumberSequence.emds_name = String.Format("Report Number Sequence For Report Number Prefix: {0}", reportNumberValue);
reportNumberSequence.Create(service);
} else {
localContext.Trace("A Report Number sequence record was found, using it.");
// a file number sequence record was returned. Even if there happen to be multiple we are going to just use the first one returned.
reportNumberSequence = lReportNumberSequences[0];
}
reportNumberValue = String.Format("{0}-{1}", reportNumberValue, reportNumberSequence.emds_index);
reportNumberSequence.emds_index++;
reportNumberSequence.Update(service);
targetEntity.new_name = reportNumberValue;
} catch (Exception ex) {
localContext.Trace(String.Format("IccPlugin.ProgramReport.AutoNumber.SetReportNumber: Exception while setting the File Number value, Error Message: {0}", ex.Message), ex);
throw ex;
} finally {
localContext.Trace("END - IccPlugin.ProgramReport.AutoNumber.SetReportNumber");
}
}
}
}
This is specifically to response to:
I am using the localContext.Trace and I am not sure where to view this
information
From the MSDN; Debug a Plug-In - Logging and Tracing
During execution and only when that plug-in passes an exception back
to the platform at run-time, tracing information is displayed to the
user. For a synchronous registered plug-in, the tracing information is
displayed in a dialog box of the Microsoft Dynamics CRM web
application. For an asynchronous registered plug-in, the tracing
information is shown in the Details area of the System Job form in the
web application.

Resources