#define PEACHW13//For now using lowest common denominator pt 2006 using System; using System.Windows.Forms; using GZTW.AyaNova.BLL; using System.Data; using System.Drawing; using System.Reflection; using System.Collections; using System.Text; using System.Threading; using System.Runtime.InteropServices; using System.Xml; using PeachtreeAccounting; using System.Text.RegularExpressions; namespace AyaNova.PlugIn.PTI { /// /// Summary description for Util. /// public class Util { public enum pfstat { OK = 0, Failed = 1, Cancel = 2 } #region Attributes and properties //case 952 public static System.Resources.ResourceManager AyaResource = null; public static Global GlobalSettings = null; //public static GZTW.AyaNova.BLL.Region RegionalSettings = null; public static LocalizedTextTable LocaleText = null; public static Integration PTI = null; public static PTIDataEx PDat = null; public static string QCountry = "US"; public static double QBVersion = 1.1; public static string QCompanyFile = ""; public static string sLastRequestXML = ""; public static string PTCompany = ""; public static string PTVersion = ""; public static string PTCompanyGUID = ""; public static string PTPath = ""; //For future in case need to specify //a particular storage location for temp files //re: security public static string TempFilePath { get { return System.IO.Path.GetTempPath(); } } //flagged as false if any import or export exceptions thrown //used by mainform close method to determine of xml files //should be deleted or not at exit public static bool ErrorFree = true; //Used to track if wo should be closed even if error //as some exceptions still result in a invoice during invoicing public static bool invoiceGenerated = true; public static Connect ptApp = null; /// /// The official and unchanging integration ID for /// PTI ({0B937F64-7BC4-4b7d-A1CF-46BD14F49918}) /// public static Guid PTID { get { return new Guid("{0B937F64-7BC4-4b7d-A1CF-46BD14F49918}"); } } #endregion static Util() { } #region General helper methods //case 952 static public Image AyaImage(string sImageResourceName) { if (AyaResource == null) return new Bitmap(10, 10);//this is strictly for design mode because forms based on setbase throw exception during design return (Image)AyaResource.GetObject(sImageResourceName); } static public Icon AyaIcon(string sImageResourceName) { return (Icon)AyaResource.GetObject(sImageResourceName); } static public void OpenWebURL(object oUrl) { if (oUrl == null) return; string sUrl = oUrl.ToString(); if (sUrl == "") return; if (sUrl.ToLower().StartsWith("http://")) System.Diagnostics.Process.Start(sUrl); else System.Diagnostics.Process.Start("http://" + sUrl); } /// /// Invert a color so it's readable against the passed /// in color /// /// /// static public Color InvertColor(Color col) { // if(log.IsDebugEnabled) // //case 1039 //log.Debug("InvertColor("+col.ToString()+")"); int nSourceColor = col.R + col.G + col.B; int r = 255 - col.R; int g = 255 - col.G; int b = 255 - col.B; int nInvertColor = r + g + b; Color invert; if (nSourceColor - nInvertColor < 28) invert = Color.White; else invert = Color.FromArgb(r, g, b); return invert; } #endregion #region Pre flight check /// /// Pre flight check, prompt user as required to repair /// or add missing settings / failed ones /// /// This method is called AFTER login. /// /// public static pfstat PreFlightCheck() { //Go through each item sequentially, keep calling it //while return status if fail, this way user keeps at it //until the situation is corrected or cancels //Peachtree initial connection here try { Connect ptApp = new Connect(); bool bOpen = ptApp.app.CompanyIsOpen; } catch (COMException e) { MessageBox.Show(e.Message); return pfstat.Failed; } //===================================================== if (PTValidate() == pfstat.Cancel) { IntegrationLog.Log(PTID, "PFC: Unable to validate Peachtree connection, user selected cancel"); return pfstat.Cancel; } else { IntegrationLog.Log(PTID, "PFC: PT validated PTVersion=" + PTVersion + ", Company=" + PTCompany); PopulatePTListCache(); PopulateAyaListCache(); } IntegrationObjectCheck(); if (ValidateSettings(false) == pfstat.Cancel) { IntegrationLog.Log(PTID, "PFC: User settings not completed, user selected cancel"); return pfstat.Cancel; } //Added: 18-Nov-2006 CASE 163 //check that linked items in integration map exist in PT if (PTI.Maps.Count == 0) return pfstat.OK; //Missing links table: DataTable dtTemp = new DataTable(); dtTemp.Columns.Add("MAPID", typeof(Guid)); dtTemp.Columns.Add("Name", typeof(string)); bool present = true; foreach (IntegrationMap m in PTI.Maps) { present = true; switch (m.RootObjectType) { case RootObjectTypes.Client: present = PTClients.Rows.Contains(m.ForeignID); break; case RootObjectTypes.Vendor: present = PTVendors.Rows.Contains(m.ForeignID); break; case RootObjectTypes.Rate: case RootObjectTypes.Part: present = PTItems.Rows.Contains(m.ForeignID); break; } if (!present) dtTemp.Rows.Add(new object[] { m.ID, m.RootObjectType.ToString() + ": " + m.Name }); } if (dtTemp.Rows.Count > 0) { if (dtTemp.Rows.Count == PTI.Maps.Count) { //None of the items mapped match offer to remove them all #region Nothing matches IntegrationLog.Log(PTID, "PFC: No integration maps match PT database objects!"); DialogResult dr = MessageBox.Show("None of the mapped items in AyaNova were found in the \r\n" + "Currently open Peachtree database.\r\n" + "It's possible you have the wrong database open.\r\n\r\n" + "Do you want to remove all mappings from AyaNova?", "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3); if (dr == DialogResult.Yes) { dr = MessageBox.Show("If you select YES all mappings will be removed from AyaNova.", "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3); if (dr == DialogResult.Yes) { IntegrationLog.Log(PTID, "PFC: User opted to remove all mappings after double warning."); foreach (DataRow row in dtTemp.Rows) { PTI.Maps.Remove(row["MAPID"].ToString()); } PTI = (Integration)PTI.Save(); return pfstat.Cancel; } } #endregion } else { //some items match so iterate them and offer to delete one by one IntegrationLog.Log(PTID, "PFC: Some integration maps do not match PT database objects"); foreach (DataRow row in dtTemp.Rows) { DialogResult dr = MessageBox.Show("Linked object: " + row["Name"].ToString() + "\r\n" + "Is missing or set Inactive in Peachtree.\r\n\r\nRemove it's link from AyaNova?", "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button3); if (dr == DialogResult.Cancel) return pfstat.Cancel; if (dr == DialogResult.Yes) PTI.Maps.Remove(row["MAPID"].ToString()); } PTI = (Integration)PTI.Save(); } } return pfstat.OK; } #region PFC AyaNova side /// /// Check if integration object is present in database, if not then /// add it. /// /// public static pfstat IntegrationObjectCheck() { if (PTI == null) { if (Integration.IntegrationExists(PTID)) { PTI = Integration.GetItem(PTID); //Get the Peachtree settings object //Block below completely modified for //Case 299 PDat = new PTIDataEx(); if (PTI.AIObject == null || PTI.AIObject.ToString() == "") { PTI.AIObject = PDat.XMLData; PTI = (Integration)PTI.Save(); } else { //All normal, parse xml and move on PDat.XMLData = (string)PTI.AIObject; } } else { PTI = Integration.NewItem(PTID); PTI.Active = true; PTI.AppVersion = "3.x"; PTI.Name = "AyaNova PTI - Peachtree integration"; //Case 299 PDat = new PTIDataEx(); PTI.AIObject = PDat.XMLData; PTI = (Integration)PTI.Save(); IntegrationLog.Log(PTID, "PFC: PTI Integration object created"); } } return pfstat.OK; } #endregion #region PFC PT side /// /// Open Peachtree connection /// gather info required for future /// transactions /// public static pfstat PTValidate() { // We want to know if we begun a session so we can end it if an // error happens bool bSessionBegun = false; while (!bSessionBegun) { try { ptApp = new Connect(); if (!ptApp.app.CompanyIsOpen) { if (MessageBox.Show( "Peachtree doesn't appear to be running with a company file open on this computer.\r\n" + "Start Peachtree (if it hasn't started already) and open your company file now before proceeding.", "AyaNova PTI: Pre flight check", MessageBoxButtons.RetryCancel, MessageBoxIcon.Information) == DialogResult.Cancel) return pfstat.Cancel; } else bSessionBegun = true; } catch (System.Runtime.InteropServices.COMException ex) { IntegrationLog.Log(PTID, "PFC: PTValidate connect unanticipated exception: " + ex.Message); MessageBox.Show(ex.Message); return pfstat.Cancel; } } PTCompany = ptApp.app.CurrentCompanyName; PTVersion = ptApp.app.ProductVersion; PTCompanyGUID = ptApp.app.CurrentCompanyGUID; PTPath = ptApp.app.CompanyPath; return pfstat.OK; } #endregion #region PFC Check if user settings are completed and valid /// /// Validate the users preferences /// if any are missing or invalid prompt for them /// /// public static pfstat ValidateSettings(bool ForceReset) { bool SetEverything = false; if (ForceReset) SetEverything = true; //Display user friendly dialog //explaining that configuration needs to be set if (PDat.NothingSet) { SetEverything = true; MessageBox.Show( "AyaNova PTI has now connected sucessfully to both AyaNova and Peachtree.\r\n\r\n" + "The next step is to set preferences for how AyaNova PTI will operate.\r\n\r\n" + "AyaNova PTI will now step through each setting and get your preference\r\n" + "in order to integrate AyaNova with Peachtree.\r\n\r\n" + "These settings can be changed later.", "Setup wizard", MessageBoxButtons.OK, MessageBoxIcon.Information); } #region confirm company file ApproveCompanyFile s0 = new ApproveCompanyFile(); s0.PTCompanyName = PTCompany.Replace("&", "&&"); s0.PTCompanyPath = PTPath; if (s0.ShowDialog() == DialogResult.Cancel) { IntegrationLog.Log(PTID, "PFC: User cancelled when shown company file currently open - " + QCompanyFile); return pfstat.Cancel; } #endregion #region WO Pre status //Validate any existing status if (SetEverything == false && PDat.PreWOStatus != Guid.Empty) { if (WorkorderStatus.Exists(PDat.PreWOStatus)) goto PRESTATUSOK; } else { //Empty pre status is valid if not first //time setup as user can opt for selecting //workorders of any status if (SetEverything == false) goto PRESTATUSOK; } //We've arrived here because there is no valid setting for Pre workorder status //or it's the first time through and needs to be selected on way or another SetWOStatus s1 = new SetWOStatus(); s1.DialogTitle = "AyaNova PTI setup - Choose billable Workorder Status"; s1.OptionTitle = "Billable workorder status"; s1.OptionDescription = "One of AyaNova PTI's tasks is to look for work orders in AyaNova \r\n" + "that are ready to be billed out and put them in a list for your selection. \r\n" + " \r\n" + "By default PTI will consider work orders that are set to service completed \r\n" + "and are not closed as it's selection criteria. \r\n" + " \r\n" + "In addition, you can further refine the types of work orders that PTI \r\n" + "considers ready for billing by specifying here a particular workorder Status \r\n" + "you want to include in addition to the default criteria. "; s1.SelectedStatus = PDat.PreWOStatus; s1.PreStatus = true; if (s1.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else PDat.PreWOStatus = s1.SelectedStatus; PRESTATUSOK: #endregion #region WO POST status //Validate any existing status if (SetEverything == false && PDat.PostWOStatus != Guid.Empty) { if (WorkorderStatus.Exists(PDat.PostWOStatus)) goto POSTSTATUSOK; } else { //Empty post status is valid if not first //time setup if (SetEverything == false) goto POSTSTATUSOK; } //We've arrived here because there is no valid setting for POST workorder status //or it's the first time through and needs to be selected on way or another s1 = new SetWOStatus(); s1.DialogTitle = "AyaNova PTI setup - Choose post billed Workorder Status"; s1.OptionTitle = "Post billing workorder status"; s1.OptionDescription = "After PTI has billed out a work order, it can change the \r\n" + "work order status for you automatically if desired."; s1.SelectedStatus = PDat.PostWOStatus; s1.PreStatus = false; if (s1.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else PDat.PostWOStatus = s1.SelectedStatus; s1.Dispose(); s1 = null; POSTSTATUSOK: #endregion #region Outside service charge as //Validate any existing status if (SetEverything == false && PDat.OutsideServiceChargeAs != null && PDat.OutsideServiceChargeAs != "") { if (PTItems.Rows.Contains(PDat.OutsideServiceChargeAs)) goto OUTSIDESERVICECHARGEASOK; else { MessageBox.Show("The Peachtree Item previously set for invoicing Outside Service items\r\n" + "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + "Peachtree Item."); } } //We've arrived here because there is no valid setting for OutsideServiceChargeAs SetPTChargeAs s2 = new SetPTChargeAs(); s2.DialogTitle = "AyaNova PTI setup - Charge outside service as?"; s2.OptionTitle = "Outside service"; s2.OptionDescription = "PTI needs to know what Peachtree Item you want \r\n" + "to use when invoicing the AyaNova \"Outside service\" portion of a work order.\r\n\r\n" + "Outside service is any 3rd party repair that is billable to the customer.\r\n\r\n" + "This setting is mandatory / required."; s2.PTItems = PTItems; s2.SelectedPTItem = PDat.OutsideServiceChargeAs; //s2.SelectedPTItem=QDat.PreWOStatus; if (s2.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else PDat.OutsideServiceChargeAs = s2.SelectedPTItem; s2.Dispose(); s2 = null; OUTSIDESERVICECHARGEASOK: #endregion #region Misc expense charge as //Validate any existing if (SetEverything == false && PDat.MiscExpenseChargeAs != null && PDat.MiscExpenseChargeAs != "") { //if(PTItems.Rows.Contains(QDat.MiscExpenseChargeAs)) goto MISCCHARGEASOK; // else // { // MessageBox.Show("The Peachtree Item previously set for invoicing Misc. Expense items\r\n" + // "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + // "Peachtree Item."); // } } //We've arrived here because there is no valid setting for Misc expense s2 = new SetPTChargeAs(); s2.DialogTitle = "AyaNova PTI setup - Charge Misc. Expense as?"; s2.OptionTitle = "Miscellaneous expenses"; s2.OptionDescription = "PTI needs to know what Peachtree Item you want \r\n" + "to use when invoicing the AyaNova \"Miscellaneous expense\" portion of a work order.\r\n\r\n" + "This setting is mandatory / required."; s2.PTItems = PTItems; s2.SelectedPTItem = PDat.MiscExpenseChargeAs; if (s2.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else PDat.MiscExpenseChargeAs = s2.SelectedPTItem; s2.Dispose(); s2 = null; MISCCHARGEASOK: #endregion #region Workorder item loan charge as //Validate any existing if (SetEverything == false && PDat.WorkorderItemLoanChargeAs != null && PDat.WorkorderItemLoanChargeAs != "") { //if(PTItems.Rows.Contains(QDat.MiscExpenseChargeAs)) goto LOANCHARGEASOK; // else // { // MessageBox.Show("The Peachtree Item previously set for invoicing Misc. Expense items\r\n" + // "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + // "Peachtree Item."); // } } //We've arrived here because there is no valid setting for Misc expense s2 = new SetPTChargeAs(); s2.DialogTitle = "AyaNova PTI setup - Charge loan item as?"; s2.OptionTitle = "Work order loans"; s2.OptionDescription = "PTI needs to know what Peachtree Item you want \r\n" + "to use when invoicing the AyaNova \"Workorder item loan\" portion of a work order.\r\n\r\n" + "This setting is mandatory / required."; s2.PTItems = PTItems; s2.SelectedPTItem = PDat.WorkorderItemLoanChargeAs; if (s2.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else PDat.WorkorderItemLoanChargeAs = s2.SelectedPTItem; s2.Dispose(); s2 = null; LOANCHARGEASOK: #endregion #region SetMemoField //No validation possible //so prompt only if not setup yet if (!SetEverything) { //if(PTItems.Rows.Contains(QDat.MiscExpenseChargeAs)) goto SETMEMOOK; // else // { // MessageBox.Show("The Peachtree Item previously set for invoicing Misc. Expense items\r\n" + // "No longer appears to be valid. You will next be prompted to re-select a valid \r\n" + // "Peachtree Item."); // } } SetMemoField s5 = new SetMemoField(); s5.DialogTitle = "AyaNova PTI setup - Set internal note field?"; s5.OptionTitle = "Invoice internal notes field"; s5.OptionDescription = "PTI needs to know if you want invoices that it creates \r\n" + "in Peachtree to have their \"internal notes\" field set with\r\n" + "information about the work order(s) that were the basis for\r\n" + "the invoice and the name of the AyaNova user who generated them.\r\n\r\n" + "This may be useful as a back reference, this setting is optional"; s5.FillMemoField = PDat.SetMemoField; if (s5.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else PDat.SetMemoField = s5.FillMemoField; s5.Dispose(); s5 = null; SETMEMOOK: #endregion #region SetAutoCloseField Case 7 //No validation possible //so prompt only if not setup yet if (!SetEverything) { goto SETAUTOCLOSEOK; } SetAutoClose s6 = new SetAutoClose(); s6.DialogTitle = "AyaNova PTI setup - Close when invoiced?"; s6.OptionTitle = "Close work order after invoicing"; s6.OptionDescription = "PTI needs to know if you want work orders that it invoices \r\n" + "automatically set to closed"; s6.AutoClose = PDat.AutoClose; if (s6.ShowDialog() == DialogResult.Cancel) { return pfstat.Cancel; } else PDat.AutoClose = s6.AutoClose; s6.Dispose(); s6 = null; SETAUTOCLOSEOK: #endregion //Save if changes made if (PDat.IsDirty) { //Case 299 PTI.AIObject = PDat.XMLData; //PTI.AIObject=QDat; PTI = (Integration)PTI.Save(); PDat.IsDirty = false; } return pfstat.OK; } #endregion #endregion pfc #region AyaNova cached lists public static void PopulateAyaListCache() { //Get the cached PT data Waiting w = new Waiting(); w.Show(); w.Ops = "Reading from AyaNova..."; w.Step = "Clients"; PopulateAyaClientList(); w.Step = "Vendors"; PopulateAyaVendorList(); w.Step = "Rates"; PopulateAyaRateList(); w.Step = "Parts"; PopulateAyaPartList(); w.Close(); } #region AyaNova clients private static ClientPickList _clientlist = null; /// /// AyaNova ClientPickList /// public static ClientPickList AyaClientList { get { return _clientlist; } } public static void PopulateAyaClientList() { _clientlist = ClientPickList.GetList(); } #endregion ayanova clients #region AyaNova Vendors private static VendorPickList _vendorlist = null; /// /// AyaNova vendor list /// public static VendorPickList AyaVendorList { get { return _vendorlist; } } public static void PopulateAyaVendorList() { _vendorlist = VendorPickList.GetList(); } #endregion ayanova vendors #region AyaNova Rates private static RatePickList _ratelist = null; /// /// AyaNova rate list /// public static RatePickList AyaRateList { get { return _ratelist; } } public static void PopulateAyaRateList() { _ratelist = RatePickList.GetListAllActiveRates(); } #endregion ayanova rates #region AyaNova Parts private static PartPickList _partlist = null; /// /// AyaNova part list /// public static PartPickList AyaPartList { get { return _partlist; } } public static void PopulateAyaPartList() { _partlist = PartPickList.GetAllParts(); } #endregion ayanova parts #endregion #region Peachtree cached lists / API helper methods/ attributes /// /// Populate or repopulate the list of /// public static void PopulatePTListCache() { //Get the cached PT data Waiting w = new Waiting(); w.Show(); w.Ops = "Reading from Peachtree..."; w.Step = "Vendors"; PopulatePTVendorCache(); w.Step = "Customers"; PopulatePTClientCache(); w.Step = "Items"; PopulatePTItemCache(); w.Step = "Default GL Accounts"; PopulatePTDefaultAccountsCache(); w.Step = "Tax Authorities"; PopulatePTTaxAuthorityCache(); w.Step = "Tax Codes"; PopulatePTTaxCodeCache(); w.Step = "Item tax types"; PopulatePTItemTaxTypesCache(); #if(DEBUG) w.Step = "Test export"; TestExport(); #endif w.Close(); } #region xml helpers private static XmlNode CurrentNode = null; private static string v(string xpath) { XmlNode n = CurrentNode.SelectSingleNode(xpath); if (n == null) return ""; return n.InnerText; } private static bool vBool(string xpath) { XmlNode n = CurrentNode.SelectSingleNode(xpath); if (n == null) return false; if (n.InnerText.ToLowerInvariant() == "true") return true; else return false; } private static int vInt(string xpath) { XmlNode n = CurrentNode.SelectSingleNode(xpath); if (n == null) return 0; return ParseInt(n.InnerText); } public static int ParseInt(object s) { if (s == null) return 0; int d = 0; if (!int.TryParse(s.ToString(), out d)) return 0; return d; } private static decimal vDecimal(string xpath) { XmlNode n = CurrentNode.SelectSingleNode(xpath); if (n == null) return 0; return ParseDecimal(n.InnerText); } public static decimal ParseDecimal(object s) { if (s == null) return 0; decimal d = 0; if (!decimal.TryParse(s.ToString(), out d)) return 0; return d; } #endregion #region Peachtree "items" public enum ptitemtype { Inventory, NonInventory, Service, OtherCharge, Assembly } private static DataTable _dtPTItems = null; /// /// qb items /// public static DataTable PTItems { get { return _dtPTItems; } } /// /// Given a PT Item ID, return the /// AyaNova Vendor ID linked to that items /// PT preferred Vendor ID or /// Guid empty on any problem or not found /// /// /// public static Guid AyaVendorForPTItem(string PTItemID) { if (PTItemID == null || PTItemID == "") return Guid.Empty; DataRow dr = _dtPTItems.Rows.Find(PTItemID); if (dr == null || dr["VendorID"] == null || dr["VendorID"].ToString() == "") return Guid.Empty; DataRow drVendor = _dtPTVendors.Rows.Find(dr["VendorID"].ToString()); if (drVendor == null) return Guid.Empty; if (!PTI.Maps.Contains(drVendor["ID"].ToString(), RootObjectTypes.Vendor)) return Guid.Empty; //Ok we have a matching vendor in the list, return the guid of it return PTI.Maps[drVendor["ID"].ToString(), RootObjectTypes.Vendor].RootObjectID; } /// /// Populate the cached qb data /// billable /// private static void PopulatePTItemCache() { if (_dtPTItems == null) { _dtPTItems = new DataTable("PTItems"); //setup the columns _dtPTItems.Columns.Add("ID", typeof(string)); _dtPTItems.Columns.Add("FullName", typeof(string)); _dtPTItems.Columns.Add("Type", typeof(ptitemtype)); _dtPTItems.Columns.Add("Modified", typeof(DateTime)); _dtPTItems.Columns.Add("Price", typeof(decimal)); _dtPTItems.Columns.Add("Cost", typeof(decimal)); _dtPTItems.Columns.Add("SalesDesc", typeof(string)); _dtPTItems.Columns.Add("ReorderPoint", typeof(decimal)); _dtPTItems.Columns.Add("VendorID", typeof(string)); _dtPTItems.Columns.Add("SalesAccountId", typeof(string)); _dtPTItems.Columns.Add("SalesTaxType", typeof(string)); _dtPTItems.PrimaryKey = new DataColumn[] { _dtPTItems.Columns[0] }; //Case 237 _dtPTItems.DefaultView.Sort = "FullName asc"; } else _dtPTItems.Clear(); try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjInventoryItemsList); exporter.ClearExportFieldList(); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_ItemId); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_ItemDescription); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_SalesDesc); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_Class); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_ClassDescription); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_UnitPrice1); //Last unit cost: exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_LaborCost); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_PrimeVendor); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_ReorderPoint); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_Inactive); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_SalesAccountId); exporter.AddToExportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_SalesTaxType); exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTitems.xml"); exporter.Export(); #endregion #region Load from export file XmlDocument items = new XmlDocument(); items.Load(TempFilePath + "PTitems.xml"); XmlNodeList reader = items.GetElementsByTagName("PAW_Item"); for (int i = 0; i <= reader.Count - 1; i++) { CurrentNode = reader[i]; if (v("isInactive") == "TRUE") continue; //skip over non ayanova related inventory items like //text only and internal activity items and master stock items which are just a group not an actual item if (v("Class") == "2" || v("Class") == "6" || v("Class") == "8") continue; DataRow dr = _dtPTItems.NewRow(); dr["ID"] = v("ID"); dr["FullName"] = v("Description"); /* * Inventory classes: * 0 - NON stock item * 1 - Stock item * 2 - Text only ("Have a nice day", "thank you for your business") WTF? * 3 - Assembly * 4 - Service * 5 - Labor * 6 - Activity item (seems to be internal stuff only, for example: "(Internal) Bookkeeping and Administrative Duties for Front Office" * 7 - Charge class item (not sure what it is, will go in other category for now) * 8 - Master stock item (A grouping item for substock items.) * 9 - Substock item (regular inventory item but grouped under a master stock item for easier selection I guess) * 10 - Serialized stock item * 11 - serialized assembly * * */ ptitemtype itemtype = ptitemtype.OtherCharge; switch (v("Class")) { case "0": itemtype = ptitemtype.OtherCharge; break; case "1": itemtype = ptitemtype.Inventory; break; case "3": itemtype = ptitemtype.Assembly; break; case "4": itemtype = ptitemtype.Service; break; case "5": itemtype = ptitemtype.Service; break; case "7": itemtype = ptitemtype.OtherCharge; break; case "9": itemtype = ptitemtype.Inventory; break; case "10": itemtype = ptitemtype.Inventory; break; case "11": itemtype = ptitemtype.Inventory; break; default://still don't know what some of the items are so they will fall into here by default itemtype = ptitemtype.OtherCharge; break; } dr["Type"] = itemtype; dr["Modified"] = DateTime.Now; dr["Price"] = ParseDecimal(v("Sales_Prices/Sales_Price_Info[@Key='1']")); dr["Cost"] = ParseDecimal(v("Last_Unit_Cost")); //Get sales description (could be empty so sub description if empty) if (string.IsNullOrEmpty(v("Description_for_Sales"))) dr["SalesDesc"] = v("Description"); else dr["SalesDesc"] = v("Description_for_Sales"); dr["ReorderPoint"] = ParseDecimal(v("Minimum_Stock")); dr["VendorID"] = v("Vendor_ID"); dr["SalesAccountId"] = v("GL_Sales_Account"); dr["SalesTaxType"] = (int.Parse(v("Tax_Type")) + 1).ToString();//tax types are zero based here and 1 based in tax types xml _dtPTItems.Rows.Add(dr); } #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "PopulatePTItemCache: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion Peachtree items #region Peachtree "Customers" private static DataTable _dtPTClients = null; /// /// PT Transaction Clients /// public static DataTable PTClients { get { return _dtPTClients; } } #region Address structure /// /// Address properties /// public struct Address { public string DeliveryAddress; public string City; public string StateProv; public string Country; public string Postal; } #endregion /// /// Populate the cached qb data /// of customers / clients /// private static void PopulatePTClientCache() { if (_dtPTClients == null) { _dtPTClients = new DataTable("PTClients"); //setup the columns _dtPTClients.Columns.Add("ID", typeof(string)); _dtPTClients.Columns.Add("FullName", typeof(string)); _dtPTClients.Columns.Add("MailAddress", typeof(Address)); _dtPTClients.Columns.Add("StreetAddress", typeof(Address)); _dtPTClients.Columns.Add("Phone", typeof(string)); _dtPTClients.Columns.Add("Fax", typeof(string)); _dtPTClients.Columns.Add("AltPhone", typeof(string)); _dtPTClients.Columns.Add("Email", typeof(string)); _dtPTClients.Columns.Add("Contact", typeof(string)); _dtPTClients.Columns.Add("Created", typeof(DateTime)); _dtPTClients.Columns.Add("Modified", typeof(DateTime)); _dtPTClients.Columns.Add("Account", typeof(string)); _dtPTClients.Columns.Add("WebAddress", typeof(string)); _dtPTClients.Columns.Add("Sales_Tax_Code", typeof(string)); //Case 539 _dtPTClients.Columns.Add("Due_Days", typeof(int)); _dtPTClients.Columns.Add("Discount_Percent", typeof(decimal)); _dtPTClients.Columns.Add("Discount_Days", typeof(int)); _dtPTClients.Columns.Add("Terms_Type", typeof(bool));//If true means due on day (Due_days) of next month _dtPTClients.Columns.Add("Use_Due_Month_End_Terms", typeof(bool));//If true means due at this month end. _dtPTClients.PrimaryKey = new DataColumn[] { _dtPTClients.Columns[0] }; //Case 237 _dtPTClients.DefaultView.Sort = "FullName asc"; } else _dtPTClients.Clear(); try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjCustomerList); exporter.ClearExportFieldList(); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerId); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerName); //Physical (and maybe mail) address exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1AddressLine1); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1AddressLine2); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1City); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1State); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1Country); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1Zip); //Bill to address (mailing) exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToAddressLine1); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToAddressLine2); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToCity); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToState); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToCountry); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToZip); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerPhone1); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerFax); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerPhone2); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerEmail); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerContact); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerAccountNumber); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerInactive); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerURL); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToTaxCode); //Case 539 exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerDueDays); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerDiscountPercent); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerDiscountDays); exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerDayIsActual);//Terms type exporter.AddToExportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerDueMonthEnd);//useduemonthendterms exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTcustomers.xml"); exporter.Export(); #endregion #region Load from export file XmlDocument customers = new XmlDocument(); customers.Load(TempFilePath + "PTcustomers.xml"); XmlNodeList reader = customers.GetElementsByTagName("PAW_Customer"); for (int i = 0; i <= reader.Count - 1; i++) { CurrentNode = reader[i]; if (v("isInactive") == "TRUE") continue; DataRow dr = _dtPTClients.NewRow(); dr["ID"] = v("ID"); dr["FullName"] = v("Name"); dr["Contact"] = v("ContactName"); //Bill to (mail address) cluster Address mail = new Address(); mail.DeliveryAddress = v("BillToAddress/Line1"); if (!string.IsNullOrEmpty(v("BillToAddress/Line2"))) mail.DeliveryAddress = mail.DeliveryAddress + "\r\n" + v("BillToAddress/Line2"); mail.City = v("BillToAddress/City"); mail.StateProv = v("BillToAddress/State"); mail.Postal = v("BillToAddress/Zip"); mail.Country = v("BillToAddress/Country"); dr["Sales_Tax_Code"] = v("BillToAddress/Sales_Tax_Code"); Address physical = new Address(); if (string.IsNullOrEmpty(v("Line1"))) { //then copy the mail address because there is no physical address physical.City = mail.City; physical.Country = mail.Country; physical.DeliveryAddress = mail.DeliveryAddress; physical.Postal = mail.Postal; physical.StateProv = mail.StateProv; } else { physical.DeliveryAddress = v("Line1"); if (!string.IsNullOrEmpty(v("Line2"))) physical.DeliveryAddress = physical.DeliveryAddress + "\r\n" + v("Line2");//case 813 physical.City = v("City"); physical.StateProv = v("State"); physical.Country = v("Country"); physical.Postal = v("Zip"); } dr["MailAddress"] = mail; dr["StreetAddress"] = physical; dr["Phone"] = v("PhoneNumbers/PhoneNumber[@Key='1']"); dr["AltPhone"] = v("PhoneNumbers/PhoneNumber[@Key='2']"); dr["Fax"] = v("FaxNumber"); dr["Email"] = v("EMail_Address"); dr["Account"] = v("Account_Number"); dr["Created"] = DateTime.Now; dr["Modified"] = DateTime.Now; dr["WebAddress"] = v("Web_Address"); //Case 539 dr["Due_Days"] = vInt("Due_Days"); dr["Discount_Percent"] = vDecimal("Discount_Percent"); dr["Discount_Days"] = vInt("Discount_Days"); dr["Terms_Type"] = vBool("Terms_Type"); dr["Use_Due_Month_End_Terms"] = vBool("Use_Due_Month_End_Terms"); _dtPTClients.Rows.Add(dr); } #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "PopulatePTClientCache: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion pt clients #region Peachtree "Vendors" private static DataTable _dtPTVendors = null; /// /// PT Vendors /// public static DataTable PTVendors { get { return _dtPTVendors; } } /// /// Populate the cached qb data /// of Vendors /// private static void PopulatePTVendorCache() { if (_dtPTVendors == null) { _dtPTVendors = new DataTable("PTVendors"); //setup the columns _dtPTVendors.Columns.Add("ID", typeof(string)); _dtPTVendors.Columns.Add("FullName", typeof(string)); _dtPTVendors.Columns.Add("MailAddress", typeof(Address)); _dtPTVendors.Columns.Add("StreetAddress", typeof(Address)); _dtPTVendors.Columns.Add("Phone", typeof(string)); _dtPTVendors.Columns.Add("Fax", typeof(string)); _dtPTVendors.Columns.Add("AltPhone", typeof(string)); _dtPTVendors.Columns.Add("Email", typeof(string)); _dtPTVendors.Columns.Add("Contact", typeof(string)); _dtPTVendors.Columns.Add("Created", typeof(DateTime)); _dtPTVendors.Columns.Add("Modified", typeof(DateTime)); _dtPTVendors.Columns.Add("Account", typeof(string)); _dtPTVendors.Columns.Add("WebAddress", typeof(string)); _dtPTVendors.PrimaryKey = new DataColumn[] { _dtPTVendors.Columns[0] }; //Case 237 _dtPTVendors.DefaultView.Sort = "FullName asc"; } else _dtPTVendors.Clear(); try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjVendorList); exporter.ClearExportFieldList(); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorId); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorName); //Physical (and maybe mail) address exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorAddressLine1); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorAddressLine2); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorCity); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorState); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorCountry); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorZip); //Remit address (mailing) exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1AddressLine1); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1AddressLine2); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1City); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1State); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1Country); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1Zip); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorPhone1); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorFax); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorPhone2); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorEmail); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorContact); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorOurAccountId); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorInactive); exporter.AddToExportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_byVendorURL); exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTvendors.xml"); exporter.Export(); #endregion #region Load from export file XmlDocument vendors = new XmlDocument(); vendors.Load(TempFilePath + "PTvendors.xml"); XmlNodeList reader = vendors.GetElementsByTagName("PAW_Vendor"); for (int i = 0; i <= reader.Count - 1; i++) { CurrentNode = reader[i]; if (v("isInactive") == "TRUE") continue; DataRow dr = _dtPTVendors.NewRow(); dr["ID"] = v("ID"); dr["FullName"] = v("Name"); dr["Contact"] = v("ContactName"); //Remit to (mail address) cluster Address mail = new Address(); mail.DeliveryAddress = v("RemitToAddress/Line1"); if (!string.IsNullOrEmpty(v("RemitToAddress/Line2"))) mail.DeliveryAddress = mail.DeliveryAddress + "\r\n" + v("RemitToAddress/Line2"); mail.City = v("RemitToAddress/City"); mail.StateProv = v("RemitToAddress/State"); mail.Postal = v("RemitToAddress/Zip"); mail.Country = v("RemitToAddress/Country"); Address physical = new Address(); if (string.IsNullOrEmpty(v("Line1"))) { //then copy the mail address because there is no physical address physical.City = mail.City; physical.Country = mail.Country; physical.DeliveryAddress = mail.DeliveryAddress; physical.Postal = mail.Postal; physical.StateProv = mail.StateProv; } else { physical.DeliveryAddress = v("Line1"); if (!string.IsNullOrEmpty(v("Line2"))) physical.DeliveryAddress = mail.DeliveryAddress + "\r\n" + v("Line2"); physical.City = v("City"); physical.StateProv = v("State"); physical.Country = v("Country"); physical.Postal = v("Zip"); } dr["MailAddress"] = mail; dr["StreetAddress"] = physical; dr["Phone"] = v("PhoneNumbers/PhoneNumber[@Key='1']"); dr["AltPhone"] = v("PhoneNumbers/PhoneNumber[@Key='2']"); dr["Fax"] = v("FaxNumber"); dr["Email"] = v("EMail_Address"); dr["Account"] = v("AccountNumber"); dr["Created"] = DateTime.Now; dr["Modified"] = DateTime.Now; dr["WebAddress"] = v("Web_Address"); _dtPTVendors.Rows.Add(dr); } #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "PopulatePTVendors: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion pt Vendors #region Peachtree Default gl accounts public static string DefaultARAccount = ""; public static string DefaultSalesAccount = ""; /// Populate the cached qb data /// of Vendors /// private static void PopulatePTDefaultAccountsCache() { try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjDefaultAccounts); exporter.ClearExportFieldList(); exporter.AddToExportFieldList((short)PeachwIEObjDefaultAccountsField.peachwIEObjDefaultAccountsField_SalesAccountId); exporter.AddToExportFieldList((short)PeachwIEObjDefaultAccountsField.peachwIEObjDefaultAccountsField_ARAccountId); exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTdefaccounts.xml"); exporter.Export(); #endregion #region Load from export file XmlDocument defaccounts = new XmlDocument(); defaccounts.Load(TempFilePath + "PTdefaccounts.xml"); XmlNodeList reader = defaccounts.GetElementsByTagName("PAW_DefaultAccount"); //this xml file only ever has one record in it CurrentNode = reader[0]; DefaultARAccount = v("AR"); DefaultSalesAccount = v("Sales"); #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "PopulatePTDefaultAccountsCache: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion pt default accounts #region Peachtree "Tax Authorities" private static DataTable _dtPTTaxAuthorities = null; /// /// PT Taxes /// public static DataTable PTTaxAuthorities { get { return _dtPTTaxAuthorities; } } /// /// Populate the cached qb data /// of Tax authorities and taxes /// private static void PopulatePTTaxAuthorityCache() { if (_dtPTTaxAuthorities == null) { _dtPTTaxAuthorities = new DataTable("PTTaxAuthorities"); //setup the columns _dtPTTaxAuthorities.Columns.Add("ID", typeof(string)); _dtPTTaxAuthorities.Columns.Add("FullName", typeof(string)); _dtPTTaxAuthorities.Columns.Add("Rate", typeof(decimal)); _dtPTTaxAuthorities.Columns.Add("AccountID", typeof(string)); _dtPTTaxAuthorities.Columns.Add("VendorID", typeof(string)); _dtPTTaxAuthorities.Columns.Add("Rate2", typeof(decimal)); _dtPTTaxAuthorities.Columns.Add("UsesFormula", typeof(string)); _dtPTTaxAuthorities.Columns.Add("TaxBasis", typeof(decimal)); _dtPTTaxAuthorities.Columns.Add("TaxLimit", typeof(decimal)); _dtPTTaxAuthorities.PrimaryKey = new DataColumn[] { _dtPTTaxAuthorities.Columns[0] }; //Case 237 _dtPTTaxAuthorities.DefaultView.Sort = "FullName asc"; } else _dtPTTaxAuthorities.Clear(); try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjSalesTaxAuthorities); exporter.ClearExportFieldList(); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_SalesTaxAuthorityCodeId); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_SalesTaxAuthorityCodeDescription); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_TaxRate); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_AccountId); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_VendorId); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_TaxRate2); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_TaxUsesFormula); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_TaxBasis); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxAuthoritiesField.peachwIEObjSalesTaxAuthoritiesField_TaxChangeAt); exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTtaxauth.xml"); exporter.Export(); #endregion #region Load from export file XmlDocument vendors = new XmlDocument(); vendors.Load(TempFilePath + "PTtaxauth.xml"); XmlNodeList reader = vendors.GetElementsByTagName("PAW_Sales_Tax_Authority"); for (int i = 0; i <= reader.Count - 1; i++) { CurrentNode = reader[i]; DataRow dr = _dtPTTaxAuthorities.NewRow(); dr["ID"] = v("ID"); dr["FullName"] = v("Name"); dr["Rate"] = decimal.Parse(v("Rate")); dr["AccountID"] = v("AccountID"); dr["VendorID"] = v("VendorID"); dr["Rate2"] = decimal.Parse(v("Rate2")); dr["UsesFormula"] = v("UsesFormula"); dr["TaxBasis"] = decimal.Parse(v("TaxBasis")); dr["TaxLimit"] = decimal.Parse(v("TaxLimit")); _dtPTTaxAuthorities.Rows.Add(dr); } #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "PopulatePTTaxAuthorities: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion pt Taxes #region Peachtree "Tax Codes" private static DataTable _dtPTTaxCodes = null; /// /// PT Taxes /// public static DataTable PTTaxCodes { get { return _dtPTTaxCodes; } } /// /// Populate the cached qb data /// of Tax Codes /// private static void PopulatePTTaxCodeCache() { if (_dtPTTaxCodes == null) { _dtPTTaxCodes = new DataTable("PTTaxCodes"); //setup the columns _dtPTTaxCodes.Columns.Add("ID", typeof(string)); _dtPTTaxCodes.Columns.Add("FullName", typeof(string)); _dtPTTaxCodes.Columns.Add("Freight", typeof(string));//No idea what this is for _dtPTTaxCodes.Columns.Add("Authorities", typeof(string));//comma separate list of authority id's _dtPTTaxCodes.PrimaryKey = new DataColumn[] { _dtPTTaxCodes.Columns[0] }; //Case 237 _dtPTTaxCodes.DefaultView.Sort = "FullName asc"; } else _dtPTTaxCodes.Clear(); try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjSalesTaxCodes); exporter.ClearExportFieldList(); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxCodesField.peachwIEObjSalesTaxCodesField_SalesTaxCodeId); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxCodesField.peachwIEObjSalesTaxCodesField_SalesTaxCodeDescription); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxCodesField.peachwIEObjSalesTaxCodesField_TaxFreight); exporter.AddToExportFieldList((short)PeachwIEObjSalesTaxCodesField.peachwIEObjSalesTaxCodesField_AuthId); exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTtaxcodes.xml"); exporter.Export(); #endregion #region Load from export file XmlDocument vendors = new XmlDocument(); vendors.Load(TempFilePath + "PTtaxcodes.xml"); XmlNodeList reader = vendors.GetElementsByTagName("PAW_Sales_Tax_Code"); for (int i = 0; i <= reader.Count - 1; i++) { CurrentNode = reader[i]; DataRow dr = _dtPTTaxCodes.NewRow(); dr["ID"] = v("ID"); dr["FullName"] = v("Description"); dr["Freight"] = v("Freight"); XmlNode n = CurrentNode.SelectSingleNode("Authorities"); XmlNodeList subreader = n.ChildNodes; string sAuthorities = ""; foreach (XmlNode nsub in subreader) { sAuthorities += nsub.SelectSingleNode("ID").InnerText + ","; } dr["Authorities"] = sAuthorities.TrimEnd(','); _dtPTTaxCodes.Rows.Add(dr); } #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "PopulatePTTaxCodes: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion pt Taxes #region Peachtree "ItemTaxTypes" private static DataTable _dtPTItemTaxTypes = null; /// /// PT Taxes /// public static DataTable PTItemTaxTypes { get { return _dtPTItemTaxTypes; } } //Case 559 public static string firstNonTaxableTaxCode = ""; public static string FirstNonTaxableTaxCode { get { if (firstNonTaxableTaxCode == "") { firstNonTaxableTaxCode = "2";//just a default in case something else goes wrong } return firstNonTaxableTaxCode; } } /// /// Determines if a particular item has a taxable tax type set /// /// /// public static bool IsTaxable(string Item) { DataRow drItem = PTItems.Rows.Find(Item); DataRow drTaxType = PTItemTaxTypes.Rows.Find(drItem["SalesTaxType"]); if (drTaxType == null) return false; return (drTaxType["IsTaxable"].ToString() == "TRUE"); // ["SalesTaxType"] // // PTItems.Rows.Find( // dr["ID"].ToString() // )["SalesTaxType"] // )["IsTaxable"].ToString()=="TRUE" } /// /// Populate the cached qb data /// of Item Tax type codes (is taxable) /// private static void PopulatePTItemTaxTypesCache() { if (_dtPTItemTaxTypes == null) { _dtPTItemTaxTypes = new DataTable("PTItemTaxTypes"); //setup the columns _dtPTItemTaxTypes.Columns.Add("Number", typeof(string)); _dtPTItemTaxTypes.Columns.Add("IsTaxable", typeof(string)); _dtPTItemTaxTypes.PrimaryKey = new DataColumn[] { _dtPTItemTaxTypes.Columns[0] }; } else _dtPTItemTaxTypes.Clear(); try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjItemTaxTypes); exporter.ClearExportFieldList(); exporter.AddToExportFieldList((short)PeachwIEObjItemTaxTypesField.peachwIEObjItemTaxTypesField_Number); exporter.AddToExportFieldList((short)PeachwIEObjItemTaxTypesField.peachwIEObjItemTaxTypesField_IsTaxable); exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTitemtaxtypes.xml"); exporter.Export(); #endregion #region Load from export file XmlDocument vendors = new XmlDocument(); vendors.Load(TempFilePath + "PTitemtaxtypes.xml"); XmlNodeList reader = vendors.GetElementsByTagName("PAW_Item_Tax_Type"); for (int i = 0; i <= reader.Count - 1; i++) { CurrentNode = reader[i]; DataRow dr = _dtPTItemTaxTypes.NewRow(); dr["Number"] = v("Number"); dr["IsTaxable"] = v("IsTaxable"); //Case 559 //get the lowest non taxable tax code because pt barfs if you use the highest if (firstNonTaxableTaxCode == "" && dr["IsTaxable"].ToString() == "TRUE") firstNonTaxableTaxCode = dr["Number"].ToString(); _dtPTItemTaxTypes.Rows.Add(dr); } #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "PopulatePTItemTaxTypes: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion pt item tax types #region Peachtree Test for viewing xml file structure /// /// Text export from peachtree /// private static void TestExport() { try { #region Export PeachtreeAccounting.Export exporter = (PeachtreeAccounting.Export)ptApp.app.CreateExporter(PeachwIEObj.peachwIEObjSalesJournal); exporter.ClearExportFieldList(); exporter.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); exporter.SetFilename(TempFilePath + "PTtestexport.xml"); exporter.Export(); #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "TestExport: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion TESTING #endregion #region Import to AyaNova #region customer /// /// Import the indicated customer /// to an AyaNova client record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportPTCustomer(string PeachtreeID, ArrayList alErrors) { DataRow dr = _dtPTClients.Rows.Find(PeachtreeID); //PTListID not found in client list? if (dr == null) { alErrors.Add("ImportPTCustomer: ID not found " + PeachtreeID); return; } string sName = dr["FullName"].ToString(); if (sName.Length > 255) { alErrors.Add("ImportPTCustomer: Peachtree customer name exceeds 255 character limit for AyaNova\r\n" + "Name: " + dr["FullName"].ToString() + "\r\n" + "was imported as: " + sName); sName = sName.Substring(0, 255); } try { //already a client by that name if (Client.Exists(Guid.Empty, dr["FullName"].ToString())) { alErrors.Add("ImportPTCustomer: " + dr["FullName"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Client c = Client.NewItem(); c.Name = sName;//1 Address a = (Address)dr["MailAddress"]; c.MailToAddress.DeliveryAddress = a.DeliveryAddress;//2 c.MailToAddress.City = a.City;//3 c.MailToAddress.StateProv = a.StateProv;//4 c.MailToAddress.Country = a.Country;//5 c.MailToAddress.Postal = a.Postal;//6 a = (Address)dr["StreetAddress"]; c.GoToAddress.DeliveryAddress = a.DeliveryAddress;//7 c.GoToAddress.City = a.City;//8 c.GoToAddress.StateProv = a.StateProv;//9 c.GoToAddress.Country = a.Country;//10 c.GoToAddress.Postal = a.Postal;//11 //Case 518 c.PopulateBothAddresses(); c.Contact = dr["Contact"].ToString(); //Phone field if (dr["Phone"].ToString() != "") { c.Phone1 = dr["Phone"].ToString();//14 } //Fax field if (dr["Fax"].ToString() != "") { c.Phone2 = dr["Fax"].ToString();//15 } //AltPhone field if (dr["AltPhone"].ToString() != "") { c.Phone3 = dr["AltPhone"].ToString();//16 } //Email field if (dr["Email"].ToString() != "") { c.Email = dr["Email"].ToString();//17 } //Account number field if (dr["Account"].ToString() != "") { c.AccountNumber = dr["Account"].ToString();//18 } if (!c.IsSavable) { alErrors.Add("ImportPTCustomer: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } c.WebAddress = dr["WebAddress"].ToString(); c = (Client)c.Save(); //Link IntegrationMap m = PTI.Maps.Add(PTI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Client; m.LastSync = DateTime.Now; m.ForeignID = PeachtreeID; PTI = (Integration)PTI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind alErrors.Add("ImportPTCustomer: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + CrackException(ex)); ErrorFree = false; } } #endregion #region Vendor /// /// Import the indicated Vendor /// to an AyaNova vendor record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportPTVendor(string PeachtreeID, VendorTypes AsVendorType, ArrayList alErrors) { DataRow dr = _dtPTVendors.Rows.Find(PeachtreeID); //PTListID not found in Vendor list? if (dr == null) { alErrors.Add("ImportPTVendor: ID not found " + PeachtreeID); return; } string sName = dr["FullName"].ToString(); if (sName.Length > 255) { alErrors.Add("ImportPTVendor: Peachtree Vendor name exceeds 255 character limit for AyaNova\r\n" + "Name: " + dr["FullName"].ToString() + "\r\n" + "was imported as: " + sName); sName = sName.Substring(0, 255); } try { //already a Vendor by that name if (Vendor.Exists(Guid.Empty, dr["FullName"].ToString())) { alErrors.Add("ImportPTVendor: " + dr["FullName"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Vendor c = Vendor.NewItem(); c.VendorType = AsVendorType; c.Name = sName; Address a = (Address)dr["MailAddress"]; c.MailToAddress.DeliveryAddress = a.DeliveryAddress; c.MailToAddress.City = a.City; c.MailToAddress.StateProv = a.StateProv; c.MailToAddress.Country = a.Country; c.MailToAddress.Postal = a.Postal; a = (Address)dr["StreetAddress"]; c.GoToAddress.DeliveryAddress = a.DeliveryAddress; c.GoToAddress.City = a.City; c.GoToAddress.StateProv = a.StateProv; c.GoToAddress.Country = a.Country; c.GoToAddress.Postal = a.Postal; c.Contact = dr["Contact"].ToString(); //Phone field if (dr["Phone"].ToString() != "") { c.Phone1 = dr["Phone"].ToString(); } //Fax field if (dr["Fax"].ToString() != "") { c.Phone2 = dr["Fax"].ToString(); } //AltPhone field if (dr["AltPhone"].ToString() != "") { c.Phone3 = dr["AltPhone"].ToString(); } //Email field if (dr["Email"].ToString() != "") { c.Email = dr["Email"].ToString(); } //Account number if (dr["Account"].ToString() != "") { c.AccountNumber = dr["Account"].ToString(); } c.WebAddress = dr["WebAddress"].ToString(); if (!c.IsSavable) { alErrors.Add("ImportPTVendor: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } c = (Vendor)c.Save(); //Link IntegrationMap m = PTI.Maps.Add(PTI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Vendor; m.LastSync = DateTime.Now; m.ForeignID = PeachtreeID; PTI = (Integration)PTI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportPTVendor: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); ErrorFree = false; } } #endregion #region Rate /// /// Import the indicated PT Item /// to an AyaNova Rate record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportPTRate(string PeachtreeID, RateTypes AsRateType, Guid MostLikelyRateUnitChargeDescriptionID, ArrayList alErrors) { DataRow dr = _dtPTItems.Rows.Find(PeachtreeID); //PTListID not found in Rate list? if (dr == null) { alErrors.Add("ImportPTRate: ID not found " + PeachtreeID); return; } string sName = dr["FullName"].ToString(); if (sName.Length > 255) { alErrors.Add("ImportPTRate: Peachtree Rate name exceeds 255 character limit for AyaNova\r\n" + "Name: " + dr["FullName"].ToString() + "\r\n" + "was imported as: " + sName); sName = sName.Substring(0, 255); } try { //already a Rate by that name if (Rate.Exists(Guid.Empty, dr["FullName"].ToString())) { alErrors.Add("ImportPTRate: " + dr["FullName"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Rates rates = Rates.GetItems(false); Rate c = rates.Add(); c.RateType = AsRateType; c.Name = sName; c.Charge = (decimal)dr["Price"]; c.ContractRate = false; c.Cost = (decimal)dr["Cost"]; c.Description = T(255, dr["SalesDesc"].ToString()); c.RateUnitChargeDescriptionID = MostLikelyRateUnitChargeDescriptionID; if (!c.IsSavable) { alErrors.Add("ImportPTRate: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } rates = (Rates)rates.Save(); //Link IntegrationMap m = PTI.Maps.Add(PTI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Rate; m.LastSync = DateTime.Now; m.ForeignID = PeachtreeID; PTI = (Integration)PTI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportPTRate: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); ErrorFree = false; } } #endregion #region Part /// /// Import the indicated PT Item /// to an AyaNova Part record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportPTPart(string PeachtreeID, ArrayList alErrors) { DataRow dr = _dtPTItems.Rows.Find(PeachtreeID); //PTListID not found in Part list? if (dr == null) { alErrors.Add("ImportPTPart: ID not found " + PeachtreeID); return; } string sName = dr["SalesDesc"].ToString(); if (sName.Length > 255) { sName = sName.Substring(0, 255); alErrors.Add("ImportPTPart: Peachtree Part name exceeds 255 character limit for AyaNova part name\r\n" + "Name: " + dr["SalesDesc"].ToString() + "\r\n" + "was imported as: " + sName); } try { //already a Part by that number? if (Part.Exists(Guid.Empty, dr["ID"].ToString())) { alErrors.Add("ImportPTPart: Part number " + dr["ID"].ToString() + " already exists in AyaNova"); return; } //Import seems safe... Part c = Part.NewItem(); c.PartNumber = dr["ID"].ToString(); c.Name = sName; c.WholesalerID = AyaVendorForPTItem(PeachtreeID); c.Retail = (decimal)dr["Price"]; c.Cost = (decimal)dr["Cost"]; if (!c.IsSavable) { alErrors.Add("ImportPTPart: AyaNova won't allow import of " + c.Name + "\r\n" + "Due to the following broken rules:\r\n" + c.GetBrokenRulesString()); return; } //Save and get ready to provide the ID c = (Part)c.Save(); //Link IntegrationMap m = PTI.Maps.Add(PTI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Part; m.LastSync = DateTime.Now; m.ForeignID = PeachtreeID; PTI = (Integration)PTI.Save(); } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; alErrors.Add("ImportPTPart: AyaNova won't allow import / link of " + sName + "\r\n" + "Due to the following error:\r\n" + ex.Message); ErrorFree = false; } } #endregion #endregion Import to AyaNova #region Export to Peachtree #region ID builder /// /// Given a fullname generate a unique ID for Peachtree /// /// Full name of object /// Table to scan for uniqueness /// private static string BuildID(string FullName, DataTable dtDest) { string sID = ""; //generate an id similar to how peachtree sample data is set up //first split the full name into separate groups based on whitespace //Then keep adding to the id the next batch of non whitespace characters //check if already exists, if it does then keep going until it's unique or //the limit of 20 characters has been reached //If the limit has been reached use the first 18 characters plus an incrementing int //up to 99, if it still doesn't come out unique then just throw an exception and bomb out Regex r = new Regex( @"\w+", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled ); bool bGotUniqueID = false; MatchCollection mc = r.Matches(FullName.ToUpper()); foreach (Match m in mc) { if (bGotUniqueID) break; sID = sID + m.Value; if (sID.Length > 20) { sID = sID.Substring(0, 20); //first time in, try it cut off at 20, if unique then bob's your uncle bGotUniqueID = !dtDest.Rows.Contains(sID); if (bGotUniqueID) break; //still not unique so start appending different numbers until it is for (int x = 1; x < 100; x++) { sID = sID.Substring(0, 18) + x.ToString(); bGotUniqueID = !dtDest.Rows.Contains(sID); if (bGotUniqueID) break; } if (!bGotUniqueID) throw new System.ApplicationException("BuildID: Unable to come up with unique ID value for " + FullName + "\r\nCan't import"); } bGotUniqueID = !dtDest.Rows.Contains(sID); } //Case 342 if (!bGotUniqueID) { //We're here because we've run out of matches (as above) and still not got a unique name //and also the item is under 20 characters in length //still not unique so start appending different numbers until it is for (int x = 1; x < 100; x++) { bGotUniqueID = !dtDest.Rows.Contains(sID + x.ToString()); if (bGotUniqueID) { sID = sID + x.ToString(); break; } } if (!bGotUniqueID) throw new System.ApplicationException("BuildID: Unable to come up with unique ID value for " + FullName + "\r\nCan't import"); } return sID; } #endregion #region Customer /// /// Import the indicated client /// to Peachtree customer record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportAyaClient(Guid ClientID, ArrayList alErrors) { if (!Client.Exists(ClientID, "")) { alErrors.Add("ImportAyaClient: Client not found in AyaNova (deleted recently?) (" + ClientID.ToString() + ")"); return; } Client c = Client.GetItem(ClientID); //Case 523 //Note that this is temporary as client is not saved so won't change AyaNova data c.PopulateBothAddresses(); string sName = c.Name; if (sName.Length > 39) { sName = sName.Substring(0, 39); alErrors.Add("ImportAyaClient: AyaNova client name exceeds 39 character limit for Peachtree\r\n" + "Name: " + c.Name + "\r\n" + "will be imported as: " + sName); } string sID = BuildID(sName, PTClients); try { #region Generate import file XmlTextWriter Writer = new XmlTextWriter(TempFilePath + "PTimpcustomers.xml", System.Text.Encoding.UTF8); Writer.WriteStartElement("PAW_Customers"); Writer.WriteAttributeString("xmlns:paw", "urn:schemas-peachtree-com/paw8.02-datatypes"); Writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2000/10/XMLSchema-instance"); Writer.WriteAttributeString("xmlns:xsd", "http://www.w3.org/2000/10/XMLSchema-datatypes"); Writer.WriteStartElement("PAW_Customer"); Writer.WriteAttributeString("xsi:type", "paw:customer"); Writer.WriteStartElement("ID"); Writer.WriteAttributeString("xsi:type", "paw:ID"); Writer.WriteString(sID); Writer.WriteEndElement(); Writer.WriteElementString("Name", sName); Writer.WriteElementString("Account_Number", T(32, c.AccountNumber)); Writer.WriteElementString("Web_Address", T(255, c.WebAddress)); Writer.WriteElementString("ContactName", T(20, c.Contact)); Writer.WriteStartElement("PhoneNumbers"); Writer.WriteStartElement("PhoneNumber"); Writer.WriteAttributeString("Key", "1"); Writer.WriteString(T(20, c.Phone1)); Writer.WriteEndElement(); Writer.WriteStartElement("PhoneNumber"); Writer.WriteAttributeString("Key", "2"); Writer.WriteString(T(20, c.Phone3)); Writer.WriteEndElement(); Writer.WriteEndElement(); Writer.WriteElementString("FaxNumber", T(20, c.Phone2)); Writer.WriteElementString("EMail_Address", T(64, c.Email)); //Bill to address bool bHasBillAddress = false; if (c.MailToAddress.DeliveryAddress != "") { Writer.WriteStartElement("BillToAddress"); bHasBillAddress = true; string[] sad = c.MailToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) Writer.WriteElementString("Line1", T(30, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) Writer.WriteElementString("Line2", T(30, sad[1].TrimEnd())); Writer.WriteElementString("City", T(20, c.MailToAddress.City)); Writer.WriteElementString("State", T(2, c.MailToAddress.StateProv)); Writer.WriteElementString("Zip", T(12, c.MailToAddress.Postal)); Writer.WriteElementString("Country", T(15, c.MailToAddress.Country)); Writer.WriteEndElement(); } //Ship to address bool bHasShipAddress = false; if (!string.IsNullOrEmpty(c.GoToAddress.DeliveryAddress)) { bHasShipAddress = true; string[] sad = c.GoToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) Writer.WriteElementString("Line1", T(30, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) Writer.WriteElementString("Line2", T(30, sad[1].TrimEnd())); Writer.WriteElementString("City", T(20, c.GoToAddress.City)); Writer.WriteElementString("State", T(2, c.GoToAddress.StateProv)); Writer.WriteElementString("Zip", T(12, c.GoToAddress.Postal)); Writer.WriteElementString("Country", T(15, c.GoToAddress.Country)); } Writer.Close(); #endregion generate import file #region Import PeachtreeAccounting.Import importer = (PeachtreeAccounting.Import)ptApp.app.CreateImporter(PeachwIEObj.peachwIEObjCustomerList); importer.ClearImportFieldList(); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerName); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerAccountNumber); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerURL); //stuff related to ayanova contact (phone numbers email contact name etc) importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerContact); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerPhone1); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerPhone2); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerFax); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerEmail); //Bill to address (mailing) if (bHasBillAddress) { importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToAddressLine1); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToAddressLine2); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToCity); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToState); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToZip); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerBillToCountry); } //Physical SHIP TO address if (bHasShipAddress) { importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1AddressLine1); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1AddressLine2); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1City); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1State); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1Zip); importer.AddToImportFieldList((short)PeachwIEObjCustomerListField.peachwIEObjCustomerListField_CustomerShipTo1Country); } importer.SetFilename(TempFilePath + "PTimpcustomers.xml"); importer.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); #if(PEACHW14) Array arout; importer.ImportAndReturnGUIDs(out arout); if (arout.GetLength(0) < 1) throw new System.ApplicationException("Peachtree did not accept customer " + sName + " - no error given"); #else importer.Import(); #endif #endregion #region Add to cache and link //create a new row to add to the client cache datatable DataRow dr = PTClients.NewRow(); dr["ID"] = sID; dr["FullName"] = sName; dr["MailAddress"] = new Address(); dr["StreetAddress"] = new Address(); dr["Phone"] = ""; dr["Fax"] = ""; dr["AltPhone"] = ""; dr["Email"] = ""; dr["Contact"] = ""; dr["Created"] = DateTime.MinValue;//flag indicating fresh record incomplete dr["Modified"] = DateTime.MinValue;//ditto dr["Account"] = ""; //add the new row for the newly imported object PTClients.Rows.Add(dr); //Link IntegrationMap m = PTI.Maps.Add(PTI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Client; m.LastSync = DateTime.Now; m.ForeignID = sID; PTI = (Integration)PTI.Save(); #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "ImportAyaClient: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion customer #region Vendor /// /// Import the indicated Vendor /// to Peachtree Vendor record /// /// /// An arraylist to hold strings indicating errors on fail public static void ImportAyaVendor(Guid VendorID, ArrayList alErrors) { if (!Vendor.Exists(VendorID, "")) { alErrors.Add("ImportAyaVendor: Vendor not found in AyaNova (deleted recently?) (" + VendorID.ToString() + ")"); return; } Vendor c = Vendor.GetItem(VendorID); string sName = c.Name; if (sName.Length > 39) { sName = sName.Substring(0, 39); alErrors.Add("ImportAyaVendor: AyaNova Vendor name exceeds 39 character limit for Peachtree\r\n" + "Name: " + c.Name + "\r\n" + "will be imported as: " + sName); } string sID = BuildID(sName, PTVendors); try { #region Generate import file XmlTextWriter Writer = new XmlTextWriter(TempFilePath + "PTimpvendors.xml", System.Text.Encoding.UTF8); Writer.WriteStartElement("PAW_Vendors"); Writer.WriteAttributeString("xmlns:paw", "urn:schemas-peachtree-com/paw8.02-datatypes"); Writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2000/10/XMLSchema-instance"); Writer.WriteAttributeString("xmlns:xsd", "http://www.w3.org/2000/10/XMLSchema-datatypes"); Writer.WriteStartElement("PAW_Vendor"); Writer.WriteAttributeString("xsi:type", "paw:vendor"); Writer.WriteStartElement("ID"); Writer.WriteAttributeString("xsi:type", "paw:ID"); Writer.WriteString(sID); Writer.WriteEndElement(); Writer.WriteElementString("Name", sName); Writer.WriteElementString("Web_Address", T(255, c.WebAddress)); Writer.WriteElementString("AccountNumber", T(32, c.AccountNumber)); Writer.WriteElementString("ContactName", T(20, c.Contact)); Writer.WriteStartElement("PhoneNumbers"); Writer.WriteStartElement("PhoneNumber"); Writer.WriteAttributeString("Key", "1"); Writer.WriteString(T(20, c.Phone1)); Writer.WriteEndElement(); Writer.WriteStartElement("PhoneNumber"); Writer.WriteAttributeString("Key", "2"); Writer.WriteString(T(20, c.Phone3)); Writer.WriteEndElement(); Writer.WriteEndElement(); Writer.WriteElementString("FaxNumber", T(20, c.Phone2)); Writer.WriteElementString("EMail_Address", T(64, c.Email)); //Bill to address bool bHasBillAddress = false; if (c.MailToAddress.DeliveryAddress != "") { Writer.WriteStartElement("RemitToAddress"); bHasBillAddress = true; string[] sad = c.MailToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) Writer.WriteElementString("Line1", T(30, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) Writer.WriteElementString("Line2", T(30, sad[1].TrimEnd())); Writer.WriteElementString("City", T(20, c.MailToAddress.City)); Writer.WriteElementString("State", T(2, c.MailToAddress.StateProv)); Writer.WriteElementString("Zip", T(12, c.MailToAddress.Postal)); Writer.WriteElementString("Country", T(15, c.MailToAddress.Country)); Writer.WriteEndElement(); } //Ship to address bool bHasShipAddress = false; if (!string.IsNullOrEmpty(c.GoToAddress.DeliveryAddress)) { bHasShipAddress = true; string[] sad = c.GoToAddress.DeliveryAddress.Replace("\r\n", "\n").Split('\n'); if (sad.GetLength(0) > 0) Writer.WriteElementString("Line1", T(30, sad[0].TrimEnd())); if (sad.GetLength(0) > 1) Writer.WriteElementString("Line2", T(30, sad[1].TrimEnd())); Writer.WriteElementString("City", T(20, c.GoToAddress.City)); Writer.WriteElementString("State", T(2, c.GoToAddress.StateProv)); Writer.WriteElementString("Zip", T(12, c.GoToAddress.Postal)); Writer.WriteElementString("Country", T(15, c.GoToAddress.Country)); } Writer.Close(); #endregion generate import file #region Import PeachtreeAccounting.Import importer = (PeachtreeAccounting.Import)ptApp.app.CreateImporter(PeachwIEObj.peachwIEObjVendorList); importer.ClearImportFieldList(); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorName); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_byVendorURL); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorOurAccountId); //stuff related to ayanova contact (phone numbers email contact name etc) importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorContact); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorPhone1); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorPhone2); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorFax); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorEmail); //Remit to address (mailing) if (bHasBillAddress) { importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1AddressLine1); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1AddressLine2); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1City); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1State); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1Zip); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_RemitTo1Country); } //Physical SHIP TO address if (bHasShipAddress) { importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorAddressLine1); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorAddressLine2); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorCity); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorState); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorZip); importer.AddToImportFieldList((short)PeachwIEObjVendorListField.peachwIEObjVendorListField_VendorCountry); } importer.SetFilename(TempFilePath + "PTimpvendors.xml"); importer.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); #if(PEACHW14) Array arout; importer.ImportAndReturnGUIDs(out arout); if (arout.GetLength(0) < 1) throw new System.ApplicationException("Peachtree did not accept vendor " + sName + " - no error given"); #else importer.Import(); #endif #endregion #region Add to cache and link //create a new row to add to the vendor cache datatable DataRow dr = PTVendors.NewRow(); dr["ID"] = sID; dr["FullName"] = sName; dr["MailAddress"] = new Address(); dr["StreetAddress"] = new Address(); dr["Phone"] = ""; dr["Fax"] = ""; dr["AltPhone"] = ""; dr["Email"] = ""; dr["Contact"] = ""; dr["Created"] = DateTime.MinValue;//flag indicating fresh record incomplete dr["Modified"] = DateTime.MinValue;//ditto dr["Account"] = ""; //add the new row for the newly imported object PTVendors.Rows.Add(dr); //Link IntegrationMap m = PTI.Maps.Add(PTI); m.Name = sName; m.RootObjectID = c.ID; m.RootObjectType = RootObjectTypes.Vendor; m.LastSync = DateTime.Now; m.ForeignID = sID; PTI = (Integration)PTI.Save(); #endregion } catch (Exception ex) { IntegrationLog.Log(PTID, "ImportAyaVendor: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion vendor private static string T(int nLength, string s) { if (s == null || s == "") return ""; if (s.Length <= nLength) return s; else return s.Substring(0, nLength); } #endregion export to peachtree #region Workorder mismatch scanning public enum MisMatchReason { NotLinkedToPT = 0, PriceDifferent = 1, NothingToInvoice = 2 } /// /// Mismatch properties /// A structure for storing mismatches identified ///so user can resolve them. /// public struct MisMatch { //public Guid WorkorderID; public Guid mRootObjectID; public RootObjectTypes mObjectType; public string mName; public MisMatchReason mReason; public decimal mAyaPrice; public decimal mPTPrice; public Guid mWorkorderItemPartID; public string mPTListID; public Guid RootObjectID { get { return mRootObjectID; } } public RootObjectTypes ObjectType { get { return mObjectType; } } public string Name { get { return mName; } } public MisMatchReason Reason { get { return mReason; } } public decimal AyaPrice { get { return mAyaPrice; } } public decimal PTPrice { get { return mPTPrice; } } public Guid WorkorderItemPartID { get { return mWorkorderItemPartID; } } public string PTListID { get { return mPTListID; } } } /// /// Given a workorder ID /// scans the objects in the workorder /// that need to be linked to PT for invoicing /// and on any error found adds them to the /// mismatched object array list /// /// Id of workorder being scanned /// An arraylist of mismatch objects /// An array of GUID values of workorderitemparts that have been set by /// user to forcibly use the price set on the workorderitem part even though /// it differs from the quickbooks price /// True if all links ok, false if there are any mismatches at all public static bool ScanLinksOK(Guid WorkorderID, ArrayList MisMatches, ArrayList PriceOverrides) { bool bReturn = true; bool bSomethingToInvoice = false; Workorder w = Workorder.GetItem(WorkorderID); //Client ok? if (!PTI.Maps.Contains(w.ClientID)) { bReturn = false; AddMisMatch(AyaClientList[w.ClientID].Name, w.ClientID, RootObjectTypes.Client, MisMatchReason.NotLinkedToPT, MisMatches); } //Service rates: foreach (WorkorderItem wi in w.WorkorderItems) { #region Labor foreach (WorkorderItemLabor wl in wi.Labors) { //If there's *any* labor then there is something to invoice bSomethingToInvoice = true; //Check that rate isn't actually guid.empty //it's possible that some users have not selected a rate on the workorder if (wl.ServiceRateID == Guid.Empty) throw new System.ApplicationException("ERROR: Workorder " + w.WorkorderService.ServiceNumber.ToString() + " has a labor item with no rate selected\r\n" + "This is a serious problem for PTI and needs to be rectified before PTI can be used.\r\n"); if (!PTI.Maps.Contains(wl.ServiceRateID)) { bReturn = false; AddMisMatch(AyaRateList[wl.ServiceRateID].Name, wl.ServiceRateID, RootObjectTypes.Rate, MisMatchReason.NotLinkedToPT, MisMatches); } } #endregion #region Travel foreach (WorkorderItemTravel wt in wi.Travels) { //If there's *any* travel then there is something to invoice bSomethingToInvoice = true; //Check that rate isn't actually guid.empty //it's possible that some users have not selected a rate on the workorder if (wt.TravelRateID == Guid.Empty) throw new System.ApplicationException("ERROR: Workorder " + w.WorkorderService.ServiceNumber.ToString() + " has a travel item with no rate selected\r\n" + "This is a serious problem for PTI and needs to be rectified before PTI can be used.\r\n"); if (!PTI.Maps.Contains(wt.TravelRateID)) { bReturn = false; AddMisMatch(AyaRateList[wt.TravelRateID].Name, wt.TravelRateID, RootObjectTypes.Rate, MisMatchReason.NotLinkedToPT, MisMatches); } } #endregion #region Parts foreach (WorkorderItemPart wp in wi.Parts) { //If there's *any* parts then there is something to invoice bSomethingToInvoice = true; //Changed: 14-Nov-2006 to check that linked item id exists in qb if (!PTI.Maps.Contains(wp.PartID) || PTItems.Rows.Find(PTI.Maps[wp.PartID].ForeignID) == null) { bReturn = false; //Changed: 21-June-2006 to use display formatted name AddMisMatch(AyaPartList[wp.PartID].DisplayName(Util.GlobalSettings.DefaultPartDisplayFormat), wp.PartID, RootObjectTypes.Part, MisMatchReason.NotLinkedToPT, MisMatches); } else { //check the price if (!PriceOverrides.Contains(wp.ID)) { decimal qbPrice = (decimal)PTItems.Rows.Find(PTI.Maps[wp.PartID].ForeignID)["Price"]; //------------DISCOUNT----------------- string disco = ""; //Added:20-July-2006 to incorporate discounts on parts into qb invoice decimal charge; //Changed: 18-Nov-2006 CASE 158 //this is all wrong, it was multiplying price by quantity to calculate charge when it shouldn't //removed quanty * price in next line to just price charge = decimal.Round(wp.Price, 2, MidpointRounding.AwayFromZero); charge = charge - (decimal.Round(charge * wp.Discount, 2, MidpointRounding.AwayFromZero)); if (wp.Discount != 0) { disco = " (Price " + wp.Price.ToString("c") + " discounted on workorder " + wp.Discount.ToString("p") + ") \r\n"; } //----------------------------- //It's a match, let's see if the price matches as well if (charge != qbPrice) { bReturn = false; AddMisMatch("WO: " + w.WorkorderService.ServiceNumber.ToString() + disco + " Part: " + AyaPartList[wp.PartID].DisplayName(Util.GlobalSettings.DefaultPartDisplayFormat),//Changed: 21-June-2006 to use display formatted name wp.PartID, RootObjectTypes.Part, MisMatchReason.PriceDifferent, MisMatches, qbPrice, charge, wp.ID, PTI.Maps[wp.PartID].ForeignID); } } } } #endregion #region Outside service charges if (wi.HasOutsideService) { if (wi.OutsideService.RepairPrice != 0 || wi.OutsideService.ShippingPrice != 0) { bSomethingToInvoice = true; //there is something billable, just need to make sure //that there is a PT charge defined for outside service if (PDat.OutsideServiceChargeAs == null || PDat.OutsideServiceChargeAs == "" || !PTItems.Rows.Contains(PDat.OutsideServiceChargeAs)) { bReturn = false; AddMisMatch("Outside service", Guid.Empty, RootObjectTypes.WorkorderItemOutsideService, MisMatchReason.NotLinkedToPT, MisMatches); } } } #endregion #region Workorder item loan charges if (wi.HasLoans) { foreach (WorkorderItemLoan wil in wi.Loans) { if (wil.Charges != 0) { //case 733 bSomethingToInvoice = true; //there is something billable, just need to make sure //that there is a PT charge defined for loaned item charges if (PDat.WorkorderItemLoanChargeAs == null || PDat.WorkorderItemLoanChargeAs == "" || !PTItems.Rows.Contains(PDat.WorkorderItemLoanChargeAs)) { bReturn = false; AddMisMatch("Workorder item loan", Guid.Empty, RootObjectTypes.WorkorderItemLoan, MisMatchReason.NotLinkedToPT, MisMatches); break; } } } } #endregion #region Workorder item misc expenses if (wi.HasExpenses) { foreach (WorkorderItemMiscExpense wie in wi.Expenses) { if (wie.ChargeToClient) { bSomethingToInvoice = true; //there is something billable, just need to make sure //that there is a PT charge defined for misc expense item charges if (PDat.MiscExpenseChargeAs == null || PDat.MiscExpenseChargeAs == "" || !PTItems.Rows.Contains(PDat.MiscExpenseChargeAs)) { bReturn = false; AddMisMatch("Workorder item expense", Guid.Empty, RootObjectTypes.WorkorderItemMiscExpense, MisMatchReason.NotLinkedToPT, MisMatches); break; } } } } #endregion }//workorder items loop //If there are no mismatches so far, //maybe it's because it's got nothing to invoice? if (bReturn && !bSomethingToInvoice) { bReturn = false; AddMisMatch("WO: " + w.WorkorderService.ServiceNumber.ToString() + " - Nothing chargeable on it, will not be invoiced", Guid.Empty, RootObjectTypes.Nothing, MisMatchReason.NothingToInvoice, MisMatches); } return bReturn; } private static void AddMisMatch(string Name, Guid RootObjectID, RootObjectTypes RootObjectType, MisMatchReason Reason, ArrayList Mismatches) { AddMisMatch(Name, RootObjectID, RootObjectType, Reason, Mismatches, 0m, 0m, Guid.Empty, ""); } private static void AddMisMatch(string Name, Guid RootObjectID, RootObjectTypes RootObjectType, MisMatchReason Reason, ArrayList Mismatches, decimal PTPrice, decimal AyaPrice, Guid WorkorderItemPartID, string PTListID) { bool bDuplicate = false; //scan through list of existing mismatches, //only add a not linked item if it's //not there already //other types of mismatches need to be added because //they need to be resolved on a case by case basis or are unresolvable if (Reason == MisMatchReason.NotLinkedToPT) { foreach (object o in Mismatches) { MisMatch m = (MisMatch)o; //Have to check ID and type here because for outside service //and loans and misc expenses the id is always empty so type is //the only way to differentiate if (m.RootObjectID == RootObjectID && m.ObjectType == RootObjectType) { bDuplicate = true; break; } } } if (!bDuplicate) { MisMatch m = new MisMatch(); m.mName = Name; m.mRootObjectID = RootObjectID; m.mObjectType = RootObjectType; m.mReason = Reason; m.mAyaPrice = AyaPrice; m.mPTPrice = PTPrice; m.mWorkorderItemPartID = WorkorderItemPartID; m.mPTListID = PTListID; Mismatches.Add(m); } } #endregion wo_mismatch_scan #region Invoice /// /// Invoice out the workorders contained in the array list /// as one single invoice /// /// Put in descriptive text as necessary and when sucessfully invoiced /// set workorders in list to status selected for post invoicing and /// set invoice number and then close them if autoclose option selected /// /// /// public static void Invoice(ArrayList alWorkorders, ArrayList alErrors) { if (alWorkorders.Count == 0) return; //a string to hold the memo field StringBuilder sbInternalNotes = new StringBuilder(); if (PDat.SetMemoField) { sbInternalNotes.Append("Imported by: " + Thread.CurrentPrincipal.Identity.Name); if (alWorkorders.Count > 1) { sbInternalNotes.Append(" Workorders: "); } else { sbInternalNotes.Append(" Workorder: "); } } //Notes stringbuilder to hold text that will //be constructed from templates StringBuilder sbNotes = new StringBuilder(); DataTable i = new DataTable("InvoiceLines"); //setup the columns i.Columns.Add("ID", typeof(string)); i.Columns.Add("Description", typeof(string)); i.Columns.Add("Quantity", typeof(decimal)); i.Columns.Add("Price", typeof(decimal)); i.Columns.Add("Amount", typeof(decimal)); //Case 533 i.Columns.Add("Serial_Number", typeof(string)); string sInvoiceNumber = ""; Workorder w = null; try { #region Generate import file XmlTextWriter Writer = new XmlTextWriter(TempFilePath + "PTimpinvoices.xml", System.Text.Encoding.UTF8); Writer.WriteStartElement("PAW_Invoices"); Writer.WriteAttributeString("xmlns:paw", "urn:schemas-peachtree-com/paw8.02-datatypes"); Writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2000/10/XMLSchema-instance"); Writer.WriteAttributeString("xmlns:xsd", "http://www.w3.org/2000/10/XMLSchema-datatypes"); Writer.WriteStartElement("PAW_Invoice"); Writer.WriteAttributeString("xsi:type", "paw:invoice"); //Get first workorder for setting header stuff w = Workorder.GetItem((Guid)alWorkorders[0]); Writer.WriteStartElement("Customer_ID"); Writer.WriteAttributeString("xsi:type", "paw:id"); Writer.WriteString(PTI.Maps[w.ClientID].ForeignID); Writer.WriteEndElement(); //Set invoice number, it was decided to use first workorder's number even if //multiple wo's on invoice (probably no other way to do it really) // string sInvoiceNumber = "WO" + w.WorkorderService.ServiceNumber + "TEST" + DateTime.Now.ToString("T"); //sInvoiceNumber = sInvoiceNumber.Replace(" ", "").Replace(":", ""); sInvoiceNumber = "WO" + w.WorkorderService.ServiceNumber; Writer.WriteElementString("Invoice_Number", sInvoiceNumber); Writer.WriteStartElement("Date"); Writer.WriteAttributeString("xsi:type", "paw:date"); Writer.WriteString(DateTime.Now.ToString("M/d/yy"));//Date - Date of the transaction. Must be in ##/##/## (month/day/yr) format. Writer.WriteEndElement(); Writer.WriteStartElement("Accounts_Receivable_Account"); Writer.WriteAttributeString("xsi:type", "paw:id"); Writer.WriteString(DefaultARAccount); Writer.WriteEndElement(); string poField = ""; #region Build Invoice line items //Loop through alworkorders foreach (object o in alWorkorders) { w = Workorder.GetItem((Guid)o); if (!string.IsNullOrEmpty(w.CustomerReferenceNumber)) poField += w.CustomerReferenceNumber + ","; string sWONum = w.WorkorderService.ServiceNumber.ToString(); InvoiceAddTextLine(i, "WO#: " + sWONum); if (PDat.SetMemoField) sbInternalNotes.Append(sWONum + ","); #region Invoice header text if (PDat.HasInvoiceHeaderTemplate) { string s = PDat.InvoiceHeaderTemplate; if (s.IndexOf("~WO#~") != -1) { s = s.Replace("~WO#~", w.WorkorderService.ServiceNumber.ToString()); } if (s.IndexOf("~CONTACT~") != -1) { s = s.Replace("~CONTACT~", w.CustomerContactName); } if (s.IndexOf("~CREF#~") != -1) { s = s.Replace("~CREF#~", w.CustomerReferenceNumber); } if (s.IndexOf("~OURREF#~") != -1) { s = s.Replace("~OURREF#~", w.InternalReferenceNumber); } if (s.IndexOf("~PROJ~") != -1) { if (w.ProjectID == Guid.Empty) s = s.Replace("~PROJ~", ""); else s = s.Replace("~PROJ~", NameFetcher.GetItem("aProject", "aName", w.ProjectID).RecordName); } if (s.IndexOf("~CLIENT~") != -1) { s = s.Replace("~CLIENT~", NameFetcher.GetItem("aClient", "aName", w.ClientID).RecordName); } if (s.IndexOf("~SERVDATE~") != -1) { s = s.Replace("~SERVDATE~", w.WorkorderService.ServiceDate.ToString()); } if (s.IndexOf("~STAT~") != -1) { if (w.WorkorderService.WorkorderStatusID == Guid.Empty) s = s.Replace("~STAT~", ""); else s = s.Replace("~STAT~", NameFetcher.GetItem("aWorkorderStatus", "aName", w.WorkorderService.WorkorderStatusID).RecordName); } if (s.IndexOf("~DESC~") != -1) { s = s.Replace("~DESC~", w.Summary); } InvoiceAddNote(sbNotes, s); } #endregion header text #region Part charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemPart p in it.Parts) { //------------DISCOUNT----------------- //Added:20-July-2006 to incorporate discounts on parts into qb invoice decimal charge; //Case 269 this is incorrect: //charge = decimal.Round(p.Quantity * p.Price, 2, MidpointRounding.AwayFromZero); charge = decimal.Round(1 * p.Price, 2, MidpointRounding.AwayFromZero); charge = charge - (decimal.Round(charge * p.Discount, 2, MidpointRounding.AwayFromZero)); //----------------------------- string sn = ""; if (p.PartSerialID != Guid.Empty) { sn = PartSerial.GetSerialNumberFromPartSerialID(p.PartSerialID); } else if (!string.IsNullOrEmpty(p.Description) && p.Description.Length < 31)//Case 533 { //No serial number but there is a description so were going to call it the sn //Note that PT appears to only allow a max sn of 30 characters so sanity check of 31 sn = p.Description; } //Case 533 InvoiceAddCharge(i, PTI.Maps[p.PartID].ForeignID, p.Quantity, charge, sn); if (sn != "") InvoiceAddTextLine(i, "SN: " + sn); //Added:18-Nov-2006 case 125 //checks for nonempty description, also checks to see if description is //same as serial number because it's copied there in some cases and no sense //in showing it twice if (p.Description != "" && sn != p.Description) InvoiceAddTextLine(i, p.Description); } } #endregion part charges #region Service charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemLabor l in it.Labors) { //Added 20-July-2006 to not charge for banked hours if (l.ServiceBankID != Guid.Empty) { InvoiceAddCharge(i, PTI.Maps[l.ServiceRateID].ForeignID, l.ServiceRateQuantity, 0); } else InvoiceAddCharge(i, PTI.Maps[l.ServiceRateID].ForeignID, l.ServiceRateQuantity, AyaRateList[l.ServiceRateID].Charge); } } #endregion Service charges #region Travel charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemTravel l in it.Travels) { InvoiceAddCharge(i, PTI.Maps[l.TravelRateID].ForeignID, l.TravelRateQuantity, AyaRateList[l.TravelRateID].Charge); } } #endregion Travel charges #region MiscExpense charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemMiscExpense l in it.Expenses) { if (l.ChargeToClient) { InvoiceAddCharge(i, PDat.MiscExpenseChargeAs, 1, l.ChargeAmount); } } } #endregion MiscExpense charges #region Loaner charges foreach (WorkorderItem it in w.WorkorderItems) { foreach (WorkorderItemLoan l in it.Loans) { InvoiceAddCharge(i, PDat.WorkorderItemLoanChargeAs, 1, l.Charges); } } #endregion Loaner charges #region OutsideService charges foreach (WorkorderItem it in w.WorkorderItems) { if (it.HasOutsideService && (it.OutsideService.ShippingPrice != 0 || it.OutsideService.RepairPrice != 0)) { InvoiceAddCharge(i, PDat.OutsideServiceChargeAs, 1, it.OutsideService.ShippingPrice + it.OutsideService.RepairPrice); } } #endregion OutsideService charges #region Invoice footer text //Loop through workorder items //inserting descriptive text as required if (PDat.HasAnyInvoiceFooterTemplateFields) { foreach (WorkorderItem it in w.WorkorderItems) { #region Item (footer) fields if (PDat.InvoiceFooterTemplate != "") { string s = PDat.InvoiceFooterTemplate; if (s.IndexOf("~ITEM_SUMMARY~") != -1) { s = s.Replace("~ITEM_SUMMARY~", it.Summary); } if (s.IndexOf("~ITEM_SERVICE_NOTES~") != -1) { s = s.Replace("~ITEM_SERVICE_NOTES~", it.TechNotes); } if (s.IndexOf("~ITEM_TYPE~") != -1) { if (it.TypeID == Guid.Empty) s = s.Replace("~ITEM_TYPE~", ""); else s = s.Replace("~ITEM_TYPE~", NameFetcher.GetItem("aWorkorderItemType", "aName", it.TypeID).RecordName); } if (s.IndexOf("~ITEM_REQUEST_DATE~") != -1) { s = s.Replace("~ITEM_REQUEST_DATE~", it.RequestDate.ToString()); } if (s.IndexOf("~ITEM_STATUS~") != -1) { if (it.WorkorderStatusID == Guid.Empty) s = s.Replace("~ITEM_STATUS~", ""); else s = s.Replace("~ITEM_STATUS~", NameFetcher.GetItem("aWorkorderStatus", "aName", it.WorkorderStatusID).RecordName); } InvoiceAddNote(sbNotes, s); } #endregion item #region Unit fields if (PDat.InvoiceUnitTemplate != "" && it.UnitID != Guid.Empty) { string s = PDat.InvoiceUnitTemplate; UnitPickList up = UnitPickList.GetListOfOneSpecificUnit(it.UnitID); if (s.IndexOf("~AYAFORMAT~") != -1) { s = s.Replace("~AYAFORMAT~", up[0].UnitName()); } if (s.IndexOf("~UNIT_SN~") != -1) { s = s.Replace("~UNIT_SN~", up[0].Serial); } if (s.IndexOf("~UNIT_METER~") != -1) { if (!up[0].Metered) s = s.Replace("~UNIT_METER~", ""); else s = s.Replace("~UNIT_METER~", Unit.LastMeterReading(up[0].ID).ToString()); } if (s.IndexOf("~UNIT_MAKE~") != -1) { s = s.Replace("~UNIT_MAKE~", up[0].VendorName); } if (s.IndexOf("~UNIT_MODEL_NAME~") != -1) { s = s.Replace("~UNIT_MODEL_NAME~", up[0].ModelName); } if (s.IndexOf("~UNIT_MODEL_NUMBER~") != -1) { s = s.Replace("~UNIT_MODEL_NUMBER~", up[0].ModelNumber); } InvoiceAddNote(sbNotes, s); } #endregion unit #region Labor fields if (PDat.InvoiceServiceTemplate != "" && it.HasLabor) { foreach (WorkorderItemLabor wl in it.Labors) { string s = PDat.InvoiceServiceTemplate; if (s.IndexOf("~SERVICE_START~") != -1) { s = s.Replace("~SERVICE_START~", wl.ServiceStartDate.ToString()); } if (s.IndexOf("~SERVICE_STOP~") != -1) { s = s.Replace("~SERVICE_STOP~", wl.ServiceStopDate.ToString()); } if (s.IndexOf("~SERVICE_QUANTITY~") != -1) { s = s.Replace("~SERVICE_QUANTITY~", wl.ServiceRateQuantity.ToString()); } if (s.IndexOf("~NO_CHARGE_QUANTITY~") != -1) { s = s.Replace("~NO_CHARGE_QUANTITY~", wl.NoChargeQuantity.ToString()); } if (s.IndexOf("~RATE_NAME~") != -1) { s = s.Replace("~RATE_NAME~", AyaRateList[wl.ServiceRateID].Name); } if (s.IndexOf("~SERVICE_TECH~") != -1) { s = s.Replace("~SERVICE_TECH~", UserPickList.GetListOfOneSpecificUser(wl.UserID)[0].Name); } if (s.IndexOf("~DETAILS~") != -1) { s = s.Replace("~DETAILS~", wl.ServiceDetails); } InvoiceAddNote(sbNotes, s); } } #endregion service #region Travel fields if (PDat.InvoiceTravelTemplate != "" && it.HasTravel) { foreach (WorkorderItemTravel wt in it.Travels) { string s = PDat.InvoiceTravelTemplate; if (s.IndexOf("~TRAVEL_START~") != -1) { s = s.Replace("~TRAVEL_START~", wt.TravelStartDate.ToString()); } if (s.IndexOf("~TRAVEL_STOP~") != -1) { s = s.Replace("~TRAVEL_STOP~", wt.TravelStopDate.ToString()); } if (s.IndexOf("~TRAVEL_QUANTITY~") != -1) { s = s.Replace("~TRAVEL_QUANTITY~", wt.TravelRateQuantity.ToString()); } if (s.IndexOf("~TRAVEL_NO_CHARGE_QUANTITY~") != -1) { s = s.Replace("~TRAVEL_NO_CHARGE_QUANTITY~", wt.NoChargeQuantity.ToString()); } if (s.IndexOf("~TRAVEL_RATE_NAME~") != -1) { s = s.Replace("~TRAVEL_RATE_NAME~", AyaRateList[wt.TravelRateID].Name); } if (s.IndexOf("~TRAVEL_TECH~") != -1) { s = s.Replace("~TRAVEL_TECH~", UserPickList.GetListOfOneSpecificUser(wt.UserID)[0].Name); } if (s.IndexOf("~TRAVEL_DETAILS~") != -1) { s = s.Replace("~TRAVEL_DETAILS~", wt.TravelDetails); } if (s.IndexOf("~TRAVEL_DISTANCE~") != -1) { s = s.Replace("~TRAVEL_DISTANCE~", wt.Distance.ToString()); } InvoiceAddNote(sbNotes, s); } } #endregion travel fields #region Outside fields if (PDat.OutsideServiceChargeAs != "" && it.HasOutsideService) { string s = PDat.InvoiceOutsideServiceTemplate; if (s.IndexOf("~REPAIR_PRICE~") != -1) { s = s.Replace("~REPAIR_PRICE~", it.OutsideService.RepairPrice.ToString("c")); } if (s.IndexOf("~SHIP_CHARGE~") != -1) { s = s.Replace("~SHIP_CHARGE~", it.OutsideService.ShippingPrice.ToString("c")); } if (s.IndexOf("~SENT~") != -1) { s = s.Replace("~SENT~", it.OutsideService.DateSent.ToString()); } if (s.IndexOf("~RETURNED~") != -1) { s = s.Replace("~RETURNED~", it.OutsideService.DateReturned.ToString()); } if (s.IndexOf("~NOTES~") != -1) { s = s.Replace("~NOTES~", it.OutsideService.Notes); } InvoiceAddNote(sbNotes, s); } #endregion outside service #region Misc expense fields if (PDat.InvoiceMiscExpenseTemplate != "" && it.HasExpenses) { foreach (WorkorderItemMiscExpense e in it.Expenses) { string s = PDat.InvoiceMiscExpenseTemplate; if (s.IndexOf("~CHARGES~") != -1) { s = s.Replace("~CHARGES~", e.ChargeAmount.ToString("c")); } if (s.IndexOf("~SUMMARY~") != -1) { s = s.Replace("~SUMMARY~", e.Name); } if (s.IndexOf("~DESCRIQBION~") != -1) { s = s.Replace("~DESCRIQBION~", e.Description); } if (s.IndexOf("~TECH~") != -1) { s = s.Replace("~TECH~", UserPickList.GetListOfOneSpecificUser(e.UserID)[0].Name); } InvoiceAddNote(sbNotes, s); } } #endregion misc expense #region Loan item fields if (PDat.InvoiceLoanItemTemplate != "" && it.HasLoans) { foreach (WorkorderItemLoan l in it.Loans) { string s = PDat.InvoiceLoanItemTemplate; if (s.IndexOf("~CHARGE~") != -1) { s = s.Replace("~CHARGE~", l.Charges.ToString("c")); } if (s.IndexOf("~ITEM~") != -1) { s = s.Replace("~ITEM~", NameFetcher.GetItem("aLoanItem", "aName", l.LoanItemID).RecordName); } if (s.IndexOf("~LOANED~") != -1) { s = s.Replace("~LOANED~", l.OutDate.ToString()); } if (s.IndexOf("~LOAN_RETURNED~") != -1) { s = s.Replace("~LOAN_RETURNED~", l.ReturnDate.ToString()); } if (s.IndexOf("~LOAN_NOTES~") != -1) { s = s.Replace("~LOAN_NOTES~", l.Notes); } InvoiceAddNote(sbNotes, s); } } #endregion loan item expense } } #endregion }//Bottom of foreach workorder loop if (!string.IsNullOrEmpty(poField)) { Writer.WriteElementString("Customer_PO", T(20, poField.TrimEnd(','))); } if (sbNotes.Length > 0) { //write out a max of 2000 chars to the internal notes element Writer.WriteElementString("Note", T(2000, sbNotes.ToString())); Writer.WriteElementString("Note_Prints_After_Line_Items", "TRUE"); } //Set memo field if (PDat.SetMemoField) { //write out a max of 2000 chars to the internal notes element Writer.WriteElementString("Internal_Note", T(2000, sbInternalNotes.ToString()).TrimEnd(',')); } #endregion Build line items decimal totalamount = 0; decimal taxableamount = 0; bool IsTax = false; foreach (DataRow dr in i.Rows) { if (dr["ID"].ToString() != "") //only if id is not empty (which means a text only line) { totalamount += Convert.ToDecimal(dr["Amount"].ToString()); if (IsTaxable(dr["ID"].ToString())) taxableamount += Convert.ToDecimal(dr["Amount"].ToString()); } } if (taxableamount != 0) IsTax = true; #region Case 539 - Terms DataRow drTerms = _dtPTClients.Rows.Find(PTI.Maps[w.ClientID].ForeignID); if (drTerms == null) { alErrors.Add("Invoice, add payment terms: Peachtree client not found " + PTI.Maps[w.ClientID].Name); return; } DateTime dateDue; DateTime discountDate; decimal discountAmount; string displayedTerms = ""; int nDueDays = (int)drTerms["Due_Days"]; //Due Date if ((bool)drTerms["Use_Due_Month_End_Terms"] == true) { //set for last day of this month dateDue = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.DaysInMonth(DateTime.Today.Year, DateTime.Today.Month)); displayedTerms = "Net " + dateDue.ToString("M/d/yy"); } else if ((bool)drTerms["Terms_Type"] == true) { //Due on specific day of next month //in this scenario PT uses the due days field to indicate which day of the month dateDue = DateTime.Today.AddMonths(1); dateDue = new DateTime(dateDue.Year, dateDue.Month, nDueDays); displayedTerms = "Net " + dateDue.ToString("M/d/yy"); } else { //due in today plus due days time dateDue = DateTime.Today.AddDays(nDueDays); displayedTerms = "Net " + nDueDays.ToString() + " days"; } //Discount date discountDate = DateTime.Today.AddDays((int)drTerms["Discount_Days"]); //Discount amount discountAmount = 0; if ((decimal)drTerms["Discount_Percent"] != 0) { decimal percentage = (decimal)drTerms["Discount_Percent"] / 100; discountAmount = percentage * Math.Abs(totalamount); discountAmount = decimal.Round(discountAmount, 2, MidpointRounding.AwayFromZero); displayedTerms = ((decimal)drTerms["Discount_Percent"]).ToString("G29") + "% " + drTerms["Discount_Days"].ToString() + ", " + displayedTerms; } Writer.WriteStartElement("Date_Due"); Writer.WriteAttributeString("xsi:type", "paw:date"); Writer.WriteString(dateDue.ToString("M/d/yy")); Writer.WriteEndElement(); Writer.WriteElementString("Discount_Amount", discountAmount.ToString());//PT's sample data shows this as the percentage off the after tax invoice total Writer.WriteStartElement("Discount_Date"); Writer.WriteAttributeString("xsi:type", "paw:date"); Writer.WriteString(discountDate.ToString("M/d/yy")); Writer.WriteEndElement(); Writer.WriteElementString("Displayed_Terms", displayedTerms); #endregion //Determine the number of gl distributions needed (why? Who freakin knows, it should be self evident to Peachtree but it's required) int nNumberOfDistributions = i.Rows.Count; #region Taxes DataRow drTaxCode = null; string[] taxauthorities = null; //Need to do taxes? if (IsTax) { //Determine the tax authorities drTaxCode = PTTaxCodes.Rows.Find(PTClients.Rows.Find(PTI.Maps[w.ClientID].ForeignID)["Sales_Tax_Code"]); if (drTaxCode == null) throw new System.ApplicationException("Can not invoice because customer " + PTI.Maps[w.ClientID].Name + "\r\n does not have a default sales tax code set in Peachtree"); //Determine the number of distributions this will add (count the tax authorities) //these are stored as a comma delimited list of Peachtree ID's in the drtaxauthority table //so just need to split them out taxauthorities = drTaxCode["Authorities"].ToString().Split(','); //Ensure no tax authorities are using formulaic tax codes //We could support that in future if enough people would use it but for now //no dice foreach (string s in taxauthorities) { DataRow drTaxAuthority = PTTaxAuthorities.Rows.Find(s); if (drTaxAuthority == null) throw new System.ApplicationException("Can not invoice: TaxAuthority " + s + " used by tax code " + drTaxCode["FullName"].ToString() + " could not be found"); if (drTaxAuthority["UsesFormula"].ToString() == "TRUE") throw new System.ApplicationException( "Can not invoice because customer " + PTI.Maps[w.ClientID].Name + "\r\nuses a default tax code that uses tax authority " + s + " which uses \r\n" + "a formula instead of a regular percentage for calculating taxes.\r\n" + "Formula based tax codes are not currently supported by AyaNova PTI"); } //Ok, at this point we are safely using tax codes and authorities correctly //Each tax authority requires one distribution line for invoice nNumberOfDistributions += taxauthorities.GetLength(0); } #endregion taxes if (IsTax) { Writer.WriteStartElement("Sales_Tax_Code"); Writer.WriteAttributeString("xsi:type", "paw:id"); Writer.WriteString(drTaxCode["ID"].ToString()); Writer.WriteEndElement(); } //Writer.WriteElementString("Accounts_Receivable_Amount", totalamount.ToString()); //Writer.WriteElementString("Accounts_Receivable_Amount", "190.80"); Writer.WriteElementString("CreditMemoType", "FALSE"); //Number of distributions (invoice detail lines) Writer.WriteElementString("Number_of_Distributions", nNumberOfDistributions.ToString()); Writer.WriteStartElement("SalesLines"); #region Tax if (IsTax) { foreach (string s in taxauthorities) { DataRow drTaxAuthority = PTTaxAuthorities.Rows.Find(s); Writer.WriteStartElement("SalesLine"); Writer.WriteElementString("SalesOrderDistributionNumber", "0"); Writer.WriteElementString("InvoiceCMDistribution", "0"); Writer.WriteElementString("Quantity", "0.00000"); //Writer.WriteStartElement("Item_ID"); //Writer.WriteAttributeString("xsi:type", "paw:ID"); //Writer.WriteString(dr["ID"].ToString()); //Writer.WriteEndElement(); //Writer.WriteElementString("Description", dr["Description"].ToString()); Writer.WriteStartElement("GL_Account"); Writer.WriteAttributeString("xsi:type", "paw:ID"); Writer.WriteString(drTaxAuthority["AccountID"].ToString()); Writer.WriteEndElement(); Writer.WriteElementString("Unit_Price", "0.00000"); Writer.WriteElementString("Tax_Type", "0"); decimal dTaxPercentage = decimal.Parse(drTaxAuthority["Rate"].ToString()) / 100; Writer.WriteElementString("Amount", (taxableamount * dTaxPercentage).ToString()); Writer.WriteElementString("Sales_Tax_Authority", drTaxAuthority["ID"].ToString()); Writer.WriteEndElement(); } } #endregion #region Billable items int nCurrentLineDistributionNumber = 0; foreach (DataRow dr in i.Rows) { nCurrentLineDistributionNumber++; Writer.WriteStartElement("SalesLine"); Writer.WriteElementString("SalesOrderDistributionNumber", "0"); Writer.WriteElementString("InvoiceCMDistribution", nCurrentLineDistributionNumber.ToString()); Writer.WriteElementString("Quantity", dr["Quantity"].ToString()); Writer.WriteStartElement("Item_ID"); Writer.WriteAttributeString("xsi:type", "paw:ID"); Writer.WriteString(dr["ID"].ToString()); Writer.WriteEndElement(); Writer.WriteElementString("Description", dr["Description"].ToString()); if (dr["ID"].ToString() != "")//Not a description only line { Writer.WriteStartElement("GL_Account"); Writer.WriteAttributeString("xsi:type", "paw:ID"); Writer.WriteString(PTItems.Rows.Find(dr["ID"].ToString())["SalesAccountId"].ToString()); Writer.WriteEndElement(); } else {//A description only line, still requires gl account, but will use default instead Writer.WriteStartElement("GL_Account"); Writer.WriteAttributeString("xsi:type", "paw:ID"); Writer.WriteString(DefaultSalesAccount); Writer.WriteEndElement(); } Writer.WriteElementString("Unit_Price", dr["Price"].ToString()); //case 559 //for some reason this line was already here but commented out and set to 2 always?!$% if (dr["ID"].ToString() != "")//Not a description only line Writer.WriteElementString("Tax_Type", PTItems.Rows.Find(dr["ID"].ToString())["SalesTaxType"].ToString()); else Writer.WriteElementString("Tax_Type", FirstNonTaxableTaxCode); Writer.WriteElementString("Amount", dr["Amount"].ToString()); //Case 533 if (!string.IsNullOrEmpty(dr["Serial_Number"].ToString())) Writer.WriteElementString("Serial_Number", dr["Serial_Number"].ToString()); Writer.WriteEndElement(); } #endregion //close Sales Lines Writer.WriteEndElement(); //Close pawInvoice Writer.WriteEndElement(); //close pawinvoices Writer.WriteEndElement(); Writer.Close(); #endregion generate import file #region Import PeachtreeAccounting.Import importer = (PeachtreeAccounting.Import)ptApp.app.CreateImporter(PeachwIEObj.peachwIEObjSalesJournal); importer.ClearImportFieldList(); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_CustomerId); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_InvoiceNumber); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_Date); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_ARAccountId); //Case 528 importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_CustomerPurchaseOrder); if (IsTax) importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_SalesTaxCode); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_IsCreditMemo); if (sbNotes.Length > 0) { importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_InvoiceNote); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_NotePrintsAfterLineItems); } if (PDat.SetMemoField) importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_InvoiceNote2); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_NumberOfDistributions); //Invoice line items (distributions) importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_SalesOrderDistNum); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_InvoiceDistNum); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_Quantity); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_ItemId); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_Description); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_GLAccountId); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_UnitPrice); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_TaxType); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_Amount); //Case 533 importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_enSerialNumber); if (IsTax) importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_SalesTaxAuthority); //Case 539 importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_DateDue); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_DiscountAmount); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_DiscountDate); importer.AddToImportFieldList((short)PeachwIEObjSalesJournalField.peachwIEObjSalesJournalField_DisplayedTerms); importer.SetFilename(TempFilePath + "PTimpinvoices.xml"); importer.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); #if(PEACHW14) Array arout; importer.ImportAndReturnGUIDs(out arout); if (arout.GetLength(0) < 1) throw new System.ApplicationException("Peachtree did not import invoice " + sInvoiceNumber + " - no error given"); #else importer.Import(); #endif #endregion invoiceGenerated = true; } catch (Exception ex) { //crack the exception in case it's a generic dataportal one //and it is if it's got an inner exception of any kind if (ex.InnerException != null) ex = ex.InnerException; invoiceGenerated = false; //check for errors that still result in an invoice being generated if (ex.Message.Contains("This transaction will cause the balance")) invoiceGenerated = true; if (invoiceGenerated) { alErrors.Add("Invoice: Invoicing completed with warning:\r\n" + ex.Message.Replace("Do you want to continue?", "")); } else { alErrors.Add("Invoice: Invoicing failed due to the following error:\r\n" + ex.Message); ErrorFree = false; } //{"WARNING! The reference number 'WO46' has already been used."} - Did not result in an invoice //{"This transaction will cause the balance of customer [MOLLYS] to go over the credit limit by $7,008.96. Do you want to continue?"} - Did result in an invoice } finally { CurrentNode = null; #if(!DEBUG) if (invoiceGenerated) { //Loop through alworkorders foreach (object o in alWorkorders) { w = Workorder.GetItem((Guid)o); if (PDat.PostWOStatus != Guid.Empty) { w.WorkorderService.WorkorderStatusID = PDat.PostWOStatus; } w.WorkorderService.InvoiceNumber = sInvoiceNumber; //Case 7 if (PDat.AutoClose) w.Closed = true; w.Save(); } } #else if (!invoiceGenerated) MessageBox.Show("DEBUG: Workorder could NOT be closed due to error"); #endif } } /// /// Add text to the invoice /// chopping into chunks less than the max 4095 characters limit /// as necessary /// private static void InvoiceAddNote(StringBuilder i, string Text) { if (Text == null || Text == "") return; i.Append(Text); i.Append(" "); } private static void InvoiceAddTextLine(DataTable dtLines, string TextLineToAdd) { DataRow dr = dtLines.NewRow(); dr["ID"] = ""; dr["Description"] = TextLineToAdd; dr["Quantity"] = 0; dr["Price"] = 0; dr["Amount"] = 0; dtLines.Rows.Add(dr); } /// /// Case 533 overload /// /// /// /// /// /// private static void InvoiceAddCharge(DataTable dtLines, string PTListID, decimal Quantity, decimal rate) { InvoiceAddCharge(dtLines, PTListID, Quantity, rate, ""); } /// /// Add charge line to the invoice /// /// private static void InvoiceAddCharge(DataTable dtLines, string PTListID, decimal Quantity, decimal rate, string sn)//case 533 { DataRow dr = dtLines.NewRow(); dr["ID"] = PTListID; dr["Description"] = PTItems.Rows.Find(PTListID)["SalesDesc"].ToString(); dr["Quantity"] = Quantity.ToString(); dr["Price"] = (rate * -1).ToString(); dr["Amount"] = ((rate * Quantity) * -1).ToString(); //case 533 dr["Serial_Number"] = sn; dtLines.Rows.Add(dr); } #endregion #region Change PT Item price public static void ChangePTItemPrice(string PTListID, decimal NewPrice) { try { #region Generate import file XmlTextWriter Writer = new XmlTextWriter(TempFilePath + "PTchgItem.xml", System.Text.Encoding.UTF8); Writer.WriteStartElement("PAW_Items"); Writer.WriteAttributeString("xmlns:paw", "urn:schemas-peachtree-com/paw8.02-datatypes"); Writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2000/10/XMLSchema-instance"); Writer.WriteAttributeString("xmlns:xsd", "http://www.w3.org/2000/10/XMLSchema-datatypes"); Writer.WriteStartElement("PAW_Item"); Writer.WriteAttributeString("xsi:type", "paw:item"); Writer.WriteStartElement("ID"); Writer.WriteAttributeString("xsi:type", "paw:ID"); Writer.WriteString(PTListID); Writer.WriteEndElement(); Writer.WriteStartElement("Sales_Prices"); Writer.WriteStartElement("Sales_Price_Info"); Writer.WriteAttributeString("Key", "1"); Writer.WriteElementString("Sales_Price", NewPrice.ToString());//NEWPRICE Writer.WriteEndElement(); Writer.WriteEndElement(); Writer.Close(); #endregion generate import file #region Import PeachtreeAccounting.Import importer = (PeachtreeAccounting.Import)ptApp.app.CreateImporter(PeachwIEObj.peachwIEObjInventoryItemsList); importer.ClearImportFieldList(); importer.AddToImportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_ItemId); importer.AddToImportFieldList((short)PeachwIEObjInventoryItemsListField.peachwIEObjInventoryItemsListField_UnitPrice1); importer.SetFilename(TempFilePath + "PTchgItem.xml"); importer.SetFileType(PeachwIEFileType.peachwIEFileTypeXML); //Array arout; importer.Import(); // importer.ImportAndReturnGUIDs(out arout); //if (arout.GetLength(0) < 1) // throw new System.ApplicationException("Peachtree did not accept price change on item " + PTListID + " - no error given"); #endregion //Update cached list with new price PTItems.Rows.Find(PTListID)["Price"] = NewPrice; } catch (Exception ex) { IntegrationLog.Log(PTID, "ChangePTItemPrice: Failed with exception:" + ex.Message); ErrorFree = false; throw; } finally { CurrentNode = null; } } #endregion #region various utils and helper methods public static string CrackException(Exception ex) { while (ex.InnerException != null) { ex = ex.InnerException; } return ex.Message + "\r\n-------TRACE------\r\n" + ex.StackTrace; } /// /// Case 262 addition /// Convert a string to a double handling french canadian locale /// , since qb is not locale aware in it's api /// conversion can fail because .net expects a comma that isn't there /// /// /// /// public static double SafeToDouble(string s) { if (string.IsNullOrEmpty(s)) return 0; try { return Convert.ToDouble(s, System.Globalization.CultureInfo.InvariantCulture); } catch (System.FormatException) { throw new System.ApplicationException("SafeToDouble: Can't parse PT string double value number: \"" + s + "\""); } } #endregion various //-------------------------------------------------------------------------------- } }