In order to overcome the limit, I have come up two custom workflow activity classes which let you create any offset date, using the workflow base class that I just created.
1. InvariantOffsetDays
using System;
using System.Globalization;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Sdk;
namespace CrmSpikes.Workflow
{
///
/// Calculate a series of dates using a base date with a set of predefined offset numbers.
/// @author Daniel Cai, http://danielcai.blogspot.com/
///
[CrmWorkflowActivity("Invariant Offset Days", "Custom Workflow")]
public class InvariantOffsetDays : SingleActivityBase
{
private DateTime _baseDate;
#region Workflow Parameters
public static DependencyProperty BaseDateProperty =
DependencyProperty.Register("BaseDate", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmInput("Base Date")]
public CrmDateTime BaseDate
{
get
{
return (CrmDateTime)GetValue(BaseDateProperty);
}
set
{
SetValue(BaseDateProperty, value);
}
}
public static DependencyProperty Offset7DaysProperty =
DependencyProperty.Register("Offset7Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("7 Days after the base date")]
public CrmDateTime Offset7Days
{
get
{
return (CrmDateTime)GetValue(Offset7DaysProperty);
}
set
{
SetValue(Offset7DaysProperty, value);
}
}
public static DependencyProperty Offset14DaysProperty =
DependencyProperty.Register("Offset14Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("14 Days after the base date")]
public CrmDateTime Offset14Days
{
get
{
return (CrmDateTime)GetValue(Offset14DaysProperty);
}
set
{
SetValue(Offset14DaysProperty, value);
}
}
public static DependencyProperty Offset21DaysProperty =
DependencyProperty.Register("Offset21Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("21 Days after the base date")]
public CrmDateTime Offset21Days
{
get
{
return (CrmDateTime)GetValue(Offset21DaysProperty);
}
set
{
SetValue(Offset21DaysProperty, value);
}
}
public static DependencyProperty Offset28DaysProperty =
DependencyProperty.Register("Offset28Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("28 Days after the base date")]
public CrmDateTime Offset28Days
{
get
{
return (CrmDateTime)GetValue(Offset28DaysProperty);
}
set
{
SetValue(Offset28DaysProperty, value);
}
}
public static DependencyProperty Offset35DaysProperty =
DependencyProperty.Register("Offset35Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("35 Days after the base date")]
public CrmDateTime Offset35Days
{
get
{
return (CrmDateTime)GetValue(Offset35DaysProperty);
}
set
{
SetValue(Offset35DaysProperty, value);
}
}
public static DependencyProperty Offset42DaysProperty =
DependencyProperty.Register("Offset42Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("42 Days after the base date")]
public CrmDateTime Offset42Days
{
get
{
return (CrmDateTime)GetValue(Offset42DaysProperty);
}
set
{
SetValue(Offset42DaysProperty, value);
}
}
public static DependencyProperty Offset49DaysProperty =
DependencyProperty.Register("Offset49Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("49 Days after the base date")]
public CrmDateTime Offset49Days
{
get
{
return (CrmDateTime)GetValue(Offset49DaysProperty);
}
set
{
SetValue(Offset49DaysProperty, value);
}
}
public static DependencyProperty Offset56DaysProperty =
DependencyProperty.Register("Offset56Days", typeof(CrmDateTime), typeof(InvariantOffsetDays));
[CrmOutput("56 Days after the base date")]
public CrmDateTime Offset56Days
{
get
{
return (CrmDateTime)GetValue(Offset56DaysProperty);
}
set
{
SetValue(Offset56DaysProperty, value);
}
}
#endregion
#region SequenceActivity
protected override void ExecuteBody()
{
_baseDate = BaseDate.UniversalTime;
Offset7Days = CalculateOffsetDate(7);
Offset14Days = CalculateOffsetDate(14);
Offset21Days = CalculateOffsetDate(21);
Offset28Days = CalculateOffsetDate(28);
Offset35Days = CalculateOffsetDate(35);
Offset42Days = CalculateOffsetDate(42);
Offset49Days = CalculateOffsetDate(49);
Offset56Days = CalculateOffsetDate(56);
}
private CrmDateTime CalculateOffsetDate(int offset)
{
DateTime resultDate = _baseDate.AddDays(offset);
return CrmDateTime.FromUniversal(resultDate);
}
#endregion
}
}After you have registered the workflow assembly, and added the custom workflow activity to your workflow by providing a base date, you can then access the generated offset dates as shown below: InvariantOffsetDays class uses a set of predefined offset numbers to generate a series of offset dates based on the provided base CRM date. The offset is hard-coded in the class due to the way how workflow activity works. You may change it to any intervals or any combination of offset numbers.
2. VariantOffsetDays class
using System;
using System.Globalization;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Sdk;
namespace CrmSpikes.Workflow
{
/// <summary>
/// Calculate a new date using a base date and an offset number (positive or negative).
/// @author Daniel Cai, http://danielcai.blogspot.com/
/// </summary>
[CrmWorkflowActivity("Variant Offset Days", "Custom Workflow")]
public class VariantOffsetDays : SingleActivityBase
{
#region Workflow Parameters
public static DependencyProperty BaseDateProperty =
DependencyProperty.Register("BaseDate", typeof(CrmDateTime), typeof(VariantOffsetDays));
[CrmInput("Base Date")]
public CrmDateTime BaseDate
{
get
{
return (CrmDateTime)GetValue(BaseDateProperty);
}
set
{
SetValue(BaseDateProperty, value);
}
}
public static DependencyProperty OffsetProperty =
DependencyProperty.Register("Offset", typeof(CrmNumber), typeof(VariantOffsetDays));
[CrmInput("Offset (Positive or Negative)")]
public CrmNumber Offset
{
get
{
return (CrmNumber)GetValue(OffsetProperty);
}
set
{
SetValue(OffsetProperty, value);
}
}
public static DependencyProperty ResultDateProperty =
DependencyProperty.Register("ResultDate", typeof(CrmDateTime), typeof(VariantOffsetDays));
[CrmOutput("Result Date")]
public CrmDateTime ResultDate
{
get
{
return (CrmDateTime)GetValue(ResultDateProperty);
}
set
{
SetValue(ResultDateProperty, value);
}
}
#endregion
#region SequenceActivity
protected override void ExecuteBody()
{
DateTime baseDate = BaseDate.UniversalTime;
DateTime resultDate = baseDate.AddDays(Offset.Value);
ResultDate = CrmDateTime.FromUniversal(resultDate);
}
#endregion
}
}VariantOffsetDays class accepts two parameters, which are the base date, and the offset days (int, either positive or negative), as shown below. It’s a more flexible solution than InvariantOffsetDays, the trade-off is it can only generate one offset date at one time. Both classes can be used to create any type of offset, including month-based offset or year-based offset, which is your call.
The reason that I wrote this blog post was that two persons asked similar questions within a month on CRM Development Forum and CRM Forum about how to specify an offset date for a CRM workflow that cannot be done using the native CRM workflow design tool. There doesn't seem to be any available solution on Internet to address this issue, so I decided to write the custom workflow along with the workflow base class.
Note: Please make sure to include the workflow base class that I created in order to compile the code.
Download the source code and compiled assembly below.
Hope this helps if you ever need to do the same thing.