Is this a good solution?

Is this a good solution?

Old forum URL: forums.lhotka.net/forums/t/2958.aspx


Patrick.Roeper posted on Wednesday, May 30, 2007

I am still getting my feet wet with CSLA so I wanted to get some feedback on the way I'm working with the framework. One portion of the program I develop has an accounting package, so I was trying to model the General Ledger component in CSLA. I personally never had to learn accounting since I was a computer science major; so for my fellow CS people, here's a few short notes about the general ledger...

The General Ledger is a sheet of all monies flowing through a company. A GL transaction typically will record a transaction date, the cause of the transaction, accounts involved in the transaction, and money being debited and credited to the accounts.

 

So I have a GL Transaction B.O. with general information (Transaction Date, Reason of Transaction, ...)

I would also have a GLTransaction Item B.O. that has the specific details of the transaction (Account where money is flowing from/to, Debit Amount, Credit Amount, ...)

 

I created a GLTransactions object (EditableRootList) to get a list of general ledger transactions. The list is comprised of children GLTransaction objects (editable switchable - this way I can load a GLTransaction object independently of a list). Each GLTransaction has a collection of GLTransactionItems (EditableChildList) which holds a bunch of children GLTransactionItem objects (EditableChild).

GLTransactions -> GLTransaction -> GLTransactionItems -> GLTransactionItem

I added some basic validation rules to the objects... 1 being a rule on GLTransaction which verifies that the Total Debit amount of the GLTransactionItems child always equals the Total Credit amount of the GLTransactionItems child. Everything up to this point is pretty good.

 

One requirement of an accounting software is to be able to reverse a transaction, meaning to flip the debits and credits around (this basically makes the original transaction equal out to $0.00 without having to delete or modify the original transaction). I still am not sure of the best way to model this, but I have a solution that works so any feedback and advice would really be appreciated.

I know that I dont want to create a new instance of the GLTransaction business object and manually fill out the information so that it becomes a reversing entry transaction. So what I did was...

1. I fetched the transaction that I want to reverse (since its a switchable object this was easily done through a factory method).

2. I then marked the object as 'New' so when the object was saved, it would be inserted instead of updated.

3. Since I also had to mark each child object as new, I had to expose the 'MarkNew' BusinessBase method; I did this by making a public method called 'MarkAsNew' on the GLTransactionItem business object which just invokes the MarkNew method.

4. I wrapped all of this (along with some other business logic) into a method on GLTransaction called ReverseTransaction.

 

So now in order to reverse a transaction, this is the code...

GLTransaction myTransaction = GLTransaction.GetGLTransaction(some_primary_key);

myTransaction.ReverseTransaction(DateTime.Now);

myTransaction.Save();

 

My reversing transaction code is...

public void ReverseTransaction(DateTime TransactionDate)

{

MarkNew();

FldOperation = "REVERSING ENTRY: " + FldOperation;

FldTransactionDateString = TransactionDate.ToString();

foreach (GLTransactionItem glItem in glTransactionItems)

{

decimal DebitAmount = 0;

decimal CreditAmount = 0;

glItem.MarkAsNew();

DebitAmount = glItem.FldDebit;

CreditAmount = glItem.FldCredit;

// Swap values

glItem.FldDebit = CreditAmount;

glItem.FldCredit = DebitAmount;

}

}

 

I know this is pretty elementary, but after watch Rocky's show on dnrTV about the data portal I always wonder if I am 'assigning jobs' to the business objects correctly. What do you think? Would you have modeled this differently?

Thanks in advance!

RockfordLhotka replied on Wednesday, May 30, 2007

You are seriously building a GL package? Wow! I didn't think anyone did that anymore given how cheap they are to purchase... That's kind of cool, it is such a well-understood problem domain that it is a relatively fun development exercise to write that sort of thing.

To your question: there's a very clear difference in use cases here - between the add-a-transaction and reverse-a-transaction use case. You should start, in your design process, by assuming the objects are not the same. They should only be the same if they have the same responsibilities in both use cases, and if you need no conditional logic in the class for the object to behave properly in both use cases.

I can't tell, from what you've provided, whether you have any conditional logic to change the object's behaviors when doing a reverse, but if you do then you need a different object. If you don't, then you may be OK. If the only reversal logic is in the method you show, then I think you are probably OK.

In any case, however, when you loop through the child items, each child item should also have a ReverseTransaction() method, and all that code you have in the parent should be in each child. Each object should be responsible for managing its own state, and it is always best to avoid having one object tamper with another object's properties in such a detailed and intimate manner.

Patrick.Roeper replied on Wednesday, May 30, 2007

A reversing entry has no special behavior, so really what I was trying to build here was a way to quickly fetch a GL Transaction object and re-insert into the database with all of it's items reversed. So I need to model the children in a way so that can modify themselves to become a part of a reversing entry... got it!

I dont know how you think this could be a cool project, its 5% C# and 95% reporting!! Crying [:'(]

Thanks for the help.. it didnt seem right to expose the MarkNew method of the child business object from the parent but now I see a better way.

RockfordLhotka replied on Wednesday, May 30, 2007

Patrick.Roeper:

I dont know how you think this could be a cool project, its 5% C# and 95% reporting!! Crying [:'(]

I'm afraid I was being a bit sarcastic, sorry Tongue Tied [:S]

Patrick.Roeper replied on Wednesday, May 30, 2007

I just like to complain about reporting and sql server error handling as much as possible Big Smile [:D]

Going back to a comment you made earlier about examining use cases and generating objects based off of unique behavior:

The next step of the project is to implement an inventory management system. So I will be using the cost of good sold accountings along with all the fun accounting stuff I just made and implementing it into the inventory system. So I am making my use cases and my schema so far (on a very minimal level) is:

 

There are many types of inventory items... [Stock Items, Non-Stock Items, Services, Raw Materials, ...]. Each has very different functionality on the accounting end.

A stock item is tracked as an asset to the company, and all associated costs of the item are a part of the companies Cost of Goods Sold. So along with tracking the revenue for the item, I must track its worth to the company as well.

A non-stock item is something that you just track revenue for. It doesnt hold a value and you dont track its cost to the company (like a box of pens or something).

 

So following your explanation earlier, I should model this as 2 seperate business objects instead of just having 1 business object with a 'Type' property that controls logic through switch statements or some other coding.

My immediate thought to this is how will I handle this in the UI? I will eventually have a Sales Order system that the user should pull down a list of all inventory items, select the inventory items from a combo box of some sort, and then a new line is added to the order. If I have one generic business object, I can use databinding on the combo box on a list of inventory objects. If I have seperate business objects, how do I "union" them into 1 control so the end user still has a friendly interface?

Wal972 replied on Wednesday, May 30, 2007

Hi,

I would suggest using inheritance of a generic inventory item or an interface with the common fields that a UI can work with.

And by the way, creating accounting packages can be fun (seriously) it just depends on your interests, mine is both accounting and programming.

Hope that helps

Ellie

RockfordLhotka replied on Friday, June 01, 2007

Patrick.Roeper:

The next step of the project is to implement an inventory management system. So I will be using the cost of good sold accountings along with all the fun accounting stuff I just made and implementing it into the inventory system. So I am making my use cases and my schema so far (on a very minimal level) is:

Remember to keep your use cases at a managable level of granularity. In other words, your application is not a use case Smile [:)]  So you likely have use cases like add-a-product and define-a-location and recieve-material and consume-material. It is quite likely that each of these use cases will have their own objects, with only a few objects shared across them.

Patrick.Roeper:

There are many types of inventory items... [Stock Items, Non-Stock Items, Services, Raw Materials, ...]. Each has very different functionality on the accounting end.

A stock item is tracked as an asset to the company, and all associated costs of the item are a part of the companies Cost of Goods Sold. So along with tracking the revenue for the item, I must track its worth to the company as well.

A non-stock item is something that you just track revenue for. It doesnt hold a value and you dont track its cost to the company (like a box of pens or something).

I'm sure that's true. But at the same time, I'm sure that not all your use cases actually impact accounting. Recieving and relieving inventory will likely post to a ledger. But defining a product or a location are unlikely to have an impact. Moving product from one location to another location is unlikely to affect accounting.

Which is why it is so important to design your objects at the use case level - otherwise they tend to have functionality in them that causes too much complexity.

Remember, objects don't track things long term. Databases do that. Objects are actors - they perform actions to accomplish the goals required by a use case.

So while a stock item is tracked as an asset - that's a database function. Your objects only care about that fact if the use case cares about that fact. Many of your use cases won't care that the product is an asset, as such. At most they'll have a field of data to that effect, but in reality the move-product-to-new-location use case almost certainly will not care or impact anything about an IsCompanyAsset column in the database.

On the other hand, the add-a-product use case does care - at least insofar as that the object needs the user to indicate whether the product in question is stock or non-stock.

Patrick.Roeper:

My immediate thought to this is how will I handle this in the UI? I will eventually have a Sales Order system that the user should pull down a list of all inventory items, select the inventory items from a combo box of some sort, and then a new line is added to the order. If I have one generic business object, I can use databinding on the combo box on a list of inventory objects. If I have seperate business objects, how do I "union" them into 1 control so the end user still has a friendly interface?

Sure, but this most certainly would not be your ProductEdit or ProductMove or ProductRecieve object. This would be a read-only name/value object designed specifically to support the (probably numerous) use cases where a lookup list of products is required.

You'll have similar NV lists for inventory locations, product types, reasons for adjustment and lots of other things. NV lists are so common that I actually created the NameValueListBase class in CSLA to help build them (even though it is really just a specialized case of the ReadOnlyListBase class).

Patrick.Roeper replied on Saturday, June 02, 2007

This is probably going to hurt your stomache reading this, but I've been modeling all of my business objects from an entity perspective, and all of my use cases as methods of the entities. So instead of having ProductEdit or ProductMove or ProductRecieve object, I have a Product object that can be .Moved(), .Received(), and .Edited()

Back to the white board Geeked [8-|]

Dawn replied on Friday, June 01, 2007

Patrick.Roeper:

I just like to complain about reporting and sql server error handling as much as possible Big Smile [:D]

Going back to a comment you made earlier about examining use cases and generating objects based off of unique behavior:

The next step of the project is to implement an inventory management system. So I will be using the cost of good sold accountings along with all the fun accounting stuff I just made and implementing it into the inventory system. So I am making my use cases and my schema so far (on a very minimal level) is:

 

There are many types of inventory items... [Stock Items, Non-Stock Items, Services, Raw Materials, ...]. Each has very different functionality on the accounting end.

A stock item is tracked as an asset to the company, and all associated costs of the item are a part of the companies Cost of Goods Sold. So along with tracking the revenue for the item, I must track its worth to the company as well.

A non-stock item is something that you just track revenue for. It doesnt hold a value and you dont track its cost to the company (like a box of pens or something).

 

So following your explanation earlier, I should model this as 2 seperate business objects instead of just having 1 business object with a 'Type' property that controls logic through switch statements or some other coding.

My immediate thought to this is how will I handle this in the UI? I will eventually have a Sales Order system that the user should pull down a list of all inventory items, select the inventory items from a combo box of some sort, and then a new line is added to the order. If I have one generic business object, I can use databinding on the combo box on a list of inventory objects. If I have seperate business objects, how do I "union" them into 1 control so the end user still has a friendly interface?

I have little experience similar to this , I think every item you should define a property which tell it's account functionality, say cost item,  revenue item or other item.  When you make a order, you accually don't care which is the item accout functionality at this time.  When you do account, you can easily distinguish them by the item's property you defined.

Hope i make my word clearly.

DansDreams replied on Thursday, May 31, 2007

RockfordLhotka:
You are seriously building a GL package? Wow! I didn't think anyone did that anymore given how cheap they are to purchase... That's kind of cool, it is such a well-understood problem domain that it is a relatively fun development exercise to write that sort of thing.

It appears I may end up going down the road of writing a 90% custom solution here, since I don't think any existing product will meet more than 20% or so of our niche special requirements.  Does anybody know of specific examples of a basic accounting .NET-based library one can purchase (or open-source if controlled well enough I guess)?  I'm talking about GL of course, but also perhaps basic inventory, AP and AR functionality too. 

RockfordLhotka:
To your question: there's a very clear difference in use cases here - between the add-a-transaction and reverse-a-transaction use case. ... They should only be the same if they have the same responsibilities in both use cases, and if you need no conditional logic in the class for the object to behave properly in both use cases.

Just because I like to play devil's advocate...

Wouldn't a position that emphatic mandate that there should be a separate business object for adding a new record vs. editing an existing one.  Or for adding vs. deleting, which are drastically different activities.

It seems to me that it's the "there's a very clear difference in use cases here" part that's significant, even though the "if you need no conditional logic in the class for the object to behave properly in both use cases" seems more like a "rule" and might therefore be taken as dogma.

Q Johnson replied on Thursday, May 31, 2007

I like doing accounting-related stuff, too.  And I have quite a lot of experience at it (and am a CPA).  So let me offer some quick advice here that isn't related to your CSLA question. 

You are not building an accounting system or a General Ledger.  If you use those phrases to describe what you are building, you risk elevating the customer's expectations well beyond what is likely the intended scope of your project (especially if they know more about accounting than you have learned so far).  Perhaps calling it a Transaction and Inventory Tracker would be better.  But the key thing is to not build in the expectation that this will have the functionality of a full-blown accounting system. 

If you do, you will find yourself with an unbelievably large task on your hands and you'll realize why most folks purchase that stuff "off the shelf" rather than commission someone to write one.  You can find really respectable capabilities in programs like Peachtree and QuickBooks for small-ish business even when they have fairly complicated inventory and sales models.  They cost well under $1,000, even for their "Pro" versions.  But if you charge a living wage to reproduce a fraction of the functionality those programs offer, the cost to the user will be in the tens of thousands of dollars.  At that price the client could be buying MAS90 or MAS200 or GreatPlains or a number of other programs whose capabilities scale almost as far above "the Peachtrees" as Peachtree does above yours.

One of the user's big advantages to using these other offerings is that they aren't a "lone ranger."  They have the company of literally tens of thousands of other users and their vendors have huge investments in providing several levels and types of customer support and volumes of documentation.  It's awfully hard for a small programming shop to compete with those, so we don't.

I'm not suggesting that you can't build something useful for your client at a price that is in line with its value to them.  Quite the contrary.  And you're working in a problem domain for which you can get plenty of assistance here because many of us work with it.  But take a step back and look at where this will likely be headed and make sure you and your client will both be happy several months from now.  If you think the project might actually evolve into a much larger (and costlier) affair, it might be best now to shift the focus to using one of the available "packages."  Many of them have an API with which you can bake in your own customizations for your customer's needs that aren't serviced properly in the "basic" product.

However you proceed, I wish you well.

  

 

Patrick.Roeper replied on Thursday, May 31, 2007

Q Johnson:

I like doing accounting-related stuff, too.  And I have quite a lot of experience at it (and am a CPA).  So let me offer some quick advice here that isn't related to your CSLA question. 

You are not building an accounting system or a General Ledger.  If you use those phrases to describe what you are building, you risk elevating the customer's expectations well beyond what is likely the intended scope of your project (especially if they know more about accounting than you have learned so far).  Perhaps calling it a Transaction and Inventory Tracker would be better.  But the key thing is to not build in the expectation that this will have the functionality of a full-blown accounting system. 

If you do, you will find yourself with an unbelievably large task on your hands and you'll realize why most folks purchase that stuff "off the shelf" rather than commission someone to write one.  You can find really respectable capabilities in programs like Peachtree and QuickBooks for small-ish business even when they have fairly complicated inventory and sales models.  They cost well under $1,000, even for their "Pro" versions.  But if you charge a living wage to reproduce a fraction of the functionality those programs offer, the cost to the user will be in the tens of thousands of dollars.  At that price the client could be buying MAS90 or MAS200 or GreatPlains or a number of other programs whose capabilities scale almost as far above "the Peachtrees" as Peachtree does above yours.

One of the user's big advantages to using these other offerings is that they aren't a "lone ranger."  They have the company of literally tens of thousands of other users and their vendors have huge investments in providing several levels and types of customer support and volumes of documentation.  It's awfully hard for a small programming shop to compete with those, so we don't.

I'm not suggesting that you can't build something useful for your client at a price that is in line with its value to them.  Quite the contrary.  And you're working in a problem domain for which you can get plenty of assistance here because many of us work with it.  But take a step back and look at where this will likely be headed and make sure you and your client will both be happy several months from now.  If you think the project might actually evolve into a much larger (and costlier) affair, it might be best now to shift the focus to using one of the available "packages."  Many of them have an API with which you can bake in your own customizations for your customer's needs that aren't serviced properly in the "basic" product.

However you proceed, I wish you well.

  

 

 

Funny you say this, because my company takes people off peach tree, quick books, MAS... we are a direct competitor with MS Dynamics... Thanks for the help though, I appreciate it.

Copyright (c) Marimer LLC