Programmatically add multiple categories to Outlook 2010 - c#

I am trying to create multiple categories in Outlook 2010 using C#. I am able to successfully generate an executable that will create one category but when I add code to create a second category it will still only add the first one and not the second. If The first category exists it will then add the second one but it will not create both from scratch at the same time.
Below is my code. Any help is greatly appreciated.
using System;
using System.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Interop.Outlook;
namespace OutlookCategory
{
class Program
{
static void Main(string[] args)
{
AddACategory();
}
private static void AddACategory()
{
var app = new Application();
Outlook.Categories categories = app.Session.Categories;
if (!CategoryExists("TEST 1", app))
{
categories.Add("TEST 1", Outlook.OlCategoryColor.olCategoryColorDarkBlue);
}
if (!CategoryExists("TEST 2", app))
{
categories.Add("TEST 2", Outlook.OlCategoryColor.olCategoryColorDarkBlue);
}
}
private static bool CategoryExists(string categoryName, Application app)
{
try
{
Outlook.Category category =
app.Session.Categories[categoryName];
if (category != null)
{
return true;
}
else
{
return false;
}
}
catch { return false; }
}
}
}

I ended up accomplishing this by creating a pipe delimited text file with my category names and their associated category color.
I then looped through the text file creating all of the categories in Outlook.
using System;
using System.IO;
using Microsoft.Office.Interop.Outlook;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace OutlookCategory
{
class Program
{
static void Main(string[] args)
{
AddACategory();
}
private static void AddACategory()
{
string[] lines = File.ReadAllLines(#"C:\CategoryList.txt");
var app = new Application();
foreach (string line in lines)
{
string[] LineArray = line.Split('|');
var color = LineArray[1];
Outlook.Categories categories = app.Session.Categories;
//Add categories
if (CategoryExists(LineArray[0], app) == false)
{
categories.Add(LineArray[0], color);
}
}
}
private static bool CategoryExists(string categoryName, Application app)
{
try
{
Outlook.Category category =
app.Session.Categories[categoryName];
if (category != null)
{
return true;
}
else
{
return false;
}
}
catch { return false; }
}
}
}

The answer already provided did not do the trick for me. I would still get spotty results while adding and removing categories. i was able to get around this by trying the add/remove, then re-getting the categories and looping until the item was added/removed as needed.
Here is code snipped from LinqPad where I would remove all of the categories, add back in my own, then list some categories and their values. This has worked with no problems since i switched to the while/loop check. I tried a few different sleep times and it didn't seem to make much of a difference. I am guessing outlook must need to do something before the new category actually 'goes in' to the datastore. Regardless, I didn't want to spend a ton of time on this, and this worked for me. I am using outlook 2010 on windows 7. not sure if newer versions of office have this same issue.
void Main()
{
//using Microsoft.Office.Interop.Outlook
Application oApp = new Application();
NameSpace oSes = oApp.Session;
Categories oCats = oSes.DefaultStore.Categories;
//remove existing categories
var catids = (from a in oCats.Cast<Category>() select a.CategoryID).ToArray();
for (int i = 0; i < catids.Count(); i++)
{
var cid = catids[i];
cid.Dump();
while(oCats[cid] != null)
{
oCats.Remove(cid);
oCats = oSes.DefaultStore.Categories;
Thread.Sleep(100);
}
}
//dictionary to hold my categories
Dictionary<string,OlCategoryColor> dCats = new Dictionary<string,OlCategoryColor>();
dCats.Add("Category One",OlCategoryColor.olCategoryColorRed);
dCats.Add("Category Two",OlCategoryColor.olCategoryColorOrange);
dCats.Add("Category Three",OlCategoryColor.olCategoryColorPeach);
dCats.Add("Category Four",OlCategoryColor.olCategoryColorYellow);
foreach (var dCat in dCats)
{
var cid = dCat.Key;
cid.Dump();
while(oCats[cid] == null)
{
oCats.Add(cid,dCat.Value);
oCats = oSes.DefaultStore.Categories;
Thread.Sleep(100);
}
}
//show categories
var cats = from c in oCats.Cast<Category>() select new {
c.CategoryBorderColor,
c.CategoryGradientBottomColor,
c.CategoryGradientTopColor,
c.CategoryID,
c.Class,
c.Color,
c.Name,
c.ShortcutKey
};
cats.Dump();
}

Related

Can't update SalesReceipt in Quickbooks desktop

I'm using QuickBooks Integrator from /nSoftware to integrate with QuickBooks Desktop
I'm trying to update an invoice and I don't get any errors but when I check in QuickBooks I see that nothing changed and it didn't actually get updated.
First I try to lookup the invoice based on the RefNumber and if it found an Invoice then I try to replace the Line Items and then i call the update method like this existingInvoice.Update();
Here's my code sample:
public static List<Invoice> FindInvoice(string refNumber)
{
var invoicesSearch = new Objsearch
{
QueryType = ObjsearchQueryTypes.qtInvoiceSearch,
RuntimeLicense = "MYLICENSEKEY",
QBConnectionString = "MYCONNECTIONSTRINGTOREMOTECONNECTOR",
SearchCriteria = new SearchCriteria
{
RefNumberContains = refNumber
},
};
invoicesSearch.Search();
var qbInvoiceList = invoicesSearch.Results.ToList();
var invoiceObjList = new List<Invoice>();
foreach (var inv in qbInvoiceList)
{
var newInv = new Invoice();
newInv.QBResponseAggregate = inv.Aggregate;
invoiceObjList.Add(newInv);
}
return invoiceObjList.FirstOrDefault();
}
public static void PutInvoice(Invoice invoice)
{
var existingInvoice = FindInvoice(invoice.RefNumber);
if (existingInvoice != null)
{
existingInvoice.LineItems.Clear();
existingInvoice.LineItems.AddRange(invoice.LineItems);
existingInvoice.QBConnectionString = "MYCONNECTIONSTRINGTOREMOTECONNECTOR";
existingInvoice.RuntimeLicense = RuntimeLicense;
existingInvoice.QBXMLVersion = "12.0";
existingInvoice.Update(); //this line
}
}
Okay, so the issue was that I was setting the QBXMLVersion the last thing before updating.
In order for the Update() to process successfully the QBXMLVersion needs to be set the first thing.
Here's an updated working example:
public static void PutInvoice(Invoice invoice)
{
var existingInvoice = FindInvoice(invoice.RefNumber);
if (existingInvoice != null)
{
existingInvoice.QBXMLVersion = "12.0";
existingInvoice.RuntimeLicense = "MyRuntimeLicenseKey";
existingInvoice.QBConnectionString = "MYCONNECTIONSTRINGTOREMOTECONNECTOR";
existingInvoice.LineItems.Clear();
existingInvoice.LineItems.AddRange(invoice.LineItems);
existingInvoice.Update();
}
}

Acumatica ERP 6.0 Create Reports Dropdown

I have created my custom Entry and I need to add some reports to it.
I'm trying to get Reports Dropdown like this
But all my efforts are unsuccessful.
I have action and function like is in the Receipt Entry
public PXAction<MyMasterView> report;
[PXUIField(DisplayName = "Reports", MapEnableRights = PXCacheRights.Select),PXButton(SpecialType = PXSpecialButtonType.Report)]
protected virtual IEnumerable Report(PXAdapter adapter, [PXString(8, InputMask = "CC.CC.CC.CC"), PXStringList(new string[]{"PO649999","PO646000"}, new string[]{"Print My Report","Print Receipt"})] string reportID)
{
List<MyMasterView> list = adapter.Get<MyMasterView>().ToList<MyMasterView>();
if (!string.IsNullOrEmpty(reportID))
{
this.Save.Press();
int num = 0;
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (MyMasterViewcurrent in list)
{
dictionary["PARAMETER"] = current.PARAMETER;
num++;
}
if (num > 0)
{
throw new PXReportRequiredException(dictionary, reportID, string.Format("Report {0}", reportID));
}
}
return list;
}
But as a result I'm getting the following
There are a couple ways you can handle this.
One way is outlined in another question here:
Acumatica - Add additional buttons to Actions drop down to screen CT30100
The other method is to utilize a list and control it with automation steps.
If you look at the PO Receipts screen you can see this.
1) Create your button method that takes a list of other items:
public PXAction<POReceipt> report;
[PXUIField(DisplayName = "Reports", MapEnableRights = PXCacheRights.Select)]
[PXButton]
protected virtual IEnumerable Report(PXAdapter adapter,
[PXString(8, InputMask = "CC.CC.CC.CC")]
[PXStringList(new string[] { "PO646000", "PO632000", "PO622000" }, new string[] { "Purchase Receipt", Messages.ReportPOReceiptBillingDetails, Messages.ReportPOReceipAllocated })]
string reportID)
{
List<POReceipt> list = adapter.Get<POReceipt>().ToList();
if (string.IsNullOrEmpty(reportID) == false)
{
Save.Press();
int i = 0;
Dictionary<string, string> parameters = new Dictionary<string, string>();
foreach (POReceipt doc in list)
{
if (reportID == "PO632000")
{
parameters["FinPeriodID"] = (string)Document.GetValueExt<POReceipt.finPeriodID>(doc);
parameters["ReceiptNbr"] = doc.ReceiptNbr;
}
else
{
parameters["ReceiptType"] = doc.ReceiptType;
parameters["ReceiptNbr"] = doc.ReceiptNbr;
}
i++;
}
if (i > 0)
{
throw new PXReportRequiredException(parameters, reportID, string.Format("Report {0}", reportID));
}
}
return list;
}
Notice the PXStringList that has the possible values and the descriptions.
Then you can control active/inactive state from the Automation Steps.
The step you are missing in your original question is that you still need to add these buttons from the automation steps to add them to the list.

Get Outlook Appointments including recurring ones using EWS

I have implemented an SSIS Package getting all appoinments from a particular shared outlook calendar.
What I noticed is that the recurring ones are NOT returned because they are a kind of virtual. Only the Master of them will give me the ability to get also the recurring ones.
Looking at my code, do you have any suggestions how I can retrieve also the recurring ones?
I'm just a little stuck and any hint is highly appreciated!
#region Namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using Microsoft.Exchange.WebServices.Data;
#endregion
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
string sharedCalendarID;
static FolderId FindPublicFolder(ExchangeService myService, FolderId baseFolderId, string folderName)
{
// search using paging.
FolderView folderView = new FolderView(10, 0);
folderView.OffsetBasePoint = OffsetBasePoint.Beginning;
// need only DisplayName and Id of every folder
// reduce the property set to the properties
folderView.PropertySet = new PropertySet(FolderSchema.DisplayName, FolderSchema.Id);
FindFoldersResults folderResults;
do
{
folderResults = myService.FindFolders(baseFolderId, folderView);
foreach (Folder folder in folderResults)
if (String.Compare(folder.DisplayName, folderName, StringComparison.OrdinalIgnoreCase) == 0)
return folder.Id;
if (folderResults.NextPageOffset.HasValue)
// go to the next page
folderView.Offset = folderResults.NextPageOffset.Value;
}
while (folderResults.MoreAvailable);
return null;
}
public override void CreateNewOutputRows()
{
// IMPORTANT: ExchangeService is NOT thread safe, so one should create an instance of
// ExchangeService whenever one needs it.
ExchangeService myService = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
myService.Credentials = new NetworkCredential("username", "password");
myService.Url = new Uri("https://......Exchange.asmx");
// next line is very practical during development phase or for debugging
myService.TraceEnabled = true;
Folder myPublicFoldersRoot = Folder.Bind(myService, WellKnownFolderName.PublicFoldersRoot);
string myPublicFolderPath = #"CHI\WIED PFL Agenda";
string[] folderPath = myPublicFolderPath.Split('\\');
FolderId fId = myPublicFoldersRoot.Id;
foreach (string subFolderName in folderPath)
{
fId = FindPublicFolder(myService, fId, subFolderName);
if (fId == null)
{
// No Calendar found
return;
}
}
// verify
Folder folderFound = Folder.Bind(myService, fId);
if (String.Compare(folderFound.FolderClass, "IPF.Appointment", StringComparison.Ordinal) == 0)
{
sharedCalendarID = fId.ToString(); ;
}
else
return;
CalendarFolder myPublicFolder = CalendarFolder.Bind(myService,
//WellKnownFolderName.Calendar,
fId,
PropertySet.FirstClassProperties);
// search using paging. page size 10
ItemView viewCalendar = new ItemView(10);
// include all properties which we need in the view
// comment the next line then ALL properties will be read from the server.
//viewCalendar.PropertySet = new PropertySet(ItemSchema.Subject, ItemSchema.Id);
viewCalendar.Offset = 0;
viewCalendar.OffsetBasePoint = OffsetBasePoint.Beginning;
viewCalendar.OrderBy.Add(ContactSchema.DateTimeCreated, SortDirection.Descending);
FindItemsResults<Item> findResultsCalendar;
do
{
//SearchFilter searchFilter = new SearchFilter.IsGreaterThan(AppointmentSchema.Start, DateTime.Today);
var searchFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And,
new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "IPM.Appointment"),
new SearchFilter.IsGreaterThanOrEqualTo(AppointmentSchema.Start, DateTime.Now),
new SearchFilter.IsLessThan(AppointmentSchema.Start, DateTime.Now.AddDays(4)));
findResultsCalendar = myPublicFolder.FindItems(searchFilter, viewCalendar);
//get additional properties for each item returned by view, do this as view cannot return a lot of useful stuff like attendees
ServiceResponseCollection<ServiceResponse> addProperties =
myService.LoadPropertiesForItems(from Item item in findResultsCalendar select item,
new PropertySet(
BasePropertySet.IdOnly,
AppointmentSchema.Body,
AppointmentSchema.Subject,
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.IsRecurring,
AppointmentSchema.AppointmentType
));
List<Appointment> additionalProperties = new List<Appointment>(addProperties.Count);
if (addProperties != null)
{
foreach (ServiceResponse currentResponce in addProperties)
{
additionalProperties.Add(((Appointment)((GetItemResponse)currentResponce).Item));
}
}
foreach (Item item in findResultsCalendar)
{
Appointment appt = item as Appointment;
if (item is Appointment || appt.AppointmentType == AppointmentType.RecurringMaster)
{
Appointment currentAppointmentAddProps = null;
currentAppointmentAddProps = additionalProperties.Find(
delegate(Appointment arg)
{
return arg.Id == item.Id;
}
);
//convert to int wether the Appointment is recurring or not
int isRecurring = currentAppointmentAddProps.IsRecurring ? 1 : 0;
Appointment appoint = item as Appointment;
OutputRecordSetBuffer.AddRow();
OutputRecordSetBuffer.ActualEndDate = currentAppointmentAddProps.End;
OutputRecordSetBuffer.ActualStartDate = currentAppointmentAddProps.Start;
OutputRecordSetBuffer.Subject = appoint.Subject;
OutputRecordSetBuffer.EntryID = Guid.NewGuid();
//OutputRecordSetBuffer.Detail = appoint.Body.ToString();
//OutputRecordSetBuffer.CalendarID = fId.ToString();
//OutputRecordSetBuffer.AppointmentID = appoint.Id.ToString();
OutputRecordSetBuffer.CalendarID = sharedCalendarID;
OutputRecordSetBuffer.AppointmentID = Guid.NewGuid();
OutputRecordSetBuffer.IsRecurring = isRecurring;
}
}
if (findResultsCalendar.NextPageOffset.HasValue)
// go to the next page
viewCalendar.Offset = findResultsCalendar.NextPageOffset.Value;
}
while (findResultsCalendar.MoreAvailable);
}
}
I have solved my issue on my own :)
Instead of using the ItemView class, I used the CalendarView class.
CalendarView expands also the recurring appointments, thats it :)

Saving windows form listbox to a text file C#

I'm trying to save the contents of a listbox into a text file. and it works, but instead of the text entered into the list box, I get this:
System.Windows.Forms.ListBox+ObjectCollection
Here is the relevant code I'm using for the form itself.
listString noted = new listString();
noted.newItem = textBox2.Text;
listBox1.Items.Add(textBox2.Text);
var radioOne = radioButton1.Checked;
var radioTwo = radioButton2.Checked;
var radioThree = radioButton3.Checked;
if (radioButton1.Checked == true)
{
using (StreamWriter sw = new StreamWriter("C:\\windowsNotes.txt"))
{
sw.Write(listBox1.Items);
}
}
else if (radioButton2.Checked == true)
{
using (StreamWriter sw = new StreamWriter("C:\\Users\\windowsNotes.txt"))
{
sw.Write(listBox1.Items);
}
}
else if (radioButton3.Checked == true)
{
using (StreamWriter sw = new StreamWriter("../../../../windowsNotes.txt"))
{
sw.Write(listBox1.Items);
}
}
else
{
MessageBox.Show("Please select a file path.");
}
}
The class is just a simple one:
namespace Decisions
{
public class listString
{
public string newItem {get; set;}
public override string ToString()
{
return string.Format("{0}", this.newItem);
}
}
}
You cannot just do
sw.Write(listBox1.Items);
as it's calling .ToString() on the collection object itself.
Try something like:
sw.Write(String.Join(Environment.NewLine, listBox1.Items));
Or loop through each item and ToString the individual item.
You will have to write the items one by one:
using (StreamWriter sw = new StreamWriter("C:\\windowsNotes.txt") {
foreach (var item in listBox1.Items) {
sw.WriteLine(item.ToString());
}
}
You're writing the ToString of the collection to the output stream rather than of the elements of the collection. Iterating over the collection and outputting each one individually would work, and I'm sure there there's a succint Linq (or even more obvious) way of doing that.

Dynamic Data Custom Field Template updating a list

I have a website using dynamic data and linq to sql. This website runs 3 'subsites' and has a list of categories with a many to many relationship.
I have 3 tables and hence 3 objects in my dbml; Website, Categories, and CategoriesToWebsites
What I am trying to do is create a field template such that on my Categories/Edit.aspx page I can edit a category and specify which website the category belongs in.
The field template is CategoriesToWebsites_Edit.ascx, which is basically a checkbox list bound to the list of websites.
Code below:
public partial class CategoriesToWebsitesEdit : FieldTemplateUserControl
{
protected override void OnLoad(EventArgs e)
{
var dataSource = (LinqDataSource)this.FindDataSourceControl();
dataSource.Inserting += OnInserting;
dataSource.Updating += OnUpdating;
}
private void OnUpdating(object sender, LinqDataSourceUpdateEventArgs e)
{
var newCategory = (Category)e.NewObject;
var oldCategory = (Category)e.OriginalObject;
foreach(var listItem in WebsiteList.Items.Cast<ListItem>())
{
//check if website category already exists
var categoryToWebsite = oldCategory.CategoriesToWebsites.FirstOrDefault(x => x.WebsiteId == Convert.ToInt32(listItem.Value));
//website category exists
if (categoryToWebsite != null)
{
// check if selected for removal, remove
if (!listItem.Selected)
{
newCategory.CategoriesToWebsites.Remove(categoryToWebsite);
}
}
//we want to insert
if (listItem.Selected)
{
//website category does not exist, add
if (categoryToWebsite == null)
{
//add selected website if not already exists
newCategory.CategoriesToWebsites.Add(new CategoriesToWebsite
{
WebsiteId = Convert.ToInt32(listItem.Value)
});
}
}
}
}
private void OnInserting(object sender, LinqDataSourceInsertEventArgs e)
{
var category = (Category)e.NewObject;
foreach(var listItem in WebsiteList.Items.Cast<ListItem>())
{
if(!listItem.Selected)
continue;
category.CategoriesToWebsites.Add(new CategoriesToWebsite
{
WebsiteId = Convert.ToInt32(listItem.Value)
});
}
}
protected override void OnDataBinding(EventArgs e)
{
var websiteRepository = new WebsiteRepository();
var websites = websiteRepository.GetAll();
var websiteCategories = (IEnumerable<CategoriesToWebsite>)FieldValue;
foreach(var website in websites)
{
var currentWebsite = website;
var listItem = new ListItem(website.Name, website.Id.ToString())
{
Selected = websiteCategories == null ? false : websiteCategories.Any(w => w.WebsiteId == currentWebsite.Id)
};
WebsiteList.Items.Add(listItem);
}
}
}
When I go to Categories/Insert.aspx to create a new category, it runs through the OnInserting code fine and saves it to db just fine, everything seems to be working here.
On Categories/Edit.aspx it goes through the code just as I expect, but does not seem to save anything.
What am I missing? - I'm not too familiar with Dynamic Data Field Templates so any guidance will be much appreciated
Apparently I was going about this slightly wrong. I was simply updating the object in the linq data source, which wasn't being saved. So instead I go straight to the repository:
private void OnUpdating(object sender, LinqDataSourceUpdateEventArgs e)
{
var newCategory = (Category)e.NewObject;
var oldCategory = (Category)e.OriginalObject;
var repository = new Repository<CategoriesToWebsite>();
var ctw = repository.GetAll().Where(x => x.CategoryId == newCategory.Id);
foreach (var listItem in WebsiteList.Items.Cast<ListItem>())
{
var current = ctw.FirstOrDefault(x => x.WebsiteId == Convert.ToInt32(listItem.Value));
//current categoriesToWebsite exists
if (current != null)
{
//if not selected, remove
if (!listItem.Selected)
repository.Delete(current);
}
//does not exist
else
{
//if selected, add
if (listItem.Selected)
repository.Save(new CategoriesToWebsite()
{
CategoryId = newCategory.Id,
WebsiteId = Convert.ToInt32(listItem.Value)
}
);
}
}
UnitOfWork.Current.SubmitChanges();
}
I'm not sure if this is the proper way to do this since the field template here is doing some updating directly to the db. But it works.

Categories