Reputation: 387
I am having some troubles today and cannot think outside the box to fix this one.
Basically I have a model called Airplane, which has_many Payments. Each payment can be divided in many Installments. Ok!
Here is the info:
Model Airplane
- has_many payments
- before_save :checks_if_everything_has_been_paid
Model Payment
- belongs_to airplane
- has_many installments
Model Installment
- belongs_to payment
So, what I want to do is when the sum of the installments be equal or greater than the Airplane ticket value then the Airplane.paid will be true. I am doing that using the before_save "checks_if_everything_has_been_paid. But it only works when there are changes on the Airplane fields.
How can I run this class when there are changes both in the Payment and Installment fields?
I want to check if the payment is completed everytime an installment is changed or the Payment itself.
Thank you!
Upvotes: 3
Views: 2319
Reputation: 24337
Instead of defining an after save
callback on the Airplane model, define a after_add
callback on the payments
association.
class Airplane < ActiveRecord::Base
has_many :payments, after_add: :checks_if_everything_has_been_paid
def checks_if_everything_has_been_paid
# work some magic
end
end
Update: I think the following may be a better approach if I understand your data model correctly. If a payment or installment is saved it will trigger the airplane to check for full payment:
class Airplane < ActiveRecord::Base
has_many :payments
has_many :installments, through: :payments
def check_for_full_payment
# work some magic
end
end
class Payment < ActiveRecord::Base
belongs_to :airplane
has_many :installments
after_save :trigger_full_payment_check
def trigger_payments_check
airplane.check_for_full_payment
end
end
class Installment < ActiveRecord::Base
belongs_to :payment
delegate :airplane, to: :payment
after_save :trigger_full_payment_check
def trigger_payments_check
airplane.check_for_full_payment
end
end
The nice thing about this approach is that the logic in Payment and Installment is identical, so you can extract it to a module:
module TriggerFullPaymentCheck
def self.included(base)
base.after_save :trigger_full_payment_check
end
def trigger_payments_check
airplane.check_for_full_payment
end
end
class Airplane < ActiveRecord::Base
has_many :payments
has_many :installments, through: :payments
def check_for_full_payment
# work some magic
end
end
class Payment < ActiveRecord::Base
include TriggerFullPaymentCheck
belongs_to :airplane
has_many :installments
end
class Installment < ActiveRecord::Base
include TriggerFullPaymentCheck
belongs_to :payment
delegate :airplane, to: :payment
end
Upvotes: 2
Reputation: 4296
You can try setting :autosave
on your belongs_to
associations. If that works, it's a clever solution :).
A better option is probably to create an after_save
hook that ask Airplane to check itself.
class Airplane
has_many :payments
def check_for_paid
# use payments(true) to force the association to reload
if payments(true).all?(&:paid?)
paid = true
save
end
end
end
class Payment
# associations
def paid?
installments(true).all?(&:paid?)
end
end
class Installment
after_save :check_for_paid
def check_for_paid
payment.airplane.check_for_paid
end
end
This way it's more explicit what's happening and why. The other would definitely be more clever, though.
Upvotes: 0