Real Test Driven Development – Thinking of Money 9

 

First we need to set the update from within the engine

        [Test]
        public void Update()
        {
            _Engine.AddTransaction(10.10, "test category", "test description", DateTime.Now);

            Assert.IsTrue(_Engine.Update());
        }
        public bool Update()
        {
            bool transFailed = false;
            
            foreach (MoneyTransaction moneyTransaction in _History)
            {
                if (!moneyTransaction.Update())
                    transFailed = true;
            }

            return !transFailed;
        }
if a transaction was to fail, we can work through the collection checking the error strings, to discover the 
reason.
we are at the point now that the only thing left is to load the transactions into the engine, we will need a data 
tier to do this which I suppose will return a data set or data table, we can then itterate through this and fill 
the history collection with transactions, using a simple add.
I want to do this using a simple Load command, which will clear the current list and reload it, thus when a user
presses the save button or what ever the engine will be updated automatically, removing those transactions that 
have been deleted, and saving new and updated items.
I am going to need the get command and get parameter methods, so I think I will move them out to a helper class,
that can be static and called as required.
   public static class DataHelpers
    {
        public static SqlParameter GetParam(string paramName, object paramValue, SqlDbType paramType, 
ParameterDirection paramDirection)
        {
            SqlParameter param = new SqlParameter(paramName, paramType);
            param.Direction = paramDirection;
            param.Value = paramValue;
            return param;
        }

        public static  SqlParameter GetParam(string paramName, object paramValue, SqlDbType paramType, 
int ParamSize, ParameterDirection paramDirection)
        {
            SqlParameter param = GetParam(paramName, paramValue, paramType, paramDirection);
            param.Size = ParamSize;

            return param;
        }

        public static SqlCommand GetCommand(string procedureName, string connectionString)
        {
            SqlConnection connection = new SqlConnection(connectionString);
            connection.Open();

            SqlCommand cmd = new SqlCommand(procedureName, connection);
            cmd.CommandType = CommandType.StoredProcedure;

            return cmd;
        }
    }

 I have updated DBMoneyTransactions to use the above class, so next is the select function the DBengine, first 
we need a test.
       [Test]
        public void GetTransactionsInDateOrder()
        {
            DBEngine dbEngine = new DBEngine();

            DataTable transactions = dbEngine.GetTransactionsInDateOrder();

            Assert.IsNotNull(transactions);
            Assert.IsTrue(transactions.Rows.Count > 0);
        }

I decided that the data tier should return a data table or null, this will allow the engine to enumerate the table one row at a time and create a transaction with all the correct data.

      public DataTable GetTransactionsInDateOrder(ref string helpText)
        {
            SqlCommand cmd = null;
            SqlDataAdapter adapter;

            try
            {
                cmd = DataHelpers.GetCommand("uspSelectTransactionsDateOrder", STR_Connection);
                
                DataSet data = new DataSet();
                adapter = new SqlDataAdapter(cmd);
                adapter.Fill(data);

                if (data.Tables.Count > 0)
                    return data.Tables[0];

                return null;               
            }
            catch (SqlException sqlEx)
            {
                helpText = sqlEx.Message;
                return null;
            }
            finally
            {
                if (cmd != null)
                {
                    cmd.Connection.Close();
                    cmd.Dispose();
                }
            }
        }
    }

so next we move up to the engine, we need a load function which will call the above routine and fill the history list with the data. so first a test.

        [Test]
        public void Load()
        {
            Assert.IsTrue(_Engine.LoadTransactions(), _Engine.ErrorText);

            Assert.IsTrue(_Engine.History.Count > 0, "no records returned");
        }

note I added a new property to the money engine that will return any errors from child objects, so they can be used by the UI to inform the user that there was a problem.

and then the problems started, I found that big int did not covert easily to int, and money didn’t seam to be a good database type to use. so after much changing of types I ended up with the money being changed to float, which converts in c# to a double. the transaction id is stored as a big int, this converts to an int32 in c#.

     public bool LoadTransactions()
        {
            DBMoneyEngine database = new DBMoneyEngine();
            DataTable transactions = database.GetTransactionsInDateOrder(ref _ErrorText);

            if (transactions == null)
                return false;

            if (transactions.Rows.Count > 0)
            {
                foreach (DataRow transData in transactions.Rows)
                {
                    Int32 transID = transData["TransID"] is DBNull ? 0 : Convert.ToInt32(transData["TransID"]);
                    TransactionType transType = transData["TransType"] is DBNull ? 
                    TransactionType.AdditionTransaction : (int) transData["TransType"] == 0 ? 
                        TransactionType.AdditionTransaction : TransactionType.SubtractionTransaction;
                    DateTime transDate = transData["TransDate"] is DBNull ? 
                             DateTime.Now : (DateTime) transData["TransDate"];
                    string transCategory = transData["TransCategory"] is DBNull ? string.Empty : 
                             transData["TransCategory"].ToString();
                    string transDescription = transData["TransDescription"] is DBNull ? string.Empty : 
                             transData["TransDescription"].ToString();
                    double transAmount = transData["TransAmount"] is DBNull ? 0 : 
                             (double)transData["TransAmount"];                     

                    MoneyTransaction tran = new MoneyTransaction(transID, transAmount, transCategory, 
                      transDescription, transDate, transType );

                    _History.Add(tran);

                }
            }

            return true;
       }
For those who are new to c# and have not transferred from c or c++, the above constructs may look strange
but they are really just short hand for an if statement for example.
if (transData["TransID"] is DBNull)
    transID = 0;
else
    transID = Convert.ToInt32(transData["TransID"]);

can be converted to 
Int32 transID = transData["TransID"] is DBNull ? 0 : Convert.ToInt32(transData["TransID"]); 
the part from the = sign to the ? is the if statement, the part after the ? is the if true part, and after 
the : is the false part, as can be seen from the above code these can be nested as well, although for 
readability it is best not to go far.
 

Posted in Projects | Leave a comment

Real Test Driven Development – Thinking of Money 8

 

so I now have an add and update, the delete will follow the same format, I have currently stored the connection string in a constant, but I suppose it can eventually be stored in an application setting. so for the delete

        [Test]
        public void DeleteTransaction()
        {
            DBMoneyTransaction transaction = new DBMoneyTransaction();

            string helpText = string.Empty;

            Assert.IsTrue(transaction.DeleteTransaction(0, ref helpText), helpText);
        }
        public bool DeleteTransaction(int transID, ref string helpText)
        {
            SqlCommand cmd = null;

            try
            {
                cmd = GetCommand("uspDeleteTransaction", STR_Connection);
                cmd.Parameters.Add(GetParam("@TransID", transID, SqlDbType.Int, ParameterDirection.Input));

                cmd.ExecuteNonQuery();

                return true;
            }
            catch (SqlException sqlEx)
            {
                helpText = sqlEx.Message;
                return false;
            }
            finally
            {
                if (cmd != null)
                {
                    cmd.Connection.Close();
                    cmd.Dispose();
                }
            }
        }

this passes and with the new functions GetCommand, and GetParam, the procedure is nice and short.  now we need to be able to load a transaction or a group of transactions. I have been thinking that if

the engine should have a collection of transactions, so I think it is here that the get routine should be, which means that the transaction data class is finished, so we can no finish the transaction class by linking the add, update and delete functions to the transaction class.

first I want to create an update function, this will call the insert or update database routines as required.

        [Test]
        public void Update()
        {
            Assert.IsTrue(_MoneyTrans.Update(),_MoneyTrans.ErrorText);
        }
        public bool Update()
        {
            DBMoneyTransaction database = new DBMoneyTransaction();

            if (TransactionID == 0)
            {
                _TransactionID = database.InsertTransaction((int)TransType, TransactionDate, TransactionCategory, 
TransactionDescription, TransactionAmount, ref _ErrorText);
                if (TransactionID == 0)
                    return false;
                else
                    return true;
            }
            else
                return database.UpdateTransaction(TransactionID, (int)TransType, TransactionDate, 
TransactionCategory, TransactionDescription, TransactionAmount, ref _ErrorText);
        }

and a delete function should rearly be part of the update, so we simply have to call update on transaction and the appropriate actions will be performed.

        [Test]
        public void Delete()
        {
       // so we actually have to call the delete routine 
       Assert.IsTrue(_MoneyTrans.Update(), _MoneyTrans.ErrorText); 
            _MoneyTrans.ToDelete = true;
            Assert.IsTrue(_MoneyTrans.Update());
        }

and after a bit of refractoring to make the update function more readable I ended with this

     public bool Update()
        {
            DBMoneyTransaction database = new DBMoneyTransaction();

            if (ToDelete)
                return DeleteTransaction(database);

            if (TransactionID == 0)
                return InsertTransaction(database);
            else
                return database.UpdateTransaction(TransactionID, (int)TransType, TransactionDate, 
                       TransactionCategory, TransactionDescription, TransactionAmount, ref _ErrorText);
        }

        private bool DeleteTransaction(DBMoneyTransaction database)
        {
            if (TransactionID != 0)
                return database.DeleteTransaction(TransactionID, ref _ErrorText);
            else
                return true;            
        }

        private bool InsertTransaction(DBMoneyTransaction database)
        {
            _TransactionID = database.InsertTransaction((int)TransType, TransactionDate, 
                             TransactionCategory, TransactionDescription, TransactionAmount, ref _ErrorText);
            if (TransactionID == 0)
                return false;
            else
                return true;
        }

So next we need to get back to the money engine and create a group of transactions and load them from the database, then be able to update and delete them each using the above command.

Posted in Projects | Leave a comment

Real Test Driven Development – Thinking of Money 7

 

The data layer I have thought and done some coding without tests, and have decided that I am going to put the code in a separate class, this will make it easier to refractor, and may be then I will combine with the main class, usually though I like to keep the data access separate, as it allows me to change the database without effecting the business layer. (though in all honestly I have never had to yet!)

 ok so we start with the standard two tests, hook and create.

using NUnit.Framework;

namespace MoneyEngine.UnitTests
{
    [TestFixture]
    public class Tests
    {
        [Test]
        public void Hook()
        {
            Assert.IsTrue(true);
        }

        [Test]
        public void Create()
        {
            Assert.IsNotNull(new DBMoneyTransaction);
        }
    }
}

and then a test to insert a row into the database.

        [Test]
        public void AddTransaction()
        {
            DBMoneyTransaction transaction = new DBMoneyTransaction();

            string helpText = String.Empty;
            int transID = transaction.InsertTransaction(0, System.DateTime.Now, "Test", "Test Entry", 0.0, 
       ref helpText);

            Assert.IsTrue(transID > 0, helpText);
  }
and the code within the class
using System;
using System.Data;
using System.Data.SqlClient;

namespace MoneyEngine
{
    class DBMoneyTransaction
    {
        public int InsertTransaction(int transType, DateTime transDate, string transCategory, string transDescription, double transAmount, ref string helpText)
        {
            SqlCommand cmd = null;

            try
            {
                SqlConnection connection = new SqlConnection(@"Data Source=DSLAPTOP\SQLEXPRESS;Initial Catalog=MoneyEngine;Integrated Security=True");
                connection.Open();

                cmd = new SqlCommand("uspInsertTransaction", connection);
                cmd.CommandType = CommandType.StoredProcedure;

                SqlParameter param = new SqlParameter("@TransType", SqlDbType.Int);
                param.Direction = ParameterDirection.Input;
                param.Value = transType;
                cmd.Parameters.Add(param);

                param = new SqlParameter("@TransDate", SqlDbType.DateTime);
                param.Direction = ParameterDirection.Input;
                param.Value = transDate;
                cmd.Parameters.Add(param);
                
                param = new SqlParameter("@TransCategory", SqlDbType.NVarChar, -1);
                param.Direction = ParameterDirection.Input;
                param.Value = transCategory;
                cmd.Parameters.Add(param);

                param = new SqlParameter("@TransDescription", SqlDbType.NVarChar, -1);
                param.Direction = ParameterDirection.Input;
                param.Value = transDescription;
                cmd.Parameters.Add(param);

                param = new SqlParameter("@transAmount", SqlDbType.Money);
                param.Direction = ParameterDirection.Input;
                param.Value = transAmount;
                cmd.Parameters.Add(param);

                param = new SqlParameter("@TransID", SqlDbType.Int);
                param.Direction = ParameterDirection.Output;
                cmd.Parameters.Add(param);

                cmd.ExecuteNonQuery();

                return (int) cmd.Parameters["@TransID"].Value;
            }
            catch (SqlException sqlEx)
            {
                helpText = sqlEx.Message;
                return 0;
            }
            finally
            {
                if (cmd != null)
                {
                    cmd.Connection.Close();
                    cmd.Dispose();
                }
            }
        }
    }
}

The test passes, but the above code needs some serious refractoring, like a lot of data access code, it is long and very repetivate, I know that there are products that will generate this code automatically, but I am old fashioned and like to write it myself.

OK to the refractoring, I am going to extract the parameter construction into a separate methods.

        private SqlParameter GetParam(string paramName, object paramValue, SqlDbType paramType, ParameterDirection paramDirection)
        {
            SqlParameter param = new SqlParameter(paramName, paramType);
            param.Direction = paramDirection;
            param.Value = paramValue;
            return param;
        }

        private SqlParameter GetParam(string paramName, object paramValue, SqlDbType paramType, int ParamSize, ParameterDirection paramDirection)
        {
            SqlParameter param = GetParam(paramName, paramValue, paramType, paramDirection);
            param.Size = ParamSize;

            return param;
        }

        public int InsertTransaction(int transType, DateTime transDate, string transCategory, string transDescription, double transAmount, ref string helpText)
        {
            SqlCommand cmd = null;

            try
            {
                SqlConnection connection = new SqlConnection(@"Data Source=DSLAPTOP\SQLEXPRESS;Initial Catalog=MoneyEngine;Integrated Security=True");
                connection.Open();

                cmd = new SqlCommand("uspInsertTransaction", connection);
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add(GetParam("@TransType", transType, SqlDbType.Int, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDate", transDate, SqlDbType.DateTime, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransCategory", transCategory, SqlDbType.NVarChar, -1, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDescription", transDescription, SqlDbType.NVarChar, -1, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransAmount", transAmount, SqlDbType.Money, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransID", 0, SqlDbType.Int, ParameterDirection.Output));

                cmd.ExecuteNonQuery();

                return (int) cmd.Parameters["@TransID"].Value;
            }
            catch (SqlException sqlEx)
            {
                helpText = sqlEx.Message;
                return 0;
            }
            finally
            {
                if (cmd != null)
                {
                    cmd.Connection.Close();
                    cmd.Dispose();
                }
            }
        }

after much testing, I managed to get the parameters out fo the main function,  and its looking a bit smaller.  now we need an update routine now so first the test

 

        [Test]
        public void UpdateTransaction()
        {
            DBMoneyTransaction transaction = new DBMoneyTransaction();

            string helpText = string.Empty;

            Assert.IsTrue(transaction.UpdateTransaction(1, 0, System.DateTime.Now, "Test", "Test Entry", 0.0, 
ref helpText));
        }

and the routine

public bool UpdateTransaction(int transID, int transType, DateTime transDate, string transCategory, string transDescription, double transAmount, ref string helpText)
        {
            SqlCommand cmd = null;

            try
            {
                SqlConnection connection = new SqlConnection(@"Data Source=DSLAPTOP\SQLEXPRESS;Initial Catalog=MoneyEngine;Integrated Security=True");
                connection.Open();

                cmd = new SqlCommand("uspInsertTransaction", connection);
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add(GetParam("@TransID", transID, SqlDbType.Int, ParameterDirection.Output));
                cmd.Parameters.Add(GetParam("@TransType", transType, SqlDbType.Int, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDate", transDate, SqlDbType.DateTime, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransCategory", transCategory, SqlDbType.NVarChar, -1, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDescription", transDescription, SqlDbType.NVarChar, -1, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransAmount", transAmount, SqlDbType.Money, ParameterDirection.Input));
               
                cmd.ExecuteNonQuery();

                return true;
            }
            catch (SqlException sqlEx)
            {
                helpText = sqlEx.Message;
                return false;
            }
            finall
            {
                if (cmd != null)
                {
                    cmd.Connection.Close();
                    cmd.Dispose();
                }
            }
        }

so new we need some refractoring, as there is a lot of duplication in the code.  after a bit of refractoring here is the new functions

 

        public SqlCommand GetCommand(string procedureName, string connectionString)
        {
            SqlConnection connection = new SqlConnection(connectionString);
            connection.Open();

            SqlCommand cmd = new SqlCommand(procedureName, connection);
            cmd.CommandType = CommandType.StoredProcedure;

            return cmd;
        }

        public int InsertTransaction(int transType, DateTime transDate, 
string transCategory, string transDescription, double transAmount, ref string helpText)
        {
            SqlCommand cmd = null;

            try
            {
                cmd = GetCommand("uspInsertTransaction", STR_Connection);

                cmd.Parameters.Add(GetParam("@TransType", transType, SqlDbType.Int, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDate", transDate, SqlDbType.DateTime, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransCategory", transCategory, 
SqlDbType.NVarChar, -1, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDescription", transDescription, 
SqlDbType.NVarChar, -1, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransAmount", transAmount, 
SqlDbType.Money, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransID", 0, SqlDbType.Int, ParameterDirection.Output));

                cmd.ExecuteNonQuery();

                return (int) cmd.Parameters["@TransID"].Value;
            }
            catch (SqlException sqlEx)
            {
                helpText = sqlEx.Message;
                return 0;
            }
            finally
            {
                if (cmd != null)
                {
                    cmd.Connection.Close();
                    cmd.Dispose();
                }
            }
        }

        public bool UpdateTransaction(int transID, int transType, DateTime transDate, string transCategory, 
string transDescription, double transAmount, ref string helpText)
        {
            SqlCommand cmd = null;

            try
            {
                cmd = GetCommand("uspInsertTransaction", STR_Connection);

                cmd.Parameters.Add(GetParam("@TransID", transID, SqlDbType.Int, ParameterDirection.Output));
                cmd.Parameters.Add(GetParam("@TransType", transType, SqlDbType.Int, ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDate", transDate, SqlDbType.DateTime, 
ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransCategory", transCategory, SqlDbType.NVarChar, -1, 
ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransDescription", transDescription, SqlDbType.NVarChar, -1, 
ParameterDirection.Input));
                cmd.Parameters.Add(GetParam("@TransAmount", transAmount, SqlDbType.Money, 
ParameterDirection.Input));
                
                cmd.ExecuteNonQuery();

                return true;
            }
            catch (SqlException sqlEx)
            {
                helpText = sqlEx.Message;
                return false;
            }
            finally
            {
                if (cmd != null)
                {
                    cmd.Connection.Close();
                    cmd.Dispose();
                }
            }
        }

Posted in Projects | Leave a comment

Real Test Driven Development – Thinking of Money 6

The data access layer: 

We are going to be loading and saving transactions, so we are going to be testing adding and deleting transactions individually, each transaction should know how to add or delete itself, and also know what its state is.

my plan is that the engine class will have an update routine that simply walks through the collection of transactions and each updates and deletes as required.

So money transaction will need to be able to work out its state. we can do the basics add or update to the database by using a new property transactionID, to mark for delete we can use a new property toDelete.

So we add a couple of new properties, in order to set the transactionID value we create a new constructor with the transactionID value, this will allow us to create items from the database as well as creating new items, both with their own constructor.  to set the internal values I have created a procedure called from the constructors.  the unit tests we created earlier will continue to work well, and we simply need to add a single test for the ToDelete property to ensure its default is false.

        [Test]
        public void ToDelete()
        {
            Assert.IsFalse(_MoneyTrans.ToDelete);
        }

 the new constructors look like

        /// <summary>
        /// Initializes a new instance of the MoneyTransaction, this will need inserting into the data store.
        /// </summary>
        /// <param name="transactionAmount"></param>
        /// <param name="transactionCategory"></param>
        /// <param name="transactionDescription"></param>
        /// <param name="transactionDate"></param>
        /// <param name="transType"></param>
        public MoneyTransaction(double transactionAmount, string transactionCategory, string transactionDescription, DateTime transactionDate, TransactionType transType)
        {
            SetDefaultValues(0, transactionAmount, transactionCategory, transactionDescription, transactionDate, transType);
        }


        /// <summary>
        /// Initializes a new instance That contains data retrevied from a data store, that has an ID
        /// </summary>
        /// <param name="transactionID"></param>
        /// <param name="transactionAmount"></param>
        /// <param name="transactionCategory"></param>
        /// <param name="transactionDescription"></param>
        /// <param name="transactionDate"></param>
        /// <param name="transType"></param>
        public MoneyTransaction(int transactionID, double transactionAmount, string transactionCategory, string transactionDescription, DateTime transactionDate, TransactionType transType)
        {
            SetDefaultValues(transactionID, transactionAmount, transactionCategory, transactionDescription, transactionDate, transType);
        }

        private void SetDefaultValues(int transactionID, double transactionAmount, string transactionCategory, string transactionDescription, DateTime transactionDate, TransactionType transType)
        {
            _TransactionID = transactionID;
            _TransactionAmount = transactionAmount;
            _TransactionCategory = transactionCategory;
            _TransactionDescription = transactionDescription;
            _TransactionDate = transactionDate;
            _TransType = transType;
            _ToDelete = false;            
        }

 and the new properties look like

              private int _TransactionID;
              public int TransactionID
             {
                    get
                    {
                           return _TransactionID;
                     }
              }

        private bool _ToDelete;
        public bool ToDelete
        {
            get
            {
                return _ToDelete;
            }
            set
            {
                _ToDelete = value;
            }
        }

 

So we now have to write the data base access methods for this class,  called from a single update command, this will first test for add or update, then test for delete, (we won’t actually be deleting anything, we will simply mark the item as deleted this is so that we can get a full history and allow people to undo errors.

by doing the add/update first, every created transaction will be added to the database even if its deleted directly afterwards.

This change will require us to change the table and the stored procedures created earlier, don’t you just love it when program specs change!

think we shall leave all this for the next entry.

Posted in Projects | Leave a comment

Ruby in Visual Studio

 

Now what I know about ruby you can put on the back of a postage stamp, but I am a bit of a language hound, and like to learn new stuff.

I have been looking a ruby for a while, but it always seamed to be Linux based, and being a complete windows aholic, I just couldn’t tare myself away from the comfort of visual studio.

Then I came across this whilst surfing the web

Ruby in Steel

with this and a complete installation of ruby and ruby on rails I can start to play,

if you decide to go for it then you will also need ruby from here.

Ruby on Rails

and have fun.

Posted in Ruby | Leave a comment

Real Test Driven Development – Thinking of Money 5

 

We now have a library that supply’s all the required functionality to  add, subtract and display a balance, also display the history of each transaction.

we now need to decide how to save this data, I could simply serialize the collection and balance to an xml file, but I want to use this data in the future, on the next project where I can budget using known data from the past.

ok so it seams that a sql database would be in order. a simple table should do for the minute. along with the usual select, insert, update and delete stored procedures

 

USE [MoneyEngine]
GO
/****** Object:  Table [dbo].[tTransactions]    Script Date: 01/01/2007 20:28:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tTransactions](
    [TransID] [bigint] IDENTITY(1,1) NOT NULL,
    [TransType] [int] NOT NULL,
    [TransDate] [datetime] NOT NULL,
    [TransCategory] [nvarchar](max) NULL,
    [TransDescription] [nvarchar](max) NULL,
    [TransAmount] [money] NULL
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF

.csharpcode, .csharpcode pre {font-size:small;color:black;font-family:consolas, "Courier New", courier, monospace;background-color:#ffffff;} .csharpcode pre {margin:0em;} .csharpcode .rem {color:#008000;} .csharpcode .kwrd {color:#0000ff;} .csharpcode .str {color:#006080;} .csharpcode .op {color:#0000c0;} .csharpcode .preproc {color:#cc6633;} .csharpcode .asp {background-color:#ffff00;} .csharpcode .html {color:#800000;} .csharpcode .attr {color:#ff0000;} .csharpcode .alt {background-color:#f4f4f4;width:100%;margin:0em;} .csharpcode .lnum {color:#606060;}

 

USE [MoneyEngine]
GO
/****** Object:  StoredProcedure [dbo].[uspSelectTransactionsDateOrder]    Script Date: 01/01/2007 20:29:50 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:        Duncan
-- Create date: 1/1/2007
-- Description:    Select all transactions
-- =============================================
CREATE PROCEDURE [dbo].[uspSelectTransactionsDateOrder] 

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    SELECT TransID, TransType, TransDate, TransCategory, TransDescription, TransAmount

    FROM tTransactions

    ORDER BY TransDate
END

.csharpcode, .csharpcode pre {font-size:small;color:black;font-family:consolas, "Courier New", courier, monospace;background-color:#ffffff;} .csharpcode pre {margin:0em;} .csharpcode .rem {color:#008000;} .csharpcode .kwrd {color:#0000ff;} .csharpcode .str {color:#006080;} .csharpcode .op {color:#0000c0;} .csharpcode .preproc {color:#cc6633;} .csharpcode .asp {background-color:#ffff00;} .csharpcode .html {color:#800000;} .csharpcode .attr {color:#ff0000;} .csharpcode .alt {background-color:#f4f4f4;width:100%;margin:0em;} .csharpcode .lnum {color:#606060;}

 

USE [MoneyEngine]
GO
/****** Object:  StoredProcedure [dbo].[uspInsertTransaction]    Script Date: 01/01/2007 20:39:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:        Duncan
-- Create date: 1/1/2007
-- Description:    insert a new transaction
-- =============================================
CREATE PROCEDURE [dbo].[uspInsertTransaction] 
    -- Add the parameters for the stored procedure here
    @TransType            INT,
    @TransDate            DATETIME,
    @TransCategory        NVARCHAR(MAX),
    @TransDescription    NVARCHAR(MAX),
    @TransAmount        MONEY,
    @TransID            BIGINT OUTPUT

AS
BEGIN
    INSERT INTO tTransactions(TransType, TransDate, TransCategory, TransDescription, TransAmount)
    VALUES(@TransType, @TransDate, @TransCategory, @TransDescription, @TransAmount)

    SET @TransID = @@IDENTITY
END
USE [MoneyEngine]
GO
/****** Object:  StoredProcedure [dbo].[uspUpdateTransaction]    Script Date: 01/01/2007 20:41:03 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:        Duncan
-- Create date: 1/1/2007
-- Description:    Update a transaction
-- =============================================
CREATE PROCEDURE [dbo].[uspUpdateTransaction] 
    -- Add the parameters for the stored procedure here
    @TransID            BIGINT,
    @TransType            INT,
    @TransDate            DATETIME,
    @TransCategory        NVARCHAR(MAX),
    @TransDescription    NVARCHAR(MAX),
    @TransAmount        MONEY

AS
BEGIN

    UPDATE tTransactions

    SET TransType = @TransType,
        TransDate = @TransDate,
        TransCategory = @TransCategory,
        TransDescription = @TransDescription,
        TransAmount = @TransAmount

    WHERE TransID = @TransID
        
END
USE [MoneyEngine]
GO
/****** Object:  StoredProcedure [dbo].[uspDeleteTransaction]    Script Date: 01/01/2007 20:53:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:        Duncan
-- Create date: 1/1/2007
-- Description:    Delete a transaction
-- =============================================
CREATE PROCEDURE [dbo].[uspDeleteTransaction] 
    -- Add the parameters for the stored procedure here
    @TransID INT 

AS
BEGIN
    DELETE 

    FROM tTransactions
    
    WHERE TransID = @TransID
END
Of cause each of these stored procedures will need to be given permission to execute, and then its back to the 
code to write the data access layer.
 
Posted in Projects | Leave a comment

First post of 2007

 

Typical of me, I start ready to get down to some serious database stuff for the money engine, and did a quick check of the rss feeds to see what was new, and just had to take this test, its all Ron Jeffries fault.

 

Seams I am only a mid ranking nerd. oh well back to the database.

Posted in General Brain Dump | Leave a comment

Technorati

 
ok so I have joined the great world of spiders!! well I suppose it had to be done, you never know someone might actually read this rubbish 🙂
 
Posted in Uncategorized | Leave a comment

Real Test Driven Development – Thinking of Money 4

 

The history functions, I was going to have two collections one for additions and one for subtractions, but in reality I think we can get away with only one and allow the interface code to decide how to display the information.

So the add and subtract routines need to add the money transaction object to a collection. First the test

        [Test]
        public void CheckHistory()
        {
            _Engine.AddTransaction(10.10, "test category", "test description", DateTime.Now);

            List<MoneyTransaction> history = _Engine.History;

            Assert.AreEqual(1, history.Count);
        }

We now need to add a history property to the engine class

        private List<MoneyTransaction> _History = new List<MoneyTransaction>();
        public List<MoneyTransaction> History
        {
            get
            {
                return _History;
            }
        }

the following line is added to the add and subtract methods, after the new money transaction is created

_History.Add(trans);

This showed up a problem with the current code base, the money transaction class is currently declared private, which causes a build error with the property declared above, changing it to public solves this.

Posted in Projects | Leave a comment

Real Test Driven Development – Thinking of Money 3

 

We now have an object to store each transaction, this will also allow us to display each transaction, so next we have to do the add, and then subtract, these should also update the balance. 

Once we have done that we can think about accessing the history stuff.

right so my methods will want to create a money transaction and complete its details, then update the balance, my initial thoughts where that I would have an add, and then subtract function, in the engine class I have created already with that in mind I started writing an add transaction function, the test would be simply to call this with the correct parameters, and then I would check the balance to ensure it was updating correctly.

        public bool AddTransaction(double amount, string category, string description, DateTime transactionDate)
        {
            MoneyTransaction trans = new MoneyTransaction();

            trans.TransactionAmount = amount;
            trans.TransactionCategory = category;
            trans.TransactionDescription = description;
            trans.TransType = TransactionType.AdditionTransaction;
            trans.TransactionDate = transactionDate;

            _Balance += trans.TransactionAmount;
        }
and the subtraction method
        public bool SubtractTransaction(double amount, string category, string description, 
                                        DateTime transactionDate)
        {
            MoneyTransaction trans = new MoneyTransaction();

            trans.TransactionAmount = amount;
            trans.TransactionCategory = category;
            trans.TransactionDescription = description;
            trans.TransType = TransactionType.SubtractionTransaction;
            trans.TransactionDate = transactionDate;

            _Balance -= trans.TransactionAmount;
        }
now I need a couple of unit tests to ensure I am not going completely mad, this is one of those times that I have
done the coding before the tests, I couldn't decide if I wanted one or two methods, finally deciding on two so
any future enchantments could be added without overhead, keeping it simple sometimes a bit more code at the start.
ok the tests, added to the engine test class
        [Test]
        public void AddTransaction()
        {
            _Engine.AddTransaction(10.10, "test category", "test description", DateTime.Now);

            Assert.AreEqual(10.10, _Engine.Balance);
        }

        [Test]
        public void SubtractTransaction()
        {
            _Engine.SubtractTransaction(10, "test category", "test description", DateTime.Now);

            Assert.AreEqual(-10, _Engine.Balance);
        }

Well that failed on the build completely, teach me to program within live writer, after some changes the two
engine methods now look like.
      public bool AddTransaction(double amount, string category, string description, DateTime transactionDate)
        {
            MoneyTransaction trans = new MoneyTransaction(amount, category, description, transactionDate, 
                                                          TransactionType.AdditionTransaction);

            _Balance += trans.TransactionAmount;

            return true;
        }

        public bool SubtractTransaction(double amount, string category, string description, 
                                        DateTime transactionDate)
        {
            MoneyTransaction trans = new MoneyTransaction(amount, category, description, transactionDate, 
                                                          TransactionType.SubtractionTransaction);

            _Balance -= trans.TransactionAmount;

            return true;
        }

ok so now the project builds and the balance is updated as expected. apart from the small error above, where I got carried away creating the transaction object then filling it in one property at a time instead of using the constructor as the object required, would writing a test before had solved that, probably not, but I always feel safe as I won’t write more than a couple of functions like the above without tests, ensuring that I have a green before proceeding.

Also notice we have no save or display methods its all been putting stuff in and then getting stuff out, next we tackle the history problem, we want to store each transaction then get it back in order, by date?

Posted in Projects | Leave a comment