根据这个问题,我花了一天的时间试图将累计的运行销售总额添加到我的销售表中。这有点棘手(对我来说),因为我想要一个isbn_id相同的销售总额,并且在该集合中,记录channel_id相同——按invoice_date排序。这些都是为了让我能够计算出某一特定销售单位的特许权使用费。
这是我的非工作回调代码,在销售模型中:
before_save :runningtotal
private
def runningtotal
@sale = Sale.order("invoice_date ASC")
@lastbal = @sale.find_all_by_isbn_id(@isbn).group_by(&:channel)
#that sucessfully gets all sales ranked by date ascending, then groups them by channel, just for the current isbn.
@lastbal.each do |channel, sale|
sale.each_with_index do |sale, i|
previous_sale = sale[i-1] unless i==0
next unless previous_sale
@total_quantity = previous_sale.quantity + :quantity
write_attribute(:total_quantity,@total_quantity)
end
end
end
回调应该是这样写的吗?只是在模型中?它只是在新的销售之前神奇地运行吗?
我的核心问题是:在before_save回调中,在isbn_id和channel_id的查找约束下,如何将属性"total_quantity"更新为当前记录的"quantity(数量)"和上一条记录的"total_ quantity"(按日期)"之和?
以下是查找的输出:
ruby-1.9.2-p180 :025 > @lastbal = @sale.find_all_by_isbn_id(@isbn).group_by(&:channel)
=> {#<Channel id: 4, isbn_id: nil, channel_name: "Gratis", created_at: "2011-05-26 11:08:22", updated_at: "2011-05-26 11:08:22">=>[#<Sale id: 26, isbn_id: 2, quantity: 10000, value: 40000, currency: "", total_quantity: nil, created_at: "2011-05-26 11:11:30", updated_at: "2011-05-26 11:11:30", customer: "6", retail_price: nil, discount: nil, channel_id: 4, invoice_date: "2011-05-18", rule_id: nil, trenche: nil>], #<Channel id: 1, isbn_id: nil, channel_name: "Home", created_at: "2011-05-16 19:47:27", updated_at: "2011-05-16 19:47:27">=>[#<Sale id: 22, isbn_id: 2, quantity: 1000, value: 5193, currency: "", total_quantity: nil, created_at: "2011-05-25 19:46:03", updated_at: "2011-05-25 19:46:03", customer: "a", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2011-05-11", rule_id: nil, trenche: nil>, #<Sale id: 24, isbn_id: 2, quantity: 1000, value: 4394, currency: "", total_quantity: nil, created_at: "2011-05-26 09:48:16", updated_at: "2011-05-26 09:48:16", customer: "g", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2011-05-10", rule_id: nil, trenche: nil>, #<Sale id: 25, isbn_id: 2, quantity: 1000, value: 4394, currency: "", total_quantity: nil, created_at: "2011-05-26 10:02:38", updated_at: "2011-05-26 10:02:38", customer: "g", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2011-05-05", rule_id: nil, trenche: nil>, #<Sale id: 21, isbn_id: 2, quantity: 1000, value: 5193, currency: "", total_quantity: nil, created_at: "2011-05-25 14:12:45", updated_at: "2011-05-25 14:12:45", customer: "a", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2010-10-15", rule_id: nil, trenche: nil>, #<Sale id: 13, isbn_id: 2, quantity: 50, value: 159, currency: "", total_quantity: nil, created_at: "2011-05-25 12:33:09", updated_at: "2011-05-25 12:33:09", customer: "a", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2010-01-01", rule_id: nil, trenche: nil>, #<Sale id: 14, isbn_id: 2, quantity: 25, value: 129, currency: "", total_quantity: nil, created_at: "2011-05-25 12:33:23", updated_at: "2011-05-25 12:33:23", customer: "", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2010-01-01", rule_id: nil, trenche: nil>, #<Sale id: 12, isbn_id: 2, quantity: 100, value: 415, currency: "", total_quantity: nil, created_at: "2011-05-25 12:32:50", updated_at: "2011-05-25 15:13:21", customer: "a", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2001-10-01", rule_id: nil, trenche: nil>, #<Sale id: 11, isbn_id: 2, quantity: 500, value: 2197, currency: "", total_quantity: nil, created_at: "2011-05-25 12:32:24", updated_at: "2011-05-25 15:11:20", customer: "a", retail_price: nil, discount: nil, channel_id: 1, invoice_date: "2000-10-01", rule_id: nil, trenche: nil>], #<Channel id: 2, isbn_id: nil, channel_name: "Export", created_at: "2011-05-16 19:47:35", updated_at: "2011-05-16 19:47:35">=>[#<Sale id: 23, isbn_id: 2, quantity: 2000, value: 5000, currency: "", total_quantity: nil, created_at: "2011-05-26 09:16:15", updated_at: "2011-05-26 09:16:15", customer: "v", retail_price: nil, discount: nil, channel_id: 2, invoice_date: "2011-05-02", rule_id: nil, trenche: nil>, #<Sale id: 17, isbn_id: 2, quantity: 242, value: 657, currency: "", total_quantity: nil, created_at: "2011-05-25 12:34:24", updated_at: "2011-05-25 12:34:24", customer: "b ", retail_price: nil, discount: nil, channel_id: 2, invoice_date: "2010-10-15", rule_id: nil, trenche: nil>, #<Sale id: 18, isbn_id: 2, quantity: 54, value: 194, currency: "", total_quantity: nil, created_at: "2011-05-25 12:34:44", updated_at: "2011-05-25 12:34:44", customer: "b ", retail_price: nil, discount: nil, channel_id: 2, invoice_date: "2010-10-15", rule_id: nil, trenche: nil>, #<Sale id: 15, isbn_id: 2, quantity: 135, value: 377, currency: "", total_quantity: nil, created_at: "2011-05-25 12:33:48", updated_at: "2011-05-25 12:33:48", customer: "b ", retail_price: nil, discount: nil, channel_id: 2, invoice_date: "2010-09-15", rule_id: nil, trenche: nil>, #<Sale id: 16, isbn_id: 2, quantity: 433, value: 830, currency: "", total_quantity: nil, created_at: "2011-05-25 12:34:06", updated_at: "2011-05-25 12:34:06", customer: "b ", retail_price: nil, discount: nil, channel_id: 2, invoice_date: "2010-09-15", rule_id: nil, trenche: nil>]}
以下是我的销售模型中的列:
# id :integer not null, primary key
# isbn_id :integer
# quantity :integer
# value :integer
# currency :string(255)
# total_quantity :integer
# created_at :datetime
# updated_at :datetime
# customer :string(255)
# retail_price :integer
# discount :decimal(, )
# channel_id :integer
# invoice_date :date
# rule_id :integer
提前非常感谢。
更新:最终解决方案。
真的不确定这是否算是"回馈社区",因为它滑稽而冗长,不枯燥,充满了我用来找出所有错误的put,而且启动格式很糟糕,但见鬼,我是个傻瓜,至少几年后当我知道自己在做什么时,我可以回到这里自嘲。这是我在Sale.rb中的最终解决方案。糟糕的过度填充模型。总有一天我会重构这个。
before_save :runningtotal
after_commit :refresh
private
def runningtotal
# get the latest sale that matches the new sale's isbn and channel id, then rank by invoice date descending, and get the first record:
lastsale = Sale.where(:isbn_id => self.isbn_id).where(:channel_id => self.channel_id).order("invoice_date DESC").first
allsales = Sale.where(:isbn_id => self.isbn_id).where(:channel_id => self.channel_id).order("invoice_date DESC")
# set the total_quantity field in the new sales record to its quantity + the last sale's total.
if allsales.maximum(:invoice_date).nil?
puts "runningtotal thinks the max of invoice date in the allsales relation is nil"
puts "and runningtotal is setting total_quantity on the new sale to be #{self.quantity + (lastsale.try(:total_quantity) || 0)}"
self.total_quantity = self.quantity + (lastsale.try(:total_quantity) || 0)
else
if self.invoice_date < allsales.maximum(:invoice_date)
puts "the runningtotal method has been skipped because runningtotal thinks the current invoice date is less than the highest invoice date in the allsales relation"
else
puts "this is a normal entry so runningtotal has set the total quantity to be #{self.quantity + (lastsale.try(:total_quantity) || 0) }"
self.total_quantity = self.quantity + (lastsale.try(:total_quantity) || 0)
end
end
end
def refresh
allsales = Sale.where(:isbn_id => self.isbn_id).where(:channel_id => self.channel_id).order("invoice_date ASC")
#if the runningtotal callback hasn't run, the total quantity will be nil, and nil triggers this after_commit callback
if total_quantity.nil?
puts "running refresh callback"
puts "here's a sample parameter pass: id: #{id} quantity: #{quantity} date: #{invoice_date} "
puts "allsales class is #{allsales.class}"
# if the new sale that's being saved has a date that's before any previous sale...
puts "before the if, refresh thinks that the earliest invoice date is #{allsales.minimum(:invoice_date)} and that invoice date is #{invoice_date}"
if invoice_date <= allsales.minimum(:invoice_date)
puts "date earlier than existing sales dates"
puts "refresh thinks that the earliest invoice date is #{allsales.minimum(:invoice_date)} and that invoice date is #{invoice_date}"
#... then set its total_quantity to the sale quantity...
update_attribute(:total_quantity, quantity)
puts "total_qty updated with qty"
# ... and update all subsequent records' total_quantity (skipping the before_save callback which would trigger an infinite loop).
allsales.each_with_index do |sale, i|
previous_sale = allsales[i-1] unless i==0
next unless previous_sale
puts "getting qty out of arel when date earlier than others: #{previous_sale.quantity}"
puts "this is adding #{quantity} to #{previous_sale.quantity } which is #{quantity + previous_sale.total_quantity }"
Sale.skip_callback(:save, :before, :runningtotal )
sale.update_attribute(:total_quantity, (sale.quantity + previous_sale.total_quantity ))
Sale.set_callback(:save, :before, :runningtotal)
end
else
# if the invoice date is within the min and max range of the previous sales...
# ... update all previous and subsequent records' total_quantity (skipping the before_save callback which would trigger an infinite loop).
allsales.each_with_index do |sale, i|
previous_sale = allsales[i-1] unless i==0
next unless previous_sale
puts "getting qty out of arel within existing date range: #{previous_sale.quantity}"
puts "this is adding #{quantity} to #{previous_sale.quantity } which is #{quantity + previous_sale.total_quantity }"
Sale.skip_callback(:save, :before, :runningtotal )
sale.update_attribute(:total_quantity, (sale.quantity + previous_sale.total_quantity ))
Sale.set_callback(:save, :before, :runningtotal )
end
end
end
end
是的,在模型中使用before_save将在每次保存时运行,无论是新的还是更新的。因此,在计算中需要注意当前(新)记录还不存在的预期。)您可能希望使用before_save, :on => :create
将其限制为创建操作。
然而,如果我理解你对这个问题的英语陈述,你的代码就相当复杂了。我甚至不知道@isbn设置在哪里,这可能很危险。。。
这是否需要更新其他对象(isbn和channel)的总数?通常情况下,最好根据需要简单地计算,而不是试图在每个记录中缓存总数。
在回调中,self
是当前(新?)记录,因此使用它来引用新值。
@sale=销售订单("invoice_date ASC")@lastbal=@sale.find_all_by_isbn_id(@isbn).group_by(&:频道)
可以用这个来代替,我认为:
@lastbal=销售订单("invoice_date ASC")。其中(:isbn_id=>self.isbn_id).group_by(&:channel)
我假设@isbn实际上是新纪录的isbn。
从那以后,我不确定你是只想更新新记录还是旧记录。。。如果您想更新当前记录,只需设置属性并退出回调,其余记录将在保存时保存:self.total_quantity = previous_sale.quantity + self.quantity
如果您也打算更新其他对象,那么我们必须更新这些对象并保存它们。我在你的代码中根本没有看到这种情况。
您的代码会经过几个循环,可能会多次命中write_attribute。。。这没有道理。
如果你的意思是,你想找到与当前isbn匹配的最后一条记录,并通过渠道更新新记录,我会这样做:
def runningtotallastsale=Sale.where(:isbn_id=>self.isbn_id)。其中(:channel_id=>self.channel_id)。订单("invoice_date DESC")。优先#这应该是最新的匹配销售#当前isbn和通道self.total_quantity=self.quantity+(lastsale.try(:total_quality)||0)#如果不存在以前的记录,请注意零^终止
`