Question

I am new to rails and am working on one of my first rails projects, it is an invoice app with nested line items within the invoice form. I want to calculate the total invoice before I save the invoice. I get it to save nicely if just added items during the save process, but it doesn't calulate the total correctly if one of the nested line items is tagged to be deleted. I would have to go back in and save again to get the correct total billed amount.

class Invoice < ActiveRecord::Base
  attr_accessible :job_name, :items_attributes, :tax1, :tax2, :subtotal

  before_save :calculate_totals


  has_many :items, :dependent => :destroy
  accepts_nested_attributes_for :items, allow_destroy: true

  private

  def calculate_totals
    self.subtotal = 0

    self.items.each do |i|
      self.subtotal = self.subtotal + (i.quantity * i.cost_per)
    end
  end
end

I'm note sure how this differs from params but the problem item record is listed in the requested paramaters with :_destroy = true

{"utf8"=>"✓",
 "_method"=>"put",
 "authenticity_token"=>"+OqRa7vRa1CKPMCdBrjhvU6jzMH1zQ=",
 "invoice"=>{"client_id"=>"1",
 "job_name"=>"dsdsadsad",
 "items_attributes"=>{"0"=>{"name"=>"jhksadhshdkjhkjdh",
 "quantity"=>"1",
 "cost_per"=>"50.0",
 "id"=>"21",
 "_destroy"=>"false"},
 "1"=>{"name"=>"delete this one",
 "quantity"=>"1",
 "cost_per"=>"10.0",
 "id"=>"24",
 "_destroy"=>"true"}}},
 "commit"=>"Update Invoice",
 "id"=>"8"}

Thanks for you help.

Was it helpful?

Solution

I found a solution that seems to work:

class Invoice < ActiveRecord::Base
  attr_accessible :job_name, :items_attributes, :tax1, :tax2, :subtotal

  before_save :calculate_totals


  has_many :items, :dependent => :destroy
  accepts_nested_attributes_for :items, allow_destroy: true

  private
    def calculate_totals
      self.subtotal = 0

      self.items.each do |i|
        unless i.marked_for_destruction?
          self.subtotal += (i.quantity * i.cost_per)
        end
      end

end

The key is the method marked_for_destruction? In this case I was checking for items that weren't marked for destroy. Here's the link to the rails api that explains it: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Thanks Steve

OTHER TIPS

params[:invoice][:items_attributes].each do |i|
  self.subtotal = self.subtotal + (i[:quantity].to_f * i[:cost_per].to_f) unless i['_destroy'] == 'true'
end

You have calculated items before Invoice was saved, so it calculated items destroyed too, as in API document has guide:

Note that the model will not be destroyed until the parent is saved.

So you just need change before_save to after_save, it will works.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top