Tintin81
Tintin81

Reputation: 10207

How to sum up all associated records from join table in Ruby on Rails?

I have the following models in my Ruby on Rails app :

class Invoice < ActiveRecord::Base

  has_many :allocations
  has_many :payments, :through   => :allocations

end

class Allocation < ActiveRecord::Base

  belongs_to :invoice
  belongs_to :payment

end

class Payment < ActiveRecord::Base

  has_many :allocations, :dependent => :destroy
  has_many :invoices,    :through => :allocations

end

My problem is that in the Allocation class I would like to use the total_amount of all associated invoices, ideally in a before_save callback.

This isn't possible right now, however, because at the time an allocation object gets saved it is only associated with one particular invoice object.

How can this be done with a minimum of database queries?

Upvotes: 1

Views: 186

Answers (1)

boulder_ruby
boulder_ruby

Reputation: 39695

Invoice.where(asdfasdfasdf).map(&:allocations).flatten.map(&:total_amount).compact.inject(:+)

Because this is rails the database call is nothing. To sum up an array of numbers you can use this:

ary = [0,12,2,6,nil]
ary.compact.inject(:+)
#=> 20

You could clean this up a bit:

class Invoice
#...
  def total_amount
    allocations.map(&:total_amount).inject(:+) #throws error if there is 1 nil 'total_amount data' value
  end 

  def self.sum_allocation_amounts(sql)
    where(sql).map(&:total_amount).inject(:+)
  end

end

Its not possible to call self.map(&:allocations) inside of an Invoice class method without errors so I'm passing in the some basic sql as a workaround. Ideally I'd make it possible to directly call this method on a daisy chain of activerecord where calls for Invoice but that's not working out for me right now ("undefined method map' for Class")

Invoice.sum_allocation_amounts("democol = 'demo params'")

Upvotes: 3

Related Questions