I read everywhere that business logic belongs in the models and not in controller but where is the limit? I am toying with a personnal accounting application.
Account
Entry
Operation
When creating an operation it is only valid if the corresponding entries are created and linked to accounts so that the operation is balanced for exemple buy a 6-pack :
o=Operation.new({:description=>"b33r", :user=>current_user, :date=>"2008/09/15"})
o.entries.build({:account_id=>1, :amount=>15})
o.valid? #=>false
o.entries.build({:account_id=>2, :amount=>-15})
o.valid? #=>true
Now the form shown to the user in the case of basic operations is simplified to hide away the entries details, the accounts are selected among 5 default by the kind of operation requested by the user (intialise account -> equity to accout, spend assets->expenses, earn revenues->assets, borrow liabilities->assets, pay debt assets->liabilities ...) I want the entries created from default values.
I also want to be able to create more complex operations (more than 2 entries). For this second use case I will have a different form where the additional complexity is exposed.This second use case prevents me from including a debit and credit field on the Operation and getting rid of the Entry link.
Which is the best form ? Using the above code in a SimpleOperationController as I do for the moment, or defining a new method on the Operation class so I can call Operation.new_simple_operation(params[:operation])
Isn't it breaking the separation of concerns to actually create and manipulate Entry objects from the Operation class ?
I am not looking for advice on my twisted accounting principles :)
edit -- It seems I didn't express myself too clearly. I am not so concerned about the validation. I am more concerned about where the creation logic code should go :
assuming the operation on the controller is called spend, when using spend, the params hash would contain : amount, date, description. Debit and credit accounts would be derived from the action which is called, but then I have to create all the objects. Would it be better to have
#error and transaction handling is left out for the sake of clarity
def spend
amount=params[:operation].delete(:amount)#remove non existent Operation attribute
op=Operation.new(params[:operation])
#select accounts in some way
...
#build entries
op.entries.build(...)
op.entries.build(...)
op.save
end
or to create a method on Operation that would make the above look like
def spend
op=Operation.new_simple_operation(params)
op.save
end
this definitely give a much thinner controller and a fatter model, but then the model will create and store instances of other models which is where my problem is.