Example #1
0
        /// <summary>
        /// Add an account to the database - Using OFX data specification
        /// </summary>
        /// <param name="account">Populated account object to add to database</param>
        /// <param name="financialInstitution">OFX financial institution to link in database. Will be created if necessary.</param>
        /// <param name="fiUser">User credentials for the financial institution</param>
        /// <returns>Created account</returns>
        public Account AddAccount(Account account, OFX.Types.FinancialInstitution financialInstitution, FinancialInstitutionUser fiUser)
        {
            // TODO: See if there's an existing FI or user with this info already
            // Look for existing FI entry with the same name
            FinancialInstitution fi;

            try
            {
                fi = DbContext.FinancialInstitutions.First(i => i.Name == financialInstitution.Name);
            }
            catch (Exception ex)
            {
                // Can result in InvalidOperationException or NullReferenceException depending on provider
                if (ex is InvalidOperationException || ex is NullReferenceException)
                {
                    // FI Doesn't exist, add a new one
                    fi = new FinancialInstitution
                    {
                        Name = financialInstitution.Name,
                        OfxFinancialUnitId = financialInstitution.FinancialId,
                        OfxOrganizationId  = financialInstitution.OrganizationId,
                        OfxUpdateUrl       = financialInstitution.ServiceEndpoint.ToString()
                    };
                    DbContext.FinancialInstitutions.Add(fi);
                }
                else
                {
                    throw; // Unhandled
                }
            }

            // Look for existing user under this FI with same userId
            try
            {
                fiUser = fi.Users.First(u => u.UserId == fiUser.UserId && u.Password == fiUser.Password);
            }
            catch (Exception ex)
            {
                // Can result in InvalidOperationException or NullReferenceException depending on provider
                if (ex is InvalidOperationException || ex is NullReferenceException)
                {
                    // User doesn't exist, add as new
                    fi.Users.Add(fiUser);
                    DbContext.FinancialInstitutionUsers.Add(fiUser);
                }
                else
                {
                    throw; // Unhandled
                }
            }

            fiUser.Accounts.Add(account);
            DbContext.Accounts.Add(account);

            return(account);
        }
Example #2
0
        /// <summary>
        /// Retrieve the list of accounts from a financial institution using OFX and return all accounts that are not already present in the database
        /// </summary>
        /// <param name="financialInstitution">Financial institution to query</param>
        /// <param name="fiCredentials">Credentials for financial institution account</param>
        /// <returns>List of accounts</returns>
        public static async Task <IEnumerable <Account> > EnumerateNewAccounts(
            OFX.Types.FinancialInstitution financialInstitution, OFX.Types.Credentials fiCredentials)
        {
            using (BackgroundTaskTracker.BeginTask("Retrieving Account Information"))
            {
                var ofxService     = new OFX2Service(financialInstitution, fiCredentials);
                var accountList    = new List <Account>();
                var ofxAccountList = await ofxService.ListAccounts().ConfigureAwait(false);

                // TODO: If ofxAccountList is null, raise a more detailed exception

                using (var dataService = new DataService())
                {
                    foreach (var ofxAccount in ofxAccountList)
                    {
                        // Convert from OFX account type to db account type and encode account id
                        AccountType accountType = AccountType.Checking;
                        string      accountId   = "";
                        if (ofxAccount.GetType() == typeof(OFX.Types.CheckingAccount))
                        {
                            accountType = AccountType.Checking;
                            accountId   = ((OFX.Types.CheckingAccount)ofxAccount).RoutingId + ":" + ofxAccount.AccountId;
                        }
                        else if (ofxAccount.GetType() == typeof(OFX.Types.SavingsAccount))
                        {
                            accountType = AccountType.Savings;
                            accountId   = ((OFX.Types.SavingsAccount)ofxAccount).RoutingId + ":" + ofxAccount.AccountId;
                        }
                        else if (ofxAccount.GetType() == typeof(OFX.Types.CreditCardAccount))
                        {
                            accountType = AccountType.Creditcard;
                            accountId   = ofxAccount.AccountId;
                        }

                        // Look for a matching account in the database
                        if (!dataService.GetAccountByFinancialId(accountId).Any())
                        {
                            // This account is not already in the DB, add to new account list
                            accountList.Add(new Account
                            {
                                AccountName =
                                    accountType + ":" +
                                    ofxAccount.AccountId.Substring(ofxAccount.AccountId.Length - 4),
                                AccountType = accountType.ToString(),
                                Currency    = "USD",
                                FiAccountId = accountId
                            });
                        }
                    }
                }

                // Return the finalized list of new accounts
                return(accountList);
            }
        }
Example #3
0
        /// <summary>
        /// Verify the provided account credentials. Raises an exception if validation fails
        /// </summary>
        /// <param name="financialInstitution">Financial institution to query</param>
        /// <param name="fiCredentials">Credentials for financial institution account</param>
        /// <returns>List of accounts</returns>
        public static async Task VerifyAccountCredentials(FinancialInstitution financialInstitution,
                                                          OFX.Types.Credentials fiCredentials)
        {
            using (BackgroundTaskTracker.BeginTask("Verifying Credentials"))
            {
                // Convert from data model FI into OFX FI
                var ofxFinancialInstitition = new OFX.Types.FinancialInstitution(financialInstitution.Name,
                                                                                 new Uri(financialInstitution.OfxUpdateUrl), financialInstitution.OfxOrganizationId,
                                                                                 financialInstitution.OfxFinancialUnitId);

                var ofxService = new OFX2Service(ofxFinancialInstitition, fiCredentials);

                // Call list accounts to validate credentials
                await ofxService.ListAccounts().ConfigureAwait(false);
            }
        }
Example #4
0
        public void TestAddAccountFromFi()
        {
            // Mock setup for DataService
            var mockAccountSet = new Mock <DbSet <Account> >();
            var mockFiSet      = new Mock <DbSet <FinancialInstitution> >();
            var mockFiUserSet  = new Mock <DbSet <FinancialInstitutionUser> >();
            var mockContext    = new Mock <SoCashDbContext>();

            mockContext.Setup(m => m.Accounts).Returns(mockAccountSet.Object);
            mockContext.Setup(m => m.FinancialInstitutions).Returns(mockFiSet.Object);
            mockContext.Setup(m => m.FinancialInstitutionUsers).Returns(mockFiUserSet.Object);


            // Account to add
            var newAccount = new Account
            {
                AccountName = "Test Account",
                AccountType = AccountType.Checking.ToString(),
                Currency    = "USD"
            };

            // Dummy FI
            var financialInstitution = new OFX.Types.FinancialInstitution("Test FI", new Uri("http://test.com/"), "Test Org ID", "Test Unit ID");

            // Dummy FI User
            var financialInstitutionUser = new FinancialInstitutionUser {
                UserId = "Test User", Password = "******"
            };


            // Add the account in a transaction
            using (var service = new DataService(mockContext.Object))
            {
                service.AddAccount(newAccount, financialInstitution, financialInstitutionUser);
            }

            // Verify that the service added 1 account, 1 fi and 1 fi user
            mockAccountSet.Verify(m => m.Add(newAccount), Times.Once());
            mockFiSet.Verify(m => m.Add(It.IsAny <FinancialInstitution>()), Times.Once());
            mockFiUserSet.Verify(m => m.Add(financialInstitutionUser), Times.Once());

            // Verify that the transaction ended properly
            mockContext.Verify(m => m.SaveChanges(), Times.Once());
        }
Example #5
0
        public void TestAddAccountFromFi()
        {
            // Mock setup for DataService
            var mockAccountSet = new Mock<DbSet<Account>>();
            var mockFiSet = new Mock<DbSet<FinancialInstitution>>();
            var mockFiUserSet = new Mock<DbSet<FinancialInstitutionUser>>();
            var mockContext = new Mock<SoCashDbContext>();
            mockContext.Setup(m => m.Accounts).Returns(mockAccountSet.Object);
            mockContext.Setup(m => m.FinancialInstitutions).Returns(mockFiSet.Object);
            mockContext.Setup(m => m.FinancialInstitutionUsers).Returns(mockFiUserSet.Object);

            // Account to add
            var newAccount = new Account
            {
                AccountName = "Test Account",
                AccountType = AccountType.Checking.ToString(),
                Currency = "USD"
            };

            // Dummy FI
            var financialInstitution = new OFX.Types.FinancialInstitution("Test FI", new Uri("http://test.com/"), "Test Org ID", "Test Unit ID");

            // Dummy FI User
            var financialInstitutionUser = new FinancialInstitutionUser { UserId = "Test User", Password = "******" };

            // Add the account in a transaction
            using (var service = new DataService(mockContext.Object))
            {

                service.AddAccount(newAccount, financialInstitution, financialInstitutionUser);
            }

            // Verify that the service added 1 account, 1 fi and 1 fi user
            mockAccountSet.Verify(m => m.Add(newAccount), Times.Once());
            mockFiSet.Verify(m => m.Add(It.IsAny<FinancialInstitution>()), Times.Once());
            mockFiUserSet.Verify(m => m.Add(financialInstitutionUser), Times.Once());

            // Verify that the transaction ended properly
            mockContext.Verify(m => m.SaveChanges(), Times.Once());
        }
Example #6
0
        /// <summary>
        /// Download OFX transactions for an account and merge them into the account transaction list
        /// </summary>
        /// <param name="account">Account configured with financial institution login information</param>
        public static async Task DownloadOfxTransactionsForAccount(Account account)
        {
            using (BackgroundTaskTracker.BeginTask("Downloading statements"))
            {
                // Default retrieval parameters
                OFX2Service       ofxService;
                OFX.Types.Account ofxAccount;
                var endTime   = DateTimeOffset.Now;
                var startTime = new DateTimeOffset(new DateTime(1997, 1, 1));

                using (var dataService = new DataService())
                {
                    // Retrieve matching account from DB - we need to get an entity in the current db session
                    var updateAccount = dataService.GetAccountById(account.AccountId);

                    // Form FI connection properties for transaction retrieval
                    var fi = new OFX.Types.FinancialInstitution(
                        updateAccount.FinancialInstitutionUser.FinancialInstitution.Name,
                        new Uri(updateAccount.FinancialInstitutionUser.FinancialInstitution.OfxUpdateUrl),
                        updateAccount.FinancialInstitutionUser.FinancialInstitution.OfxOrganizationId,
                        updateAccount.FinancialInstitutionUser.FinancialInstitution.OfxFinancialUnitId
                        );

                    // Form credentials for login
                    var credentials = new OFX.Types.Credentials(
                        updateAccount.FinancialInstitutionUser.UserId,
                        updateAccount.FinancialInstitutionUser.Password
                        );

                    // Create service
                    ofxService = new OFX2Service(fi, credentials);

                    // Create proper account type for this account
                    var accountType = (AccountType)account.AccountType;
                    if (accountType == AccountType.Checking)
                    {
                        // Split routing and account id from combined string
                        var accountIdComponents = account.FiAccountId.Split(':');
                        ofxAccount = new OFX.Types.CheckingAccount(accountIdComponents[0], accountIdComponents[1],
                                                                   "",
                                                                   true);
                    }
                    else if (accountType == AccountType.Savings)
                    {
                        // Split routing and account id from combined string
                        var accountIdComponents = account.FiAccountId.Split(':');
                        ofxAccount = new OFX.Types.SavingsAccount(accountIdComponents[0], accountIdComponents[1], "",
                                                                  true);
                    }
                    else //if (accountType == AccountType.CREDITCARD)
                    {
                        ofxAccount = new OFX.Types.CreditCardAccount(account.FiAccountId, "", true);
                    }

                    // Use the start time of the latest transaction if we have any
                    try
                    {
                        var lastTransaction =
                            (from transaction in updateAccount.Transactions
                             orderby transaction.Date descending
                             select transaction).First();
                        startTime = new DateTimeOffset(lastTransaction.Date);
                    }
                    catch (InvalidOperationException)
                    {
                        // No transactions - ignore and use default start date.
                    }
                }

                // Retrieve statement(s) (should only be one per protocol, but we can handle any number)
                try
                {
                    var ofxStatments =
                        await ofxService.GetStatement(ofxAccount, startTime, endTime).ConfigureAwait(false);

                    foreach (var ofxStatement in ofxStatments)
                    {
                        MergeStatementTransactionsIntoAccount(account, ofxStatement);
                    }
                }
                catch (OfxException ex)
                {
                    MessageBox.Show(ex.Message, "Error");
                }
                catch (InvalidOperationException ex)
                {
                    MessageBox.Show("The data provided by the financial institution could not be parsed.", "Error");
                }
            }
        }
Example #7
0
        /// <summary>
        /// Verify the provided account credentials. Raises an exception if validation fails
        /// </summary>
        /// <param name="financialInstitution">Financial institution to query</param>
        /// <param name="fiCredentials">Credentials for financial institution account</param>
        /// <returns>List of accounts</returns>
        public static async Task VerifyAccountCredentials(FinancialInstitution financialInstitution,
            OFX.Types.Credentials fiCredentials)
        {
            using (BackgroundTaskTracker.BeginTask("Verifying Credentials"))
            {

                // Convert from data model FI into OFX FI
                var ofxFinancialInstitition = new OFX.Types.FinancialInstitution(financialInstitution.Name,
                    new Uri(financialInstitution.OfxUpdateUrl), financialInstitution.OfxOrganizationId,
                    financialInstitution.OfxFinancialUnitId);

                var ofxService = new OFX2Service(ofxFinancialInstitition, fiCredentials);

                // Call list accounts to validate credentials
                await ofxService.ListAccounts().ConfigureAwait(false);
            }
        }
Example #8
0
        /// <summary>
        /// Download OFX transactions for an account and merge them into the account transaction list
        /// </summary>
        /// <param name="account">Account configured with financial institution login information</param>
        public static async Task DownloadOfxTransactionsForAccount(Account account)
        {
            using (BackgroundTaskTracker.BeginTask("Downloading statements"))
            {
                // Default retrieval parameters
                OFX2Service ofxService;
                OFX.Types.Account ofxAccount;
                var endTime = DateTimeOffset.Now;
                var startTime = new DateTimeOffset(new DateTime(1997, 1, 1));

                using (var dataService = new DataService())
                {
                    // Retrieve matching account from DB - we need to get an entity in the current db session
                    var updateAccount = dataService.GetAccountById(account.AccountId);

                    // Form FI connection properties for transaction retrieval
                    var fi = new OFX.Types.FinancialInstitution(
                        updateAccount.FinancialInstitutionUser.FinancialInstitution.Name,
                        new Uri(updateAccount.FinancialInstitutionUser.FinancialInstitution.OfxUpdateUrl),
                        updateAccount.FinancialInstitutionUser.FinancialInstitution.OfxOrganizationId,
                        updateAccount.FinancialInstitutionUser.FinancialInstitution.OfxFinancialUnitId
                        );

                    // Form credentials for login
                    var credentials = new OFX.Types.Credentials(
                        updateAccount.FinancialInstitutionUser.UserId,
                        updateAccount.FinancialInstitutionUser.Password
                        );

                    // Create service
                    ofxService = new OFX2Service(fi, credentials);

                    // Create proper account type for this account
                    var accountType = (AccountType) account.AccountType;
                    if (accountType == AccountType.Checking)
                    {
                        // Split routing and account id from combined string
                        var accountIdComponents = account.FiAccountId.Split(':');
                        ofxAccount = new OFX.Types.CheckingAccount(accountIdComponents[0], accountIdComponents[1],
                            "",
                            true);
                    }
                    else if (accountType == AccountType.Savings)
                    {
                        // Split routing and account id from combined string
                        var accountIdComponents = account.FiAccountId.Split(':');
                        ofxAccount = new OFX.Types.SavingsAccount(accountIdComponents[0], accountIdComponents[1], "",
                            true);
                    }
                    else //if (accountType == AccountType.CREDITCARD)
                    {
                        ofxAccount = new OFX.Types.CreditCardAccount(account.FiAccountId, "", true);
                    }

                    // Use the start time of the latest transaction if we have any
                    try
                    {
                        var lastTransaction =
                            (from transaction in updateAccount.Transactions
                                orderby transaction.Date descending
                                select transaction).First();
                        startTime = new DateTimeOffset(lastTransaction.Date);

                    }
                    catch (InvalidOperationException)
                    {
                        // No transactions - ignore and use default start date.
                    }

                }

                // Retrieve statement(s) (should only be one per protocol, but we can handle any number)
                var ofxStatments = await ofxService.GetStatement(ofxAccount, startTime, endTime).ConfigureAwait(false);

                if (!String.IsNullOrEmpty(ofxStatments.Item2) || !String.IsNullOrWhiteSpace(ofxStatments.Item2))
                {
                    MessageBox.Show(ofxStatments.Item2, "Error");
                }

                foreach (var ofxStatement in ofxStatments.Item1)
                    MergeStatementTransactionsIntoAccount(account, ofxStatement);
            }

        }