如何通过关系计算has_many中的before_save总数

时间:2014-11-15 00:58:31

标签: ruby-on-rails activerecord ruby-on-rails-4

我从早上起就一直在努力解决这个问题,现在是我国的凌晨3点。

我只有一个比尔模型:

class Bill < ActiveRecord::Base

  has_many :bill_lines, inverse_of: :bill
  validates_associated :bill_lines

  accepts_nested_attributes_for :bill_lines, allow_destroy: true

  after_validation :calculate_totals

  def calculate_totals
    self.total = self.bill_lines.sum(:total)
    self.tax_total = self.bill_lines.sum(:tax_total)
    self.grand_total = self.bill_lines.sum(:grand_total)
  end

end

还有:

class BillLine < ActiveRecord::Base

  belongs_to :bill, inverse_of: :bill_lines
  has_many :bill_line_taxes
  has_many :taxes, through: :bill_line_taxes
  validates_associated :bill_line_taxes

  validates :bill, presence: true
  validates :quantity, presence: true, numericality: true
  validates :price, presence: true, numericality: true

  accepts_nested_attributes_for :bill_line_taxes, allow_destroy: true 

  after_validation :calculate_totals

  def calculate_totals
    self.total = self.price * self.quantity
    self.grand_total = self.total
    self.taxes.each do |tax|
      rate = Tax.find(tax.id).tax_rate
      self.grand_total = self.grand_total + ( rate / 100 ) * self.grand_total
    end
    self.tax_total = self.grand_total - self.total
    true
  end

end

我得到了结果:

bill = FactoryGirl.create(:bill)
bill.total # 50.0 as expected
bill.tax_total # 0.0
bill.grand_total # 50.0

我尝试了self.taxes.each和self.bill_line_taxes.each以及许多回调,如after_save,before_validation等。我无法在回调中达到税ids。但是关联正确地保存到数据库中。如果我验证代码bill.bill_lines.first.valid?比bill.save,税额被添加到数据库(我想这是因为这次关联已经在数据库中了。)

我不确定这是否是我尝试的最佳做法,所以我愿意接受任何改变我的方法的建议。但无论如何要在after_validation回调中获得相关的税ids,我们非常感激。

1 个答案:

答案 0 :(得分:1)

您无法在回调中获取ID,因为它们尚未保留在您的数据库中,并且您想要加入,因为没有记录,它将永远无法工作。 after_save 回调会为记录保留一个ID,但除非after_commit您的新数据,否则无法获得该ID。只有持久化的记录才有ID。


你有两个案例

  1. 继续您的设计,BillLine和税收是多方协会。
  2. 将您的关联更改为一对多&#34; BillLine has_many税,每项税收仅属于一个billLine&#34;

  3. 解决方案

    • 案例1:

      • 在新操作中构建bill_line_tax并从中构建税
      • 将accept_nested_attributes添加到BillLineTax模型。
      • 在您的创建操作中,只需BillLine.new(params)然后保存。
      • 您的表单必须嵌套。

    f.fields_for :bill_line_items do |s|
      s.fileds_for :taxes do ....
    

    这将帮助您构建整个记录而不保存它们,因此bill_line_tax将使用它构建一个税务记录,将您的循环更改为

    self.bill_line_taxs.each do |bill_line_tax|
      rate = bill_line_tax.tax.tax_rate
      self.grand_total = self.grand_total + ( rate / 100 ) * self.grand_total
    end
    
    • 案例2: 如果它是一对多,就像我说你的代码将会工作找到。

      class BillLine < ActiveRecord::Base
      
        belongs_to :bill, inverse_of: :bill_lines
        has_many :taxes
        validates_associated :taxes
      
        validates :bill, presence: true
        validates :quantity, presence: true, numericality: true
        validates :price, presence: true, numericality: true
      
        accepts_nested_attributes_for :taxes, allow_destroy: true 
      
        after_validation :calculate_totals
      
        def calculate_totals
          self.total = self.price * self.quantity
          self.grand_total = self.total
          self.taxes.each do |tax|
            rate = tax.tax_rate
            self.grand_total = self.grand_total + ( rate / 100 ) * self.grand_total
          end
          self.tax_total = self.grand_total - self.total
          true
        end
      
      end
      

    在您的表单中

    f.fields_for :taxes do |s|
      // taxes attributes
    

    f表示您的案例中的BillLine的父对象或您处理表单的任何内容。但请确保它与我所说明的完全嵌套,以便在回调中自动获取构建的对象。