ruby on rails - How to validate that payment can never cause invoice amount payable to be less than zero? -
i have class:
class payment < activerecord::base attr_accessible :amount, :invoice_id belongs_to :invoice validates :amount, :numericality => { :greater_than => 0, :less_than_or_equal_to => :maximum_amount } after_save :update_amount_payable after_destroy :update_amount_payable private def maximum_amount invoice.amount_payable end def update_amount_payable invoice.update_amount_payable end end class invoice < activerecord::base has_many :payments after_save :update_amount_payable def update_amount_payable update_column(:amount_payable_in_cents, new_amount_payable) end private def new_amount_payable (total - payments.map(&:amount).sum) * 100 end end the code above works. how can validate no payment amount can ever cause invoice.amount_payable less 0?
especially when multiple payments same invoice possible, turns out tricky.
i've been trying head around hours, no avail. maybe after callback rollback database can used here?
thanks help.
one cross-database solution work use optimistic locking. essentially, requires special lock_version column, checked whenever update made. if lock_version @ time update called different model expecting, throws error noting outside of model caused record change (thus invalidating update). activerecord supports out of box, , suffice needs if don't mind blocking concurrent transactions altogether.
a case won't work want allow concurrent updates. in case, you'll need manually check result during update:
def update_amount_payable new_value = new_amount_payable raise "payment amounts can't greater total invoice amount" if new_value < 0 count = invoice.where(id: id, amount_payable_in_cents: amount_payable_in_cents). update_all(amount_payable_in_cents: new_value) raise activerecord::staleobjecterror.new(self, 'update amount_payable_in_cents') if count != 1 end private def new_amount_payable (total - payments.sum(:amount)) * 100 # amount sum database end
Comments
Post a Comment