diff --git a/source/OFXSharp.Console/App.config b/source/OFXSharp.Console/App.config
new file mode 100644
index 0000000..8e15646
--- /dev/null
+++ b/source/OFXSharp.Console/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/OFXSharp.Console/OFXSharp.Console.csproj b/source/OFXSharp.Console/OFXSharp.Console.csproj
new file mode 100644
index 0000000..942f8ba
--- /dev/null
+++ b/source/OFXSharp.Console/OFXSharp.Console.csproj
@@ -0,0 +1,64 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {BD64B333-EFE8-400F-B61A-F3EC0E98889B}
+ Exe
+ Properties
+ OFXSharp.Console
+ OFXSharp.Console
+ v4.5
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {7bba813d-ee93-491a-b0c7-fbd588e393cb}
+ OFXSharp
+
+
+
+
+
\ No newline at end of file
diff --git a/source/OFXSharp.Console/Program.cs b/source/OFXSharp.Console/Program.cs
new file mode 100644
index 0000000..b6115db
--- /dev/null
+++ b/source/OFXSharp.Console/Program.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OFXSharp.Console
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ var parser = new OFXDocumentParser();
+ var ofxDocument = parser.Import(new FileStream(@"F:\Sandboxes\TM\OFXSharp\source\OFXSharp.Tests\itau.ofx", FileMode.Open));
+ }
+ }
+}
diff --git a/source/OFXSharp.Console/Properties/AssemblyInfo.cs b/source/OFXSharp.Console/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..cc6abe6
--- /dev/null
+++ b/source/OFXSharp.Console/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OFXSharp.Console")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("OFXSharp.Console")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("401deb64-26de-48e7-8f07-a64dd948dffc")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/source/OFXSharp.sln b/source/OFXSharp.sln
index c73e5ab..1f2a282 100644
--- a/source/OFXSharp.sln
+++ b/source/OFXSharp.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
-VisualStudioVersion = 12.0.30110.0
+VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFXSharp", "OFXSharp\OFXSharp.csproj", "{7BBA813D-EE93-491A-B0C7-FBD588E393CB}"
EndProject
@@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{4456DF
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFXSharp.Tests", "OFXSharp.Tests\OFXSharp.Tests.csproj", "{4753DE21-42DB-405F-BB2A-A3F189A62482}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFXSharp.Console", "OFXSharp.Console\OFXSharp.Console.csproj", "{BD64B333-EFE8-400F-B61A-F3EC0E98889B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
{4753DE21-42DB-405F-BB2A-A3F189A62482}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4753DE21-42DB-405F-BB2A-A3F189A62482}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4753DE21-42DB-405F-BB2A-A3F189A62482}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BD64B333-EFE8-400F-B61A-F3EC0E98889B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD64B333-EFE8-400F-B61A-F3EC0E98889B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BD64B333-EFE8-400F-B61A-F3EC0E98889B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BD64B333-EFE8-400F-B61A-F3EC0E98889B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/source/OFXSharp/Account.cs b/source/OFXSharp/Account.cs
index b80acf4..fe91b53 100644
--- a/source/OFXSharp/Account.cs
+++ b/source/OFXSharp/Account.cs
@@ -1,94 +1,96 @@
-using System;
-using System.Xml;
-
-namespace OFXSharp
-{
- public class Account
- {
- public string AccountID { get; set; }
- public string AccountKey { get; set; }
- public AccountType AccountType { get; set; }
-
- #region Bank Only
-
- private BankAccountType _BankAccountType = BankAccountType.NA;
-
- public string BankID { get; set; }
-
- public string BranchID { get; set; }
-
-
- public BankAccountType BankAccountType
- {
- get
- {
- if (AccountType == AccountType.BANK)
- return _BankAccountType;
-
- return BankAccountType.NA;
- }
- set
- {
- _BankAccountType = AccountType == AccountType.BANK ? value : BankAccountType.NA;
- }
- }
-
- #endregion
-
- public Account(XmlNode node, AccountType type)
- {
- AccountType = type;
-
- AccountID = node.GetValue("//ACCTID");
- AccountKey = node.GetValue("//ACCTKEY");
-
- switch (AccountType)
- {
- case AccountType.BANK:
- InitializeBank(node);
- break;
- case AccountType.AP:
- InitializeAP(node);
- break;
- case AccountType.AR:
- InitializeAR(node);
- break;
- default:
- break;
- }
- }
-
- ///
- /// Initializes information specific to bank
- ///
- private void InitializeBank(XmlNode node)
- {
- BankID = node.GetValue("//BANKID");
- BranchID = node.GetValue("//BRANCHID");
-
+using System;
+using System.Xml;
+
+namespace OFXSharp
+{
+ public class Account
+ {
+ public string AccountID { get; set; }
+ public string AccountKey { get; set; }
+ public AccountType AccountType { get; set; }
+
+ #region Bank Only
+
+ private BankAccountType _BankAccountType = BankAccountType.NA;
+
+ public string BankID { get; set; }
+
+ public string BranchID { get; set; }
+
+
+ public BankAccountType BankAccountType
+ {
+ get
+ {
+ if (AccountType == AccountType.Bank)
+ return _BankAccountType;
+
+ return BankAccountType.NA;
+ }
+ set
+ {
+ _BankAccountType = AccountType == AccountType.Bank ? value : BankAccountType.NA;
+ }
+ }
+
+ #endregion
+
+ public Account(XmlNode node, AccountType type)
+ {
+ AccountType = type;
+
+ AccountID = node.GetValue(".//ACCTID");
+ AccountKey = node.GetValue(".//ACCTKEY");
+
+ switch (AccountType)
+ {
+ case AccountType.Bank:
+ InitializeBank(node);
+ break;
+ case AccountType.AccountsPayable:
+ InitializeAP(node);
+ break;
+ case AccountType.AccountsReceivable:
+ InitializeAR(node);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public Account() { }
+
+ ///
+ /// Initializes information specific to bank
+ ///
+ private void InitializeBank(XmlNode node)
+ {
+ BankID = node.GetValue(".//BANKID");
+ BranchID = node.GetValue(".//BRANCHID");
+
//Get Bank Account Type from XML
- string bankAccountType = node.GetValue("//ACCTTYPE");
-
- //Check that it has been set
- if (String.IsNullOrEmpty(bankAccountType))
- throw new OFXParseException("Bank Account type unknown");
-
- //Set bank account enum
- _BankAccountType = bankAccountType.GetBankAccountType();
- }
-
- #region Account types not supported
-
- private void InitializeAP(XmlNode node)
- {
- throw new OFXParseException("AP Account type not supported");
- }
-
- private void InitializeAR(XmlNode node)
- {
- throw new OFXParseException("AR Account type not supported");
- }
-
- #endregion
- }
+ string bankAccountType = node.GetValue(".//ACCTTYPE");
+
+ //Check that it has been set
+ if (String.IsNullOrEmpty(bankAccountType))
+ throw new OFXParseException("Bank Account type unknown");
+
+ //Set bank account enum
+ _BankAccountType = bankAccountType.GetBankAccountType();
+ }
+
+ #region Account types not supported
+
+ private void InitializeAP(XmlNode node)
+ {
+ throw new OFXParseException("AP Account type not supported");
+ }
+
+ private void InitializeAR(XmlNode node)
+ {
+ throw new OFXParseException("AR Account type not supported");
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/source/OFXSharp/AccountType.cs b/source/OFXSharp/AccountType.cs
index bb79556..1b48fb6 100644
--- a/source/OFXSharp/AccountType.cs
+++ b/source/OFXSharp/AccountType.cs
@@ -5,13 +5,13 @@ namespace OFXSharp
public enum AccountType
{
[Description("Bank Account")]
- BANK,
+ Bank,
[Description("Credit Card")]
- CC,
+ CreditCard,
[Description("Accounts Payable")]
- AP,
+ AccountsPayable,
[Description("Accounts Recievable")]
- AR,
+ AccountsReceivable,
NA,
}
}
\ No newline at end of file
diff --git a/source/OFXSharp/Balance.cs b/source/OFXSharp/Balance.cs
index 19d15ec..7ea2117 100644
--- a/source/OFXSharp/Balance.cs
+++ b/source/OFXSharp/Balance.cs
@@ -16,7 +16,7 @@ public class Balance
public Balance(XmlNode ledgerNode, XmlNode avaliableNode)
{
- var tempLedgerBalance = ledgerNode.GetValue("//BALAMT");
+ var tempLedgerBalance = ledgerNode.GetValue(".//BALAMT");
if (!String.IsNullOrEmpty(tempLedgerBalance))
{
@@ -47,7 +47,7 @@ public Balance(XmlNode ledgerNode, XmlNode avaliableNode)
}
else
{
- var tempAvaliableBalance = avaliableNode.GetValue("//BALAMT");
+ var tempAvaliableBalance = avaliableNode.GetValue(".//BALAMT");
if (!String.IsNullOrEmpty(tempAvaliableBalance))
{
@@ -58,10 +58,12 @@ public Balance(XmlNode ledgerNode, XmlNode avaliableNode)
{
throw new OFXParseException("Avaliable balance has not been set");
}
- AvaliableBalanceDate = avaliableNode.GetValue("//DTASOF").ToDate();
+ AvaliableBalanceDate = avaliableNode.GetValue(".//DTASOF").ToDate();
}
- LedgerBalanceDate = ledgerNode.GetValue("//DTASOF").ToDate();
+ LedgerBalanceDate = ledgerNode.GetValue(".//DTASOF").ToDate();
}
+
+ public Balance() { }
}
}
\ No newline at end of file
diff --git a/source/OFXSharp/ModelDiagram.cd b/source/OFXSharp/ModelDiagram.cd
index 39de33a..db37bb6 100644
--- a/source/OFXSharp/ModelDiagram.cd
+++ b/source/OFXSharp/ModelDiagram.cd
@@ -1,53 +1,64 @@
-
+
- AACBAAAAAAAAAAAEAAgAAAABCAAAAAAAAAAAAAAAAEI=
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAI=
OFXDocument.cs
-
-
-
-
+
-
+
AAAAAAAAAAAAAAAAAAIIAAAAAAAAAAEAAAAAAAAAAAQ=
Balance.cs
-
+
BACAAIAAIQAAAAAAAgAAIAAAAAAAAAAAACAAQAAAAAA=
Account.cs
-
+
- AAAAAAAACAAAAAAAAAAAAABAAAAABAAAIAQAAAAAAAA=
+ AAAAAAAACAAAAAAAAAAAAABAAEAABAAAIAQAAAAQAAA=
SignOn.cs
-
+
AAAAAgAAAACAAQAAAAAEAgSgCBABFAUDgAAAAAAAIAA=
Transaction.cs
+
+
+
+ AACBAAAAAAAAAAAEAAgAAAABCAAAAAAAAAAAAAAAAEA=
+ Statement.cs
+
+
+
+
+
+
+
+
+
-
+
- AAAAAAIAAAAAAAAQAAAAAAEgAAAAAgAAAAAAAAAAAAA=
+ AQAAAAIAAAAAQAAAAQAAACAAAAAAAAAAAAAAAAAAAAA=
AccountType.cs
diff --git a/source/OFXSharp/OFXDocument.cs b/source/OFXSharp/OFXDocument.cs
index a4efada..f04e49b 100644
--- a/source/OFXSharp/OFXDocument.cs
+++ b/source/OFXSharp/OFXDocument.cs
@@ -5,20 +5,7 @@ namespace OFXSharp
{
public class OFXDocument
{
- public DateTime StatementStart { get; set; }
-
- public DateTime StatementEnd { get; set; }
-
- public AccountType AccType { get; set; }
-
- public string Currency { get; set; }
-
public SignOn SignOn { get; set; }
-
- public Account Account { get; set; }
-
- public Balance Balance { get; set; }
-
- public List Transactions { get; set; }
+ public IList Statements { get; set; }
}
}
\ No newline at end of file
diff --git a/source/OFXSharp/OFXDocumentParser.cs b/source/OFXSharp/OFXDocumentParser.cs
index 7529077..7ad9429 100644
--- a/source/OFXSharp/OFXDocumentParser.cs
+++ b/source/OFXSharp/OFXDocumentParser.cs
@@ -6,281 +6,325 @@
namespace OFXSharp
{
- public class OFXDocumentParser
- {
- public OFXDocument Import(FileStream stream)
- {
- using (var reader = new StreamReader(stream))
- {
- return Import(reader.ReadToEnd());
- }
- }
-
- public OFXDocument Import(string ofx)
- {
- return ParseOfxDocument(ofx);
- }
-
- private OFXDocument ParseOfxDocument(string ofxString)
- {
- //If OFX file in SGML format, convert to XML
- if (!IsXmlVersion(ofxString))
- {
- ofxString = SGMLToXML(ofxString);
- }
-
- return Parse(ofxString);
- }
-
- private OFXDocument Parse(string ofxString)
- {
- var ofx = new OFXDocument {AccType = GetAccountType(ofxString)};
-
- //Load into xml document
- var doc = new XmlDocument();
- doc.Load(new StringReader(ofxString));
-
- var currencyNode = doc.SelectSingleNode(GetXPath(ofx.AccType, OFXSection.CURRENCY));
-
- if (currencyNode != null)
- {
- ofx.Currency = currencyNode.FirstChild.Value;
- }
- else
- {
- throw new OFXParseException("Currency not found");
- }
-
- //Get sign on node from OFX file
- var signOnNode = doc.SelectSingleNode(Resources.SignOn);
-
- //If exists, populate signon obj, else throw parse error
- if (signOnNode != null)
- {
- ofx.SignOn = new SignOn(signOnNode);
- }
- else
- {
- throw new OFXParseException("Sign On information not found");
- }
-
- //Get Account information for ofx doc
- var accountNode = doc.SelectSingleNode(GetXPath(ofx.AccType, OFXSection.ACCOUNTINFO));
-
- //If account info present, populate account object
- if (accountNode != null)
- {
- ofx.Account = new Account(accountNode, ofx.AccType);
- }
- else
- {
- throw new OFXParseException("Account information not found");
- }
-
- //Get list of transactions
- ImportTransations(ofx, doc);
-
- //Get balance info from ofx doc
- var ledgerNode = doc.SelectSingleNode(GetXPath(ofx.AccType, OFXSection.BALANCE) + "/LEDGERBAL");
- var avaliableNode = doc.SelectSingleNode(GetXPath(ofx.AccType, OFXSection.BALANCE) + "/AVAILBAL");
-
- //If balance info present, populate balance object
- // ***** OFX files from my bank don't have the 'avaliableNode' node, so i manage a 'null' situation
- if (ledgerNode != null) // && avaliableNode != null
- {
- ofx.Balance = new Balance(ledgerNode, avaliableNode);
- }
- else
- {
- throw new OFXParseException("Balance information not found");
- }
-
- return ofx;
- }
-
-
- ///
- /// Returns the correct xpath to specified section for given account type
- ///
- /// Account type
- /// Section of OFX document, e.g. Transaction Section
- /// Thrown in account type not supported
- private string GetXPath(AccountType type, OFXSection section)
- {
- string xpath, accountInfo;
-
- switch (type)
- {
- case AccountType.BANK:
- xpath = Resources.BankAccount;
- accountInfo = "/BANKACCTFROM";
- break;
- case AccountType.CC:
- xpath = Resources.CCAccount;
- accountInfo = "/CCACCTFROM";
- break;
- default:
- throw new OFXException("Account Type not supported. Account type " + type);
- }
-
- switch (section)
- {
- case OFXSection.ACCOUNTINFO:
- return xpath + accountInfo;
- case OFXSection.BALANCE:
- return xpath;
- case OFXSection.TRANSACTIONS:
- return xpath + "/BANKTRANLIST";
- case OFXSection.SIGNON:
- return Resources.SignOn;
- case OFXSection.CURRENCY:
- return xpath + "/CURDEF";
- default:
- throw new OFXException("Unknown section found when retrieving XPath. Section " + section);
- }
- }
-
- ///
- /// Returns list of all transactions in OFX document
- ///
- /// OFX document
- /// List of transactions found in OFX document
- private void ImportTransations(OFXDocument ofxDocument, XmlDocument doc)
- {
- var xpath = GetXPath(ofxDocument.AccType, OFXSection.TRANSACTIONS);
-
- ofxDocument.StatementStart = doc.GetValue(xpath + "//DTSTART").ToDate();
- ofxDocument.StatementEnd = doc.GetValue(xpath + "//DTEND").ToDate();
-
- var transactionNodes = doc.SelectNodes(xpath + "//STMTTRN");
-
- ofxDocument.Transactions = new List();
-
- foreach (XmlNode node in transactionNodes)
- ofxDocument.Transactions.Add(new Transaction(node, ofxDocument.Currency));
- }
-
-
- ///
- /// Checks account type of supplied file
- /// OFX file want to check
- /// Account type for account supplied in ofx file
- private AccountType GetAccountType(string file)
- {
- if (file.IndexOf("") != -1)
- return AccountType.CC;
-
- if (file.IndexOf("") != -1)
- return AccountType.BANK;
-
- throw new OFXException("Unsupported Account Type");
- }
-
- ///
- /// Check if OFX file is in SGML or XML format
- ///
- ///
- ///
- private bool IsXmlVersion(string file)
- {
- return (file.IndexOf("OFXHEADER:100") == -1);
- }
-
- ///
- /// Converts SGML to XML
- ///
- /// OFX File (SGML Format)
- /// OFX File in XML format
- private string SGMLToXML(string file)
- {
- var reader = new SgmlReader();
-
- //Inititialize SGML reader
- reader.InputStream = new StringReader(ParseHeader(file));
- reader.DocType = "OFX";
-
- var sw = new StringWriter();
- var xml = new XmlTextWriter(sw);
-
- //write output of sgml reader to xml text writer
- while (!reader.EOF)
- xml.WriteNode(reader, true);
-
- //close xml text writer
- xml.Flush();
- xml.Close();
-
- var temp = sw.ToString().TrimStart().Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
-
- return String.Join("", temp);
- }
-
- ///
- /// Checks that the file is supported by checking the header. Removes the header.
- ///
- /// OFX file
- /// File, without the header
- private string ParseHeader(string file)
- {
- //Select header of file and split into array
- //End of header worked out by finding first instance of '<'
- //Array split based of new line & carrige return
- var header = file.Substring(0, file.IndexOf('<'))
- .Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
-
- //Check that no errors in header
- CheckHeader(header);
-
- //Remove header
- return file.Substring(file.IndexOf('<') - 1);
- }
-
- ///
- /// Checks that all the elements in the header are supported
- ///
- /// Header of OFX file in array
- private void CheckHeader(string[] header)
- {
- if (header[0] != "OFXHEADER:100")
- throw new OFXParseException("Incorrect header format");
-
- if (header[1] != "DATA:OFXSGML")
- throw new OFXParseException("Data type unsupported: " + header[1] + ". OFXSGML required");
-
- if (header[2] != "VERSION:102")
- throw new OFXParseException("OFX version unsupported. " + header[2]);
-
- if (header[3] != "SECURITY:NONE")
- throw new OFXParseException("OFX security unsupported");
-
- if (header[4] != "ENCODING:USASCII")
- throw new OFXParseException("ASCII Format unsupported:" + header[4]);
-
- if (header[5] != "CHARSET:1252")
- throw new OFXParseException("Charecter set unsupported:" + header[5]);
-
- if (header[6] != "COMPRESSION:NONE")
- throw new OFXParseException("Compression unsupported");
-
- if (header[7] != "OLDFILEUID:NONE")
- throw new OFXParseException("OLDFILEUID incorrect");
- }
-
- #region Nested type: OFXSection
-
- ///
- /// Section of OFX Document
- ///
- private enum OFXSection
- {
- SIGNON,
- ACCOUNTINFO,
- TRANSACTIONS,
- BALANCE,
- CURRENCY
- }
-
- #endregion
- }
+ public class OFXDocumentParser
+ {
+ public OFXDocument Import(Stream stream)
+ {
+ using (var reader = new StreamReader(stream))
+ {
+ return Import(reader.ReadToEnd());
+ }
+ }
+
+ public OFXDocument Import(string ofx)
+ {
+ return ParseOfxDocument(ofx);
+ }
+
+ private OFXDocument ParseOfxDocument(string ofxString)
+ {
+ //If OFX file in SGML format, convert to XML
+ if (!IsXmlVersion(ofxString))
+ {
+ ofxString = SGMLToXML(ofxString);
+ }
+
+ return Parse(ofxString);
+ }
+
+ private OFXDocument Parse(string ofxString)
+ {
+ var accountTypes = GetAccountTypes(ofxString);
+
+ //Load into xml document
+ var doc = new XmlDocument();
+ doc.Load(new StringReader(ofxString));
+
+ var ofx = new OFXDocument { Statements = new List() };
+
+ //Get sign on node from OFX file
+ var signOnNode = doc.SelectSingleNode(Resources.SignOn);
+
+ //If exists, populate signon obj, else throw parse error
+ if (signOnNode != null)
+ {
+ ofx.SignOn = new SignOn(signOnNode);
+ }
+ else
+ {
+ throw new OFXParseException("Sign On information not found");
+ }
+
+ // OFX supports multiple statements and account types in a single file, so loop through them
+ foreach (var accountType in accountTypes)
+ {
+ // get the statement responses for this account type
+ var statementNodes = doc.SelectNodes(GetXPath(accountType, OFXSection.Statement));
+
+ if (statementNodes == null || statementNodes.Count == 0)
+ {
+ throw new OFXParseException("No statement responses found for account type " + accountType);
+ }
+
+ // now we can get into the nitty gritty
+ foreach (XmlNode statementNode in statementNodes)
+ {
+ var statement = new Statement { AccType = accountType };
+
+ // Get currency info
+ var currencyNode = statementNode.SelectSingleNode(GetXPath(accountType, OFXSection.Currency));
+ if (currencyNode != null)
+ {
+ statement.Currency = currencyNode.FirstChild.Value;
+ }
+ else
+ {
+ throw new OFXParseException("Currency not found");
+ }
+
+ // Get account information for ofx doc
+ var accountNode = statementNode.SelectSingleNode(GetXPath(accountType, OFXSection.AccountInfo));
+ if (accountNode != null)
+ {
+ statement.Account = new Account(accountNode, accountType);
+ }
+ else
+ {
+ throw new OFXParseException("Account information not found");
+ }
+
+ // Get list of transactions
+ ImportTransations(statement, statementNode);
+
+ // Get balance info from ofx doc
+ var ledgerNode = statementNode.SelectSingleNode(".//LEDGERBAL"); //GetXPath(accountType, OFXSection.Balance) +
+ var avaliableNode = statementNode.SelectSingleNode(".//AVAILBAL"); //GetXPath(accountType, OFXSection.Balance) +
+
+ // ***** OFX files from my bank don't have the 'avaliableNode' node, so i manage a 'null' situation
+ if (ledgerNode != null) // && avaliableNode != null
+ {
+ statement.Balance = new Balance(ledgerNode, avaliableNode);
+ }
+ else
+ {
+ throw new OFXParseException("Balance information not found");
+ }
+
+ ofx.Statements.Add(statement);
+ }
+
+ }
+
+ return ofx;
+ }
+
+
+ ///
+ /// Returns the correct xpath to specified section for given account type
+ ///
+ /// Account type
+ /// Section of OFX document, e.g. Transaction Section
+ /// Thrown in account type not supported
+ private string GetXPath(AccountType type, OFXSection section)
+ {
+ string xpath, accountInfo;
+
+ switch (type)
+ {
+ case AccountType.Bank:
+ xpath = Resources.BankAccount;
+ accountInfo = ".//BANKACCTFROM";
+ break;
+ case AccountType.CreditCard:
+ xpath = Resources.CCAccount;
+ accountInfo = ".//CCACCTFROM";
+ break;
+ default:
+ throw new OFXException("Account Type not supported. Account type " + type);
+ }
+
+ switch (section)
+ {
+ case OFXSection.Statement:
+ return xpath;
+ case OFXSection.AccountInfo:
+ return accountInfo; //xpath +
+ case OFXSection.Balance:
+ return xpath;
+ case OFXSection.Transactions:
+ return ".//BANKTRANLIST"; //xpath +
+ case OFXSection.SignOn:
+ return Resources.SignOn;
+ case OFXSection.Currency:
+ return ".//CURDEF"; //xpath +
+ default:
+ throw new OFXException("Unknown section found when retrieving XPath. Section " + section);
+ }
+ }
+
+ ///
+ /// Returns list of all transactions in OFX document
+ ///
+ /// OFX Statement
+ /// OFX document
+ /// List of transactions found in OFX document
+ private void ImportTransations(Statement ofxStatement, XmlNode node)
+ {
+ var xpath = GetXPath(ofxStatement.AccType, OFXSection.Transactions);
+
+ ofxStatement.StatementStart = node.GetValue(xpath + "//DTSTART").ToDate();
+ ofxStatement.StatementEnd = node.GetValue(xpath + "//DTEND").ToDate();
+
+ var transactionNodes = node.SelectNodes(xpath + "//STMTTRN");
+ ofxStatement.Transactions = new List();
+
+ if (transactionNodes == null || transactionNodes.Count == 0)
+ {
+ return;
+ }
+
+ foreach (XmlNode txNode in transactionNodes)
+ {
+ ofxStatement.Transactions.Add(new Transaction(txNode, ofxStatement.Currency));
+ }
+ }
+
+
+ ///
+ /// Checks account types of supplied file
+ ///
+ /// OFX file want to check
+ /// Account type for account supplied in ofx file
+ private IEnumerable GetAccountTypes(string file)
+ {
+ var accountTypes = new List();
+ var accountTypeFound = false;
+
+ if (file.IndexOf("") != -1)
+ {
+ accountTypes.Add(AccountType.CreditCard);
+ accountTypeFound = true;
+ }
+
+ if (file.IndexOf("") != -1)
+ {
+ accountTypes.Add(AccountType.Bank);
+ accountTypeFound = true;
+ }
+
+ if (!accountTypeFound)
+ {
+ throw new OFXException("Unsupported Account Type");
+ }
+
+ return accountTypes;
+ }
+
+ ///
+ /// Check if OFX file is in SGML or XML format
+ ///
+ ///
+ ///
+ private static bool IsXmlVersion(string file)
+ {
+ return (file.IndexOf("OFXHEADER:100") == -1);
+ }
+
+ ///
+ /// Converts SGML to XML
+ ///
+ /// OFX File (SGML Format)
+ /// OFX File in XML format
+ private static string SGMLToXML(string file)
+ {
+ var reader = new SgmlReader();
+
+ //Inititialize SGML reader
+ reader.InputStream = new StringReader(ParseHeader(file));
+ reader.DocType = "OFX";
+
+ var sw = new StringWriter();
+ var xml = new XmlTextWriter(sw);
+
+ //write output of sgml reader to xml text writer
+ while (!reader.EOF)
+ xml.WriteNode(reader, true);
+
+ //close xml text writer
+ xml.Flush();
+ xml.Close();
+
+ var temp = sw.ToString().TrimStart().Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+
+ return String.Join("", temp);
+ }
+
+ ///
+ /// Checks that the file is supported by checking the header. Removes the header.
+ ///
+ /// OFX file
+ /// File, without the header
+ private static string ParseHeader(string file)
+ {
+ //Select header of file and split into array
+ //End of header worked out by finding first instance of '<'
+ //Array split based of new line & carrige return
+ var header = file.Substring(0, file.IndexOf('<'))
+ .Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+
+ //Check that no errors in header
+ CheckHeader(header);
+
+ //Remove header
+ return file.Substring(file.IndexOf('<') - 1);
+ }
+
+ ///
+ /// Checks that all the elements in the header are supported
+ ///
+ /// Header of OFX file in array
+ private static void CheckHeader(string[] header)
+ {
+ if (header[0] != "OFXHEADER:100")
+ throw new OFXParseException("Incorrect header format");
+
+ if (header[1] != "DATA:OFXSGML")
+ throw new OFXParseException("Data type unsupported: " + header[1] + ". OFXSGML required");
+
+ if (header[2] != "VERSION:102")
+ throw new OFXParseException("OFX version unsupported. " + header[2]);
+
+ if (header[3] != "SECURITY:NONE")
+ throw new OFXParseException("OFX security unsupported");
+
+ if (header[4] != "ENCODING:USASCII")
+ throw new OFXParseException("ASCII Format unsupported:" + header[4]);
+
+ if (header[5] != "CHARSET:1252")
+ throw new OFXParseException("Charecter set unsupported:" + header[5]);
+
+ if (header[6] != "COMPRESSION:NONE")
+ throw new OFXParseException("Compression unsupported");
+
+ if (header[7] != "OLDFILEUID:NONE")
+ throw new OFXParseException("OLDFILEUID incorrect");
+ }
+
+ #region Nested type: OFXSection
+
+ ///
+ /// Section of OFX Document
+ ///
+ private enum OFXSection
+ {
+ SignOn,
+ Statement,
+ AccountInfo,
+ Transactions,
+ Balance,
+ Currency
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/source/OFXSharp/OFXSharp.csproj b/source/OFXSharp/OFXSharp.csproj
index b7013ce..f1ffe77 100644
--- a/source/OFXSharp/OFXSharp.csproj
+++ b/source/OFXSharp/OFXSharp.csproj
@@ -80,6 +80,7 @@
+
diff --git a/source/OFXSharp/Resources.Designer.cs b/source/OFXSharp/Resources.Designer.cs
index 119f382..c760e03 100644
--- a/source/OFXSharp/Resources.Designer.cs
+++ b/source/OFXSharp/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.34003
+// Runtime Version:4.0.30319.18408
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -61,7 +61,7 @@ internal Resources() {
}
///
- /// Looks up a localized string similar to OFX/BANKMSGSRSV1/STMTTRNRS/.
+ /// Looks up a localized string similar to OFX/BANKMSGSRSV1/STMTTRNRS.
///
internal static string BankAccount {
get {
@@ -70,7 +70,7 @@ internal static string BankAccount {
}
///
- /// Looks up a localized string similar to OFX/CREDITCARDMSGSRSV1/CCSTMTTRNRS/CCSTMTRS.
+ /// Looks up a localized string similar to OFX/CREDITCARDMSGSRSV1/CCSTMTTRNRS.
///
internal static string CCAccount {
get {
diff --git a/source/OFXSharp/Resources.resx b/source/OFXSharp/Resources.resx
index 4629925..6bbbaa3 100644
--- a/source/OFXSharp/Resources.resx
+++ b/source/OFXSharp/Resources.resx
@@ -118,10 +118,10 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- OFX/BANKMSGSRSV1/STMTTRNRS/
+ OFX/BANKMSGSRSV1/STMTTRNRS
- OFX/CREDITCARDMSGSRSV1/CCSTMTTRNRS/
+ OFX/CREDITCARDMSGSRSV1/CCSTMTTRNRS
There are insufficent funds to pay your {item}. We have allocated what you have evenly between the {item} but there is no room in the budget for anything else, sorry...
@@ -135,4 +135,4 @@
OFX/SIGNONMSGSRSV1/SONRS
-
+
\ No newline at end of file
diff --git a/source/OFXSharp/SignOn.cs b/source/OFXSharp/SignOn.cs
index 4f5dc46..117dfaf 100644
--- a/source/OFXSharp/SignOn.cs
+++ b/source/OFXSharp/SignOn.cs
@@ -6,22 +6,24 @@ namespace OFXSharp
public class SignOn
{
public string StatusSeverity { get; set; }
-
public DateTime DTServer { get; set; }
-
public int StatusCode { get; set; }
-
public string Language { get; set; }
-
public string IntuBid { get; set; }
+ public string FinancialInstitutionName { get; set; }
+ public string FinancialInstitutionId { get; set; }
public SignOn(XmlNode node)
{
- StatusCode = Convert.ToInt32(node.GetValue("//CODE"));
- StatusSeverity = node.GetValue("//SEVERITY");
- DTServer = node.GetValue("//DTSERVER").ToDate();
- Language = node.GetValue("//LANGUAGE");
- IntuBid = node.GetValue("//INTU.BID");
+ StatusCode = Convert.ToInt32(node.GetValue(".//CODE"));
+ StatusSeverity = node.GetValue(".//SEVERITY");
+ DTServer = node.GetValue(".//DTSERVER").ToDate();
+ Language = node.GetValue(".//LANGUAGE");
+ IntuBid = node.GetValue(".//INTU.BID");
+ FinancialInstitutionName = node.GetValue(".//ORG");
+ FinancialInstitutionId = node.GetValue(".//FID");
}
+
+ public SignOn() { }
}
}
\ No newline at end of file
diff --git a/source/OFXSharp/Statement.cs b/source/OFXSharp/Statement.cs
new file mode 100644
index 0000000..fcd39da
--- /dev/null
+++ b/source/OFXSharp/Statement.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OFXSharp
+{
+ public class Statement
+ {
+ public DateTime StatementStart { get; set; }
+ public DateTime StatementEnd { get; set; }
+ public AccountType AccType { get; set; }
+ public string Currency { get; set; }
+ public Account Account { get; set; }
+ public Balance Balance { get; set; }
+ public List Transactions { get; set; }
+ }
+}
diff --git a/source/OFXSharp/Transaction.cs b/source/OFXSharp/Transaction.cs
index 4124b37..66649ca 100644
--- a/source/OFXSharp/Transaction.cs
+++ b/source/OFXSharp/Transaction.cs
@@ -4,134 +4,118 @@
namespace OFXSharp
{
- public class Transaction
- {
- public OFXTransactionType TransType { get; set; }
-
- public DateTime Date { get; set; }
-
- public decimal Amount { get; set; }
-
- public string TransactionID { get; set; }
-
- public string Name { get; set; }
-
- public DateTime TransactionInitializationDate { get; set; }
-
- public DateTime FundAvaliabilityDate { get; set; }
-
- public string Memo { get; set; }
-
- public string IncorrectTransactionID { get; set; }
-
- public TransactionCorrectionType TransactionCorrectionAction { get; set; }
-
- public string ServerTransactionID { get; set; }
-
- public string CheckNum { get; set; }
-
- public string ReferenceNumber { get; set; }
-
- public string Sic { get; set; }
-
- public string PayeeID { get; set; }
-
- public Account TransactionSenderAccount { get; set; }
-
- public string Currency { get; set; }
-
- public Transaction()
- {
- }
-
- public Transaction(XmlNode node, string currency)
- {
- TransType = GetTransactionType(node.GetValue(".//TRNTYPE"));
- Date = node.GetValue(".//DTPOSTED").ToDate();
- TransactionInitializationDate = node.GetValue(".//DTUSER").ToDate();
- FundAvaliabilityDate = node.GetValue(".//DTAVAIL").ToDate();
-
- try
- {
- Amount = Convert.ToDecimal(node.GetValue(".//TRNAMT"), CultureInfo.InvariantCulture);
- }
- catch (Exception ex)
- {
- throw new OFXParseException("Transaction Amount unknown", ex);
- }
-
- try
- {
- TransactionID = node.GetValue(".//FITID");
- }
- catch (Exception ex)
- {
- throw new OFXParseException("Transaction ID unknown", ex);
- }
-
- IncorrectTransactionID = node.GetValue(".//CORRECTFITID");
-
-
- //If Transaction Correction Action exists, populate
- var tempCorrectionAction = node.GetValue(".//CORRECTACTION");
-
- TransactionCorrectionAction = !String.IsNullOrEmpty(tempCorrectionAction)
- ? GetTransactionCorrectionType(tempCorrectionAction)
- : TransactionCorrectionType.NA;
-
- ServerTransactionID = node.GetValue(".//SRVRTID");
- CheckNum = node.GetValue(".//CHECKNUM");
- ReferenceNumber = node.GetValue(".//REFNUM");
- Sic = node.GetValue(".//SIC");
- PayeeID = node.GetValue(".//PAYEEID");
- Name = node.GetValue(".//NAME");
- Memo = node.GetValue(".//MEMO");
-
- //If differenct currency to CURDEF, populate currency
- if (NodeExists(node, ".//CURRENCY"))
- Currency = node.GetValue(".//CURRENCY");
- else if (NodeExists(node, ".//ORIGCURRENCY"))
- Currency = node.GetValue(".//ORIGCURRENCY");
+ public class Transaction
+ {
+ public OFXTransactionType TransType { get; set; }
+ public DateTime Date { get; set; }
+ public decimal Amount { get; set; }
+ public string TransactionID { get; set; }
+ public string Name { get; set; }
+ public DateTime TransactionInitializationDate { get; set; }
+ public DateTime FundAvaliabilityDate { get; set; }
+ public string Memo { get; set; }
+ public string IncorrectTransactionID { get; set; }
+ public TransactionCorrectionType TransactionCorrectionAction { get; set; }
+ public string ServerTransactionID { get; set; }
+ public string CheckNum { get; set; }
+ public string ReferenceNumber { get; set; }
+ public string Sic { get; set; }
+ public string PayeeID { get; set; }
+ public Account TransactionSenderAccount { get; set; }
+ public string Currency { get; set; }
+
+ public Transaction()
+ {
+ }
+
+ public Transaction(XmlNode node, string currency)
+ {
+ TransType = GetTransactionType(node.GetValue(".//TRNTYPE"));
+ Date = node.GetValue(".//DTPOSTED").ToDate();
+ TransactionInitializationDate = node.GetValue(".//DTUSER").ToDate();
+ FundAvaliabilityDate = node.GetValue(".//DTAVAIL").ToDate();
+
+ try
+ {
+ Amount = Convert.ToDecimal(node.GetValue(".//TRNAMT"), CultureInfo.InvariantCulture);
+ }
+ catch (Exception ex)
+ {
+ throw new OFXParseException("Transaction Amount unknown", ex);
+ }
+
+ try
+ {
+ TransactionID = node.GetValue(".//FITID");
+ }
+ catch (Exception ex)
+ {
+ throw new OFXParseException("Transaction ID unknown", ex);
+ }
+
+ IncorrectTransactionID = node.GetValue(".//CORRECTFITID");
+
+
+ //If Transaction Correction Action exists, populate
+ var tempCorrectionAction = node.GetValue(".//CORRECTACTION");
+
+ TransactionCorrectionAction = !String.IsNullOrEmpty(tempCorrectionAction)
+ ? GetTransactionCorrectionType(tempCorrectionAction)
+ : TransactionCorrectionType.NA;
+
+ ServerTransactionID = node.GetValue(".//SRVRTID");
+ CheckNum = node.GetValue(".//CHECKNUM");
+ ReferenceNumber = node.GetValue(".//REFNUM");
+ Sic = node.GetValue(".//SIC");
+ PayeeID = node.GetValue(".//PAYEEID");
+ Name = node.GetValue(".//NAME");
+ Memo = node.GetValue(".//MEMO");
+
+ //If differenct currency to CURDEF, populate currency
+ if (NodeExists(node, ".//CURRENCY"))
+ Currency = node.GetValue(".//CURRENCY");
+ else if (NodeExists(node, ".//ORIGCURRENCY"))
+ Currency = node.GetValue(".//ORIGCURRENCY");
//If currency not different, set to CURDEF
- else
- Currency = currency;
-
- //If senders bank/credit card details avaliable, add
- if (NodeExists(node, ".//BANKACCTTO"))
- TransactionSenderAccount = new Account(node.SelectSingleNode(".//BANKACCTTO"), AccountType.BANK);
- else if (NodeExists(node, ".//CCACCTTO"))
- TransactionSenderAccount = new Account(node.SelectSingleNode(".//CCACCTTO"), AccountType.CC);
- }
-
- ///
- /// Returns TransactionType from string version
- ///
- /// string version of transaction type
- /// Enum version of given transaction type string
- private OFXTransactionType GetTransactionType(string transactionType)
- {
- return (OFXTransactionType) Enum.Parse(typeof (OFXTransactionType), transactionType);
- }
-
- ///
- /// Returns TransactionCorrectionType from string version
- ///
- /// string version of Transaction Correction Type
- /// Enum version of given TransactionCorrectionType string
- private TransactionCorrectionType GetTransactionCorrectionType(string transactionCorrectionType)
- {
- return (TransactionCorrectionType) Enum.Parse(typeof (TransactionCorrectionType), transactionCorrectionType);
- }
-
- ///
- /// Checks if a node exists
- ///
- /// Node to search in
- /// XPath to node you want to see if exists
- ///
- private bool NodeExists(XmlNode node, string xpath)
- {
- return (node.SelectSingleNode(xpath) != null);
- }
- }
+ else
+ Currency = currency;
+
+ //If senders bank/credit card details avaliable, add
+ if (NodeExists(node, ".//BANKACCTTO"))
+ TransactionSenderAccount = new Account(node.SelectSingleNode(".//BANKACCTTO"), AccountType.Bank);
+ else if (NodeExists(node, ".//CCACCTTO"))
+ TransactionSenderAccount = new Account(node.SelectSingleNode(".//CCACCTTO"), AccountType.CreditCard);
+ }
+
+ ///
+ /// Returns TransactionType from string version
+ ///
+ /// string version of transaction type
+ /// Enum version of given transaction type string
+ private OFXTransactionType GetTransactionType(string transactionType)
+ {
+ return (OFXTransactionType)Enum.Parse(typeof(OFXTransactionType), transactionType);
+ }
+
+ ///
+ /// Returns TransactionCorrectionType from string version
+ ///
+ /// string version of Transaction Correction Type
+ /// Enum version of given TransactionCorrectionType string
+ private TransactionCorrectionType GetTransactionCorrectionType(string transactionCorrectionType)
+ {
+ return (TransactionCorrectionType)Enum.Parse(typeof(TransactionCorrectionType), transactionCorrectionType);
+ }
+
+ ///
+ /// Checks if a node exists
+ ///
+ /// Node to search in
+ /// XPath to node you want to see if exists
+ ///
+ private bool NodeExists(XmlNode node, string xpath)
+ {
+ return (node.SelectSingleNode(xpath) != null);
+ }
+ }
}
\ No newline at end of file