Yeah, then you need a recipient provider that can look at orders.
Or make a setup like this:
- Create a group in user management for anonymous users in ecommerce
- Create a sceheduled task using SQL that will take all user info from orders and insert them into AccessUser table in the group from step 1
- Make logic to ensure users are updated depending on your logic if they place additional orders
- Use smart search to query that group
Alternative is to create a RecipientProvider - below our abandoned cart provider you can use as a starting point - it also takes a look at orders:
/// <summary>
/// Abandoned cart recipients provider
/// </summary>
[AddInName("Abandoned cart recipients")]
public class AbandonedCartRecipientProvider : EmailRecipientProvider, IDropDownOptions
{
/// <summary>
/// Gets or sets 'mark abandoned after' value
/// </summary>
/// <returns>Integer value of hours</returns>
[AddInParameter("MarkAbandonedAfter"), AddInLabel("Mark abandoned after"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")]
public int MarkAbandonedAfter { get; set; } = 180; // Default 180 = 3 hours
/// <summary>
/// Gets or sets 'ignore carts older than' value
/// </summary>
/// <returns>Integer value of hours</returns>
[AddInParameter("IgnoreAbandonedCartsOlderThan"), AddInLabel("Ignore carts older than"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")]
public int IgnoreAbandonedCartsOlderThan { get; set; } = 1440; // Default 1440 = 24 hours
/// <summary>
/// Gets or sets 'only abandoned carts from this shop' value
/// </summary>
/// <returns>String value</returns>
[AddInParameter("AbandonedCartsShop"), AddInLabel("Only carts from this shop"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")]
public string AbandonedCartsShop { get; set; } = string.Empty;
/// <summary>
/// Gets or sets 'only abandoned carts in this language' value
/// </summary>
/// <returns>String value</returns>
[AddInParameter("AbandonedCartsLanguage"), AddInLabel("Only carts in this language"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")]
public string AbandonedCartsLanguage { get; set; } = string.Empty;
/// <summary>
/// Gets or sets 'require customer was logged in' value
/// </summary>
/// <returns>True in case when customer was logged in</returns>
[AddInParameter("RequireCustomerLoggedIn"), AddInLabel("Require customer was logged in"), AddInParameterEditor(typeof(YesNoParameterEditor), "")]
public bool RequireCustomerLoggedIn { get; set; }
/// <summary>
/// Gets or sets 'require customer accepted sales terms' value
/// </summary>
/// <returns>Boolean value</returns>
[AddInParameter("RequireCustomerAcceptedSalesTerms"), AddInLabel("Require customer accepted sales terms"), AddInParameterEditor(typeof(YesNoParameterEditor), "")]
public bool RequireCustomerAcceptedSalesTerms { get; set; }
/// <summary>
/// Gets or sets 'require logged in or accepted sales terms' value
/// </summary>
/// <returns>Boolean value</returns>
[AddInParameter("RequireCustomerLoggedInOrAcceptedTerms"), AddInLabel("Require logged in or accepted sales terms"), AddInParameterEditor(typeof(YesNoParameterEditor), "")]
public bool RequireEitherCustomerLoggedInOrAcceptedTerms { get; set; }
/// <summary>
/// Get order recipients
/// </summary>
/// <returns>Recipient collection</returns>
public override RecipientCollection GetRecipients()
{
RecipientCollection recipients = new RecipientCollection();
CommandBuilder commandBuilder = new CommandBuilder();
commandBuilder.Add("SELECT OrderID, OrderCustomerEmail, OrderCustomerName, OrderCustomerAccessUserID");
commandBuilder.Add("FROM EcomOrders");
commandBuilder.Add("WHERE");
commandBuilder.Add(" OrderComplete = 0");
commandBuilder.Add(" AND OrderCart = 1");
AddOrderModifiedWhereClause(commandBuilder, IgnoreAbandonedCartsOlderThan, MarkAbandonedAfter);
commandBuilder.Add(" AND OrderCustomerEmail IS NOT NULL");
commandBuilder.Add(" AND OrderCustomerEmail <> ''");
commandBuilder.Add(" AND OrderCustomerEmail <> ''");
//ensure that cart is not "lost"
commandBuilder.Add(" AND (OrderCustomerAccessUserID IS NULL OR OrderCustomerAccessUserID < 1 OR OrderID = (SELECT TOP 1 AccessUserCartID FROM AccessUser WHERE AccessUserID = OrderCustomerAccessUserID)");
//also check contexts
commandBuilder.Add(" OR OrderID IN (SELECT OrderContextAccessUserOrderID FROM EcomOrderContextAccessUserRelation WHERE OrderContextAccessUserAccessUserID = OrderCustomerAccessUserID))");
if (RequireEitherCustomerLoggedInOrAcceptedTerms || RequireCustomerLoggedIn || RequireCustomerAcceptedSalesTerms)
{
commandBuilder.Add(" AND (");
if (RequireEitherCustomerLoggedInOrAcceptedTerms || RequireCustomerLoggedIn)
{
commandBuilder.Add(" OrderCustomerAccessUserID > 0");
}
if (RequireEitherCustomerLoggedInOrAcceptedTerms)
{
commandBuilder.Add(" OR");
}
else if (RequireCustomerLoggedIn && RequireCustomerAcceptedSalesTerms)
{
commandBuilder.Add(" AND");
}
if (RequireEitherCustomerLoggedInOrAcceptedTerms || RequireCustomerAcceptedSalesTerms)
{
commandBuilder.Add(" OrderCustomerAccepted = 1");
}
commandBuilder.Add(" )");
}
if (!string.IsNullOrWhiteSpace(AbandonedCartsShop))
{
commandBuilder.Add(" AND OrderShopID = '" + AbandonedCartsShop + "'");
}
if (!string.IsNullOrWhiteSpace(AbandonedCartsLanguage))
{
commandBuilder.Add(" AND OrderLanguageID = '" + AbandonedCartsLanguage + "'");
}
using (IDataReader dataReader = Database.CreateDataReader(commandBuilder))
{
while (dataReader.Read())
{
string orderId = Converter.ToString(dataReader["OrderID"]);
string email = Converter.ToString(dataReader["OrderCustomerEmail"]);
string name = Converter.ToString(dataReader["OrderCustomerName"]);
int userId = Converter.ToInt32(dataReader["OrderCustomerAccessUserID"]);
if (!StringHelper.IsValidEmailAddress(email))
{
continue;
}
Recipient recipient = new Recipient();
recipient.EmailAddress = email;
recipient.Name = name;
recipient.RecipientKey = orderId;
if (userId > 0)
{
User user = User.GetUserByID(userId);
AccessUserRecipientProvider.SetTags(recipient, user);
}
recipients.Add(recipient);
}
}
return recipients;
}
/// <summary>
/// Gets context of recipient content
/// </summary>
/// <param name="recipient">Recipient object instance</param>
/// <returns>Page ciew context with order data</returns>
public override PageViewContext GetRecipientContentContext(Recipient recipient)
{
PageViewContext pageViewContext = new PageViewContext();
Order order = Ecommerce.Services.Orders.GetById(recipient.RecipientKey);
if (order != null && !string.IsNullOrEmpty(order.Id))
{
pageViewContext.SetValue("OrderID", recipient.RecipientKey);
pageViewContext.SetValue("Order", order);
}
return pageViewContext;
}
private static void AddOrderModifiedWhereClause(CommandBuilder builder, int lowerTimeBound, int upperTimeBound)
{
DateTime now = DateTime.Now;
var lower = now.AddMinutes(-lowerTimeBound);
var upper = now.AddMinutes(-upperTimeBound);
if (lowerTimeBound > 0 && upperTimeBound > 0)
{
builder.Add(" AND OrderModified BETWEEN {0} AND {1}", lower, upper);
}
else if (lowerTimeBound > 0)
{
builder.Add(" AND OrderModified >= {0}", lower);
}
else if (upperTimeBound > 0)
{
builder.Add(" AND OrderModified <= {0}", upper);
}
else
{
builder.Add(" AND OrderModified <= {0}", DateTime.Now);
}
}
/// <summary>
/// Implements <see cref="M:Dynamicweb.Extensibility.Editors.IDropDownOptions.GetOptions">IDropDownOptions.GetOptions</see> method,
/// and used for DropDownLists control
/// </summary>
/// <param name="name">Specified current control name</param>
/// <returns>Hashtable with specified control options</returns>
public Hashtable GetOptions(string name)
{
Hashtable result = new Hashtable();
if (name == "MarkAbandonedAfter")
{
TimeSpan[] timePeriods = new TimeSpan[]
{
new TimeSpan(0, 1, 0),
new TimeSpan(0, 30, 0),
new TimeSpan(1, 0, 0),
new TimeSpan(2, 0, 0),
new TimeSpan(3, 0, 0),
new TimeSpan(4, 0, 0),
new TimeSpan(5, 0, 0),
new TimeSpan(6, 0, 0),
new TimeSpan(12, 0, 0),
new TimeSpan(18, 0, 0),
new TimeSpan(24, 0, 0),
new TimeSpan(48, 0, 0),
new TimeSpan(72, 0, 0),
new TimeSpan(96, 0, 0),
new TimeSpan(120, 0, 0),
new TimeSpan(144, 0, 0),
new TimeSpan(7, 0, 0, 0),
new TimeSpan(14, 0, 0, 0, 0),
new TimeSpan(30, 0, 0, 0, 0)
};
foreach (TimeSpan span in timePeriods)
{
result.Add(Convert.ToInt32(span.TotalMinutes), GetTimeSpanInText(span));
}
}
else if (name == "IgnoreAbandonedCartsOlderThan")
{
result.Add(0, Translate.Translate("No limit"));
for (var i = 1; i <= 48; i++)
{
var span = new TimeSpan(i, 0, 0);
result.Add(Convert.ToInt32(span.TotalMinutes), GetTimeSpanInText(span));
}
TimeSpan[] additionalTimePeriods = new TimeSpan[]
{
new TimeSpan(60, 0, 0),
new TimeSpan(72, 0, 0),
new TimeSpan(7, 0, 0, 0),
new TimeSpan(14, 0, 0, 0, 0),
new TimeSpan(30, 0, 0, 0, 0)
};
foreach (TimeSpan span in additionalTimePeriods)
{
result.Add(Convert.ToInt32(span.TotalMinutes), GetTimeSpanInText(span));
}
}
else if (name == "AbandonedCartsShop")
{
result.Add("", Translate.Translate("All"));
foreach (Shop shop in Ecommerce.Services.Shops.GetShops())
{
result.Add(shop.Id, shop.Name);
}
}
else if (name == "AbandonedCartsLanguage")
{
result.Add("", Translate.Translate("All"));
foreach (Language language in Ecommerce.Services.Languages.GetLanguages())
{
result.Add(language.LanguageId, language.Name);
}
}
return result;
}
private string GetTimeSpanInText(TimeSpan span)
{
string timeText = "";
int timeValue = 0;
if (span.TotalDays >= 30)
{
timeText = "month" + (span.TotalDays > 30 ? "s" : string.Empty);
timeValue = Convert.ToInt32(span.TotalDays) / 30;
}
else if (span.TotalDays >= 7)
{
timeText = "week" + (span.TotalDays > 7 ? "s" : string.Empty);
timeValue = Convert.ToInt32(span.TotalDays) / 7;
}
else if (span.TotalHours >= 1)
{
timeText = "hour" + (span.TotalHours > 1 ? "s" : string.Empty);
timeValue = Convert.ToInt32(span.TotalHours);
}
else if (span.TotalMinutes >= 1)
{
timeText = "minute" + (span.TotalMinutes > 1 ? "s" : string.Empty);
timeValue = Convert.ToInt32(span.TotalMinutes);
}
else
{
throw new ArgumentException("TimeSpan 'span' is outside the excepted range.");
}
return string.Format("{0} {1}", timeValue, Translate.Translate(timeText));
}
}