This sample code will get all orders processed from eBay and Amazon that have been processed more than 10 days ago and less than 1 month ago. The script then sends "Please leave feedback" email to the customer and records an email as sent in the audit trail. The initial selection will not include the orders where "Please Leave Feedback" email has already been sent.
/*Email Client configuration. Here you will need to specify Server, user name and password for your SMTP server, and also testing tag*/ string SMTPServer = "smtp.gmail.com"; int SMTPPort = 25; string UserName = "someuser@linnsystems.com"; string Password = "somepassword"; string FromEmail = "someuser@linnsystems.com"; string FromName = "Some user"; //email subject and email body string Subject = "Please Leave feedback for your [{Source}] order from SUPER COOL STORE"; string eBayEmailBody = "Hello, [{Name}].\r\n\r\nPlease leave feedback for the items you have purchase from us\r\n\r\n[{ItemList}]\r\nThanks a bunch"; /*query syntax. Here we will select all orders processed 10 days ago but not older than 1 month, that don't have FEEDBACK email sent and order item table. Two queries in one request */ string query =@"SELECT pkOrderId,cEmailAddress, cFullName, [Source] FROM [Order] o LEFT OUTER JOIN Order_LifeHistory ls on ls.fkOrderId = o.pkOrderID and ls.fkOrderHistoryTypeId='EMAIL_SENT' and ls.Tag='FEEDBACK' WHERE o.bProcessed = 1 AND o.HoldOrCancel=0 AND o.dProcessedOn BETWEEN DATEADD(M,-1,GETDATE()) AND DATEADD(D,-10,GETDATE()) AND ls.sid_history is null AND o.Source IN ('DIRECT','EBAY','AMAZON'); SELECT pkOrderId,ItemNumber = oi.ItemNumber,ItemTitle = sis.cItemName FROM [Order] o INNER JOIN [OrderItem] oi on oi.fkOrderID = o.pkOrderID INNER JOIN [StockItems] sis on sis.pkStockID = oi.fkStockID LEFT OUTER JOIN Order_LifeHistory ls on ls.fkOrderId = o.pkOrderID and ls.fkOrderHistoryTypeId='EMAIL_SENT' and ls.Tag='FEEDBACK' WHERE o.bProcessed = 1 AND o.HoldOrCancel=0 AND o.dProcessedOn BETWEEN DATEADD(M,-1,GETDATE()) AND DATEADD(D,-10,GETDATE()) AND ls.sid_history is null AND o.Source IN ('DIRECT','EBAY','AMAZON');"; //list of order ids - here we will put all orderids for which emails have been successfully sent List<Guid> emailSent = new List<Guid>(); //list of order ids - here we will put all orderids for which email address was invalid List<Guid> invalidEmail = new List<Guid>(); //exectue data query and get two tables [order] and [orderitems] List<CodeHelper.DataQueryParameter> par = new List<CodeHelper.DataQueryParameter>(); DataSet ds = CodeHelper.ExecuteDataQuerySet(query, par,new string[]{"order","orderitems"},ActiveConnection); int count = 0; //iterate through all orders in the table foreach(DataRow row in ds.Tables["order"].Rows){ count+=1; debug.Progress((int)((double)count/(double)ds.Tables["order"].Rows.Count * 100),"Sending Feedback request email to "+ row["cEmailAddress"].ToString()); //check if it has valid email address if (CodeHelper.IsValidEmail(row["cEmailAddress"].ToString())){ //lets build orderitems text by itterating through all order items and find all items belonging to the selected order string orderitems = ""; foreach(DataRow itemRow in ds.Tables["orderitems"].Rows) if ((Guid)itemRow["pkOrderId"]==(Guid)row["pkOrderId"]) // check the order item row belongs to the order { // add order item text to the string orderitems+=(orderitems!="" ? "\r\n":"")+itemRow["ItemNumber"].ToString()+" - " + itemRow["ItemTitle"].ToString(); } // do a bit of tag replacing string replacedeBayEmailBody= eBayEmailBody.Replace("[{Name}]",row["cFullName"].ToString()); replacedeBayEmailBody= replacedeBayEmailBody.Replace("[{ItemList}]",orderitems); string replacedSubject = Subject.Replace("[{Source}]",row["Source"].ToString()); // just a debuging line to see what we are outputting debug.AddEntry(row["cEmailAddress"].ToString()+" "+ replacedeBayEmailBody); try{ //Send email using SendEmailNow command in the commondata. // UNCOMMENT WHEN YOU ARE READY TO SEND EMAILS //Email.SendEmailNow(SMTPServer,SMTPPort,UserName,Password,true,FromEmail,FromName,row["cEmailAddress"].ToString(),row["cFullName"].ToString(), replacedSubject,replacedeBayEmailBody); //add order id to the list of successfuly sent emails emailSent.Add((Guid)row["pkOrderId"]); }catch(Exception ex){ } }else{ // put order id into the list of invalid email addresses invalidEmail.Add((Guid)row["pkOrderId"]); debug.AddEntry("Invalid email " + row["cEmailAddress"].ToString()); } } /*We now sent out all the emails and need to log the fact that the emails have now been sent for all these orders. This will prevent the script resending emails again. We do this by adding Audit trail tag EMAIL_SENT This needs to be done in a batch to limit the number of queries we do on the server. OrderData.AddOrderLifeHistoryBatch - does exactly that */ debug.Progress(100,"Logging EMAIL_SENT"); debug.AddEntry("Batch history update for EMAIL_SENT successfully for " + emailSent.Count.ToString()); if (emailSent.Count>0){ OrderData.AddOrderLifeHistoryBatch(ActiveConnection,emailSent.ToArray(),"EMAIL_SENT","Feedback request email sent","","FEEDBACK","Macro Script"); } debug.AddEntry("Batch history update for EMAIL_SENT unsuccessful for " + invalidEmail.Count.ToString()); if (invalidEmail.Count>0){ OrderData.AddOrderLifeHistoryBatch(ActiveConnection,invalidEmail.ToArray(),"EMAIL_SENT","Feedback: Email is invalid","","FEEDBACK","Macro Script"); }Email customers who's order is unprocessed and is 3 days old or older
This sample code will get all unprocessed orders that are 3 days old or older. We then email the customer to inform then that the order is late and record the email sent in the audit trail. The initial selection will not include the orders where "LATE3DAYS" email has already been sent.
/*Email Client configuration. Here you will need to specify Server, user name and password for your SMTP server, and also testing tag*/
string SMTPServer = "smtp.gmail.com";
int SMTPPort = 25;
string UserName = "someuser@linnsystems.com";
string Password = "somepassword";
string FromEmail = "someuser@linnsystems.com";
string FromName = "Some user";
//email subject and email body
string Subject = "Your order is running late for your [{Source}] order from SUPER COOL STORE";
string eBayEmailBody = "Hello, [{Name}].\r\n\r\nWe regret to inform you that the items you have purchased from us are currently late\r\n\r\n[{ItemList}]\r\nWe will contact your shortly when we are able to fully process your order";
/*query syntax.Here we will select all open orders 3 days, that don't have LATE3DAYS email sent
and order item table. Two queries in one request
*/
string query =@"SELECT pkOrderId,cEmailAddress, cFullName, [Source]
FROM [Order] o
LEFT OUTER JOIN Order_LifeHistory ls on ls.fkOrderId = o.pkOrderID AND ls.fkOrderHistoryTypeId='EMAIL_SENT' AND ls.Tag='LATE3DAYS'
WHERE
o.bProcessed = 0 AND o.HoldOrCancel=0 AND o.dReceievedDate <= DATEADD(d,-3,GETDATE()) AND ls.sid_history is null;
SELECT pkOrderId,ItemNumber = oi.ItemNumber,ItemTitle = sis.cItemName
FROM [Order] o
INNER JOIN [OrderItem] oi on oi.fkOrderID = o.pkOrderID
INNER JOIN [StockItems] sis on sis.pkStockID = oi.fkStockID
LEFT OUTER JOIN Order_LifeHistory ls on ls.fkOrderId = o.pkOrderID AND ls.fkOrderHistoryTypeId='EMAIL_SENT' AND ls.Tag='LATE3DAYS'
WHERE
o.bProcessed = 0 AND o.HoldOrCancel=0 AND o.dReceievedDate <= DATEADD(d,-3,GETDATE()) AND ls.sid_history is null";
//list of order ids - here we will put all orderids for which emails have been successfully sent
List emailSent=new List();
//list of order ids - here we will put all orderids for which email address was invalid
List invalidEmail =new List();
//exectue data query and get two tables [order] and [orderitems]
DataSet ds = CodeHelper.ExecuteDataQuerySet(query,new List(),new string[]{"order","orderitems"},ActiveConnection);
int count = 0;
//iterate through all orders in the table
foreach(DataRow row in ds.Tables["order"].Rows){
count+=1;
debug.Progress((int)((double)count/(double)ds.Tables["order"].Rows.Count * 100),"Sending 3 Day Overdue email to "+ row["cEmailAddress"].ToString());
//check if it has valid email address
if (CodeHelper.IsValidEmail(row["cEmailAddress"].ToString())){
//lets build orderitems text by itterating through all order items and find all items belonging to the selected order
string orderitems = "";
foreach(DataRow itemRow in ds.Tables["orderitems"].Rows)
if ((Guid)itemRow["pkOrderId"]==(Guid)row["pkOrderId"]) // check the order item row belongs to the order
{
// add order item text to the string
orderitems+=(orderitems!="" ? "\r\n":"")+itemRow["ItemNumber"].ToString()+" - " + itemRow["ItemTitle"].ToString();
}
// do a bit of tag replacing
string replacedeBayEmailBody= eBayEmailBody.Replace("[{Name}]",row["cFullName"].ToString());
replacedeBayEmailBody= replacedeBayEmailBody.Replace("[{ItemList}]",orderitems);
string replacedSubject = Subject.Replace("[{Source}]",row["Source"].ToString());
// just a debuging line to see what we are outputting
debug.AddEntry(row["cEmailAddress"].ToString()+" "+ replacedeBayEmailBody);
try{
//Send email using SendEmailNow command in the commondata.
// UNCOMMENT WHEN YOU ARE READY TO SEND EMAILS
//Email.SendEmailNow(SMTPServer,SMTPPort,UserName,Password,true,FromEmail,FromName,row["cEmailAddress"].ToString(),row["cFullName"].ToString(), replacedSubject,replacedeBayEmailBody);
//add order id to the list of successfuly sent emails
emailSent.Add((Guid)row["pkOrderId"]);
}catch(Exception ex){
}
}else{
// put order id into the list of invalid email addresses
invalidEmail.Add((Guid)row["pkOrderId"]);
debug.AddEntry("Invalid email " + row["cEmailAddress"].ToString());
}
}
/*We now sent out all the emails and need to log the fact that the emails have now been sent for all these orders.
This will prevent the script resending emails again. We do this by adding Audit trail tag EMAIL_SENT
This needs to be done in a batch to limit the number of queries we do on the server.
OrderData.AddOrderLifeHistoryBatch - does exactly that
*/
debug.Progress(100,"Logging EMAIL_SENT");
debug.AddEntry("Batch history update for EMAIL_SENT successfully for " + emailSent.Count.ToString());
if (emailSent.Count>0){
OrderData.AddOrderLifeHistoryBatch(ActiveConnection,emailSent.ToArray(),"EMAIL_SENT","Order 3 Days Late Email sent","","LATE3DAYS","Macro Script");
}
debug.AddEntry("Batch history update for EMAIL_SENT unsuccessful for " + invalidEmail.Count.ToString());
if (invalidEmail.Count>0){
OrderData.AddOrderLifeHistoryBatch(ActiveConnection,invalidEmail.ToArray(),"EMAIL_SENT","Order 3 Days Late: Email is invalid","","LATE3DAYS","Macro Script");
}Import Stock Levels from supplier FTP
This sample code will download an inventory file from FTP and update stock levels. It checks the modified date of the last downloaded version with the modified date of the current version on the Ftp. If Ftp version is newer then download and update will be actioned
Select Settings | Macros | Add New
Use the sample code below and update the settings to suit your FTP
Give the script a name eg DownloadInventoryLevelsFromFtp
Click Save
Click Save againThe macro will run on Sync, but only download when required.
namespace linnworks.finaware.CommonData // leave untouched
{ // leave untouched
public class ScriptMacroClass : linnworks.scripting.core.IMacroScript // leave untouched
{
public void Initialize(linnworks.scripting.core.Debugger debug, System.Data.SqlClient.SqlConnection ActiveConnection) // leave untouched
{ // leave untouched
// this macro will download an inventory file from FTP and update stock levels
// it checks the modified date of the last downloaded version with the modified date of the current version on the Ftp
// if Ftp version is newer then download and update will be actioned
// there are various settings that need to be set to make this macro work
// Each of these has the following Wrapper to make them easier to find
//******* User Settings Required ~ Start
//******* User Settings Required ~ Finish
// create a query to retrieve the time stamp based on the last time we imported
// check first to see if the setting exists and if not add it
string query = @"IF NOT EXISTS (SELECT top 1 1 FROM AppSettings WHERE settingCategory = 'TIMESTAMP' and settingname = 'INVENTORY_FILE_CREATED_TIME')
BEGIN
INSERT INTO AppSettings(settingCategory, settingname, settingValue)
VALUES ('TIMESTAMP','INVENTORY_FILE_CREATED_TIME',GETDATE()-30)
END
SELECT settingValue
FROM AppSettings
WHERE settingCategory = 'TIMESTAMP' and settingname = 'INVENTORY_FILE_CREATED_TIME'";
// use active connection to the Linnworks database
using (ActiveConnection)
{
// run the query
debug.AddEntry("Run query to get date");
SqlCommand cmdGetDate = new SqlCommand(query, ActiveConnection);
object objLastModified = cmdGetDate.ExecuteScalar();
// make sure we have something returned from the query
if (objLastModified != null)
{
debug.AddEntry("Query worked");
debug.AddEntry("Last downloaded file date = " + objLastModified.ToString());
// assign object retrun value to a local datetime variable
DateTime date = Convert.ToDateTime (objLastModified);
//******* User Settings Required ~ Start
// set FTP variables, edit these to suit your FTP server settings
debug.AddEntry("Set Ftp params");
string ftpPath = "*******************"; // enter full path eg ftp://My.Supplier.com/EndOfDayReports/
string ftpUser = "*******************"; // enter ftp User Name
string ftpPassword = "*******************"; // enter ftp password
string ftpFilename = "*******************"; // enter file name eg "inventory.csv";
// Linnworks location,
string sLocation = "Default"; // enter Linnworks Location to suit where Stock Levels need to be updated
//******* User Settings Required ~ Finish
// retrieve the modified date for the file on the ftp
debug.AddEntry("Check modified date");
DateTime modifiedDate = CheckModifiedDateTime(ftpPath, ftpUser, ftpPassword, false, ftpFilename);
debug.AddEntry(" File modified Date = " + modifiedDate);
// compare the date in the database with the modified date
if (modifiedDate > date)
{
debug.AddEntry("File modified, download required");
// create a list to contain the items to update
List<linnworks.finaware.CommonData.FulfilmentCenter.BatchStockLevelUpdateItem> items = new List<linnworks.finaware.CommonData.FulfilmentCenter.BatchStockLevelUpdateItem>();
// download the file from the Ftp server
debug.AddEntry("Start Download of file " + ftpFilename );
string file = DownloadFileFromFTP(ftpPath, ftpUser, ftpPassword, false, ftpFilename);
debug.AddEntry(" Download Complete" );
// initialise the settings for the format of the flat file
// these must be set to match the format of the file being downloaded
debug.AddEntry("Initialise Flat File settings" );
linnworks.finaware.CommonData.Classes.Generic.FlatFileSettings settings = new linnworks.finaware.CommonData.Classes.Generic.FlatFileSettings();
settings.ColumnHeaders = true;
settings.delimiter = ",";
settings.textseparator = "\"";
settings.UTF8 = false;
// convert the downloaded file to a database table
debug.AddEntry("Start Convert flat file to table");
DataTable tbl = linnworks.finaware.CommonData.FlatFileWorker.FlatFileToTable(settings, file);
debug.AddEntry(" Convert Complete");
// iterate each row of the table and read the SKU/quantity values
debug.AddEntry("Start adding SKU's/Quantities to List");
foreach (DataRow row in tbl.Rows)
{
string sku = row["SKU"].ToString();
int quantity = 0;
try
{
quantity = Convert.ToInt32(row["quantity"].ToString());
}
catch
{
quantity = int.Parse(row["quantity"].ToString());
}
if (sku != "")
{
// if we have sku add it to the list for updating linnworks
items.Add(new linnworks.finaware.CommonData.FulfilmentCenter.BatchStockLevelUpdateItem(sku, quantity));
}
}
debug.AddEntry(" Finish adding SKU's/Quantities");
// find guid for required Location
Guid locationId = linnworks.finaware.CommonData.Locations.GetLocationIdByName(ActiveConnection, sLocation);
// now do the actual stock update
debug.AddEntry("Start update");
linnworks.finaware.CommonData.FulfilmentCenter.BatchStockLevelUpdate(ActiveConnection, locationId, items);
debug.AddEntry(" Finish update");
// update the date in App Settings to match the modified date of the import file
debug.AddEntry("Start AppSettings update");
debug.AddEntry("Update TIMESTAMP / INVENTORY_FILE_CREATED_TIME to " + modifiedDate.ToString());
string updateQuery = @" update AppSettings
SET settingValue = '" + modifiedDate.ToString() + @"'
WHERE settingCategory = 'TIMESTAMP' and settingname = 'INVENTORY_FILE_CREATED_TIME'";
SqlCommand cmdUpdateDate = new SqlCommand(updateQuery, ActiveConnection);
cmdUpdateDate.ExecuteNonQuery();
debug.AddEntry(" Finish AppSettings update");
}
else
{
debug.AddEntry("File NOT modified, no download required");
}
}
}
} // leave untouched
// check the modified Date/Time of a file on an Ftp server
static DateTime CheckModifiedDateTime(string FTPPath, string Username, string Password, bool Active, string filename)
{
DateTime ret = DateTime.Now;
try
{
System.Net.FtpWebRequest requestDown = (System.Net.FtpWebRequest)System.Net.WebRequest.Create(FTPPath + "/" + filename);
requestDown.Method = System.Net.WebRequestMethods.Ftp.GetDateTimestamp;
requestDown.Credentials = new System.Net.NetworkCredential(Username, Password);
using (System.Net.FtpWebResponse resp = (System.Net.FtpWebResponse)requestDown.GetResponse())
{
ret = resp.LastModified;
}
}
catch (Exception ex)
{
throw new Exception("CheckModifiedDateTime " + ex.Message);
}
return ret;
}
// download a file from an Ftp server
public static string DownloadFileFromFTP(string FTPPath, string Username, string Password, bool Active, string filename)
{
string step = "";
try
{
step = "Create Download Request " + FTPPath + "/" + filename;
System.Net.FtpWebRequest requestDown = (System.Net.FtpWebRequest)System.Net.WebRequest.Create(FTPPath + "/" + filename);
requestDown.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
requestDown.Credentials = new System.Net.NetworkCredential(Username, Password);
requestDown.UsePassive = !Active;
step = "GetResponse Download " + FTPPath + "/" + filename;
System.Net.FtpWebResponse responseDownload = (System.Net.FtpWebResponse)requestDown.GetResponse();
step = "Open Download stream " + FTPPath + "/" + filename;
System.IO.Stream responseStream = responseDownload.GetResponseStream();
step = "Read file " + FTPPath + "/" + filename;
System.IO.StreamReader reader = new System.IO.StreamReader(responseStream);
string downloadedFile = reader.ReadToEnd();
try
{
reader.Close();
responseStream.Close();
responseDownload.Close();
}
catch { }
return downloadedFile;
}
catch (Exception ex)
{
throw new Exception("Download file from FTP " + ex.Message);
}
}
} // leave untouched
} // leave untouched