0

私はこのクラスを持っています:

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

上記のコードは機能します。invoice.amount_payableしかし、支払い額が未満になることは決してないことをどのように検証でき0ますか?

特に、同じ請求書に対して複数の支払いが可能な場合、これは注意が必要です。

私は何時間もこれを回避しようとしてきましたが、役に立ちませんでした。ここでデータベースをロールバックするためのafterコールバックを使用できますか?

助けてくれてありがとう。

4

2 に答える 2

1

有効なクロスデータベース ソリューションの 1 つは、楽観的ロックを使用することです。基本的にlock_version、更新が行われるたびにチェックされる特別な列が必要です。UPDATE が呼び出されたlock_version時点でモデルが予期しているものと異なる場合、このモデルの外部にある何かによってレコードが変更されたことを示すエラーがスローされます (したがって、更新が無効になります)。ActiveRecord はこれを標準でサポートしており、同時トランザクションを完全にブロックすることを気にしないのであれば、これで十分でしょう。

それが機能しないケースは、同時更新を許可したい場合です。この場合、更新中に結果を手動で確認する必要があります。

def update_amount_payable
  new_value = new_amount_payable
  raise "Payment amounts can't be greater than 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  # get the amount sum from the database
end
于 2013-05-13T19:14:17.163 に答える