Topic: AR validation

What is the best way to implement a validation for a withdrawal operation? So there is an Operation and Client models.
Client has_many :operations. In case of withdrawal operation, I'd like to check if the requested sum is not greater than a Client account balance. How to link the validation in the Operation model to a specific client ? I tried scope, before save, etc, nothing works as needed.

class Client < AR
  has_many :operations
end

class Operation < AR
  belongs_to :client
  attr_accessible :sum
end

In fact, the validation should be done only in case of withdrawal operation type defined as a constant value in the model Operation.
The routes are defined so that an operation could only created in the context of a Client (as nested route).

Thanks

Last edited by Javix (2012-10-12 17:20:14)

Re: AR validation

Not tested yet, may be like that:

class Operation < ActiveRecord::Base
  belongs_to :client
  TRANSACTIONS = %w{deposit withdrawal}
  scope :client_operations, lambda { |client| where (client_id: client)}

    before_save :check_withdrawal_capacity, if: Proc.new {|op| op.operation_type == TRANSACTIONS[1]}

  private

  def check_withdrawal_capacity
      errors.add(:sum, "You don't have enough money on your account") if sum > self.client_operations(client: client).reduce(:+)
    end


end

Last edited by Javix (2012-10-12 17:32:34)

Re: AR validation

Not sure I follow exactly what your issue is but to ensure that a check is done as I don;t know what your withdrawal process is but whatever it is it needs to be a specific method in your operation model so I would

1) Add a withdrawal method that returns a boolean failed result to your operation model that ontains the business logic you need
2) Call that method directly or in a AR callback. If the method returns false then the call back will fail and the relevant action (save, update delete or whatever) would roll back.

it might be worth considering a trigger on your database table.

I suspect I might have missed the mark in understanding your issue as I'm sure you have already thought of the above.

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: AR validation

Sorry, cross posted!

Your second post pretty much covers exactly what I was meaning in my last reply

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: AR validation

Finally the correct version would like that, I think:

class Operation < ActiveRecord::Base
  belongs_to :client
  before_validation :calculate_interests_and_total
...
  private
    def calculate_interests_and_total
   #some stuff and check co;e here
      check_balance
...
    end

  end

def check_balance
    errors.add(:sum, "#{sum} exceeds the actual balance") if client.operations.empty? || sum > client.operations.map(&:total).inject(:+)
  end

There was an error in the previous post code in inject call, - I had to apply the sum (+) on an attribut (total in this cas and not on the Operation object itself).
Thanks and cheers