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