Files
2018-06-29 19:47:36 +00:00

4571 lines
182 KiB
C#

#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>
/// Summary description for Util.
/// </summary>
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;
/// <summary>
/// The official and unchanging integration ID for
/// PTI ({0B937F64-7BC4-4b7d-A1CF-46BD14F49918})
/// </summary>
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);
}
/// <summary>
/// Invert a color so it's readable against the passed
/// in color
/// </summary>
/// <param name="col"></param>
/// <returns></returns>
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
/// <summary>
/// Pre flight check, prompt user as required to repair
/// or add missing settings / failed ones
///
/// This method is called AFTER login.
/// </summary>
/// <returns></returns>
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
/// <summary>
/// Check if integration object is present in database, if not then
/// add it.
/// </summary>
/// <returns></returns>
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
/// <summary>
/// Open Peachtree connection
/// gather info required for future
/// transactions
/// </summary>
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
/// <summary>
/// Validate the users preferences
/// if any are missing or invalid prompt for them
/// </summary>
/// <returns></returns>
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;
/// <summary>
/// AyaNova ClientPickList
/// </summary>
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;
/// <summary>
/// AyaNova vendor list
/// </summary>
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;
/// <summary>
/// AyaNova rate list
/// </summary>
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;
/// <summary>
/// AyaNova part list
/// </summary>
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
/// <summary>
/// Populate or repopulate the list of
/// </summary>
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;
/// <summary>
/// qb items
/// </summary>
public static DataTable PTItems
{
get
{
return _dtPTItems;
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="PTItemID"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Populate the cached qb data
/// billable
/// </summary>
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;
/// <summary>
/// PT Transaction Clients
/// </summary>
public static DataTable PTClients
{
get
{
return _dtPTClients;
}
}
#region Address structure
/// <summary>
/// Address properties
/// </summary>
public struct Address
{
public string DeliveryAddress;
public string City;
public string StateProv;
public string Country;
public string Postal;
}
#endregion
/// <summary>
/// Populate the cached qb data
/// of customers / clients
/// </summary>
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;
/// <summary>
/// PT Vendors
/// </summary>
public static DataTable PTVendors
{
get
{
return _dtPTVendors;
}
}
/// <summary>
/// Populate the cached qb data
/// of Vendors
/// </summary>
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
/// </summary>
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;
/// <summary>
/// PT Taxes
/// </summary>
public static DataTable PTTaxAuthorities
{
get
{
return _dtPTTaxAuthorities;
}
}
/// <summary>
/// Populate the cached qb data
/// of Tax authorities and taxes
/// </summary>
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;
/// <summary>
/// PT Taxes
/// </summary>
public static DataTable PTTaxCodes
{
get
{
return _dtPTTaxCodes;
}
}
/// <summary>
/// Populate the cached qb data
/// of Tax Codes
/// </summary>
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;
/// <summary>
/// PT Taxes
/// </summary>
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;
}
}
/// <summary>
/// Determines if a particular item has a taxable tax type set
/// </summary>
/// <param name="Item"></param>
/// <returns></returns>
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"
}
/// <summary>
/// Populate the cached qb data
/// of Item Tax type codes (is taxable)
/// </summary>
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
/// <summary>
/// Text export from peachtree
/// </summary>
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
/// <summary>
/// Import the indicated customer
/// to an AyaNova client record
/// </summary>
/// <param name="PeachtreeID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Import the indicated Vendor
/// to an AyaNova vendor record
/// </summary>
/// <param name="PeachtreeID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Import the indicated PT Item
/// to an AyaNova Rate record
/// </summary>
/// <param name="PeachtreeID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Import the indicated PT Item
/// to an AyaNova Part record
/// </summary>
/// <param name="PeachtreeID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Given a fullname generate a unique ID for Peachtree
/// </summary>
/// <param name="FullName">Full name of object</param>
/// <param name="dtDest">Table to scan for uniqueness</param>
/// <returns></returns>
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
/// <summary>
/// Import the indicated client
/// to Peachtree customer record
/// </summary>
/// <param name="ClientID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
/// <summary>
/// Import the indicated Vendor
/// to Peachtree Vendor record
/// </summary>
/// <param name="VendorID"></param>
/// <param name="alErrors">An arraylist to hold strings indicating errors on fail</param>
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
}
/// <summary>
/// Mismatch properties
/// A structure for storing mismatches identified
///so user can resolve them.
/// </summary>
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;
}
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="WorkorderID">Id of workorder being scanned</param>
/// <param name="MisMatches">An arraylist of mismatch objects</param>
/// <param name="PriceOverrides">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</param>
/// <returns>True if all links ok, false if there are any mismatches at all</returns>
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
/// <summary>
/// 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
/// </summary>
/// <param name="alWorkorders"></param>
/// <returns></returns>
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
}
}
/// <summary>
/// Add text to the invoice
/// chopping into chunks less than the max 4095 characters limit
/// as necessary
/// </summary>
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);
}
/// <summary>
/// Case 533 overload
/// </summary>
/// <param name="dtLines"></param>
/// <param name="PTListID"></param>
/// <param name="Quantity"></param>
/// <param name="rate"></param>
/// <param name="sn"></param>
private static void InvoiceAddCharge(DataTable dtLines, string PTListID, decimal Quantity, decimal rate)
{
InvoiceAddCharge(dtLines, PTListID, Quantity, rate, "");
}
/// <summary>
/// Add charge line to the invoice
///
/// </summary>
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;
}
/// <summary>
/// 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
///
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
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
//--------------------------------------------------------------------------------
}
}