我已经建立了一个跟踪书籍销售的应用程序,并且 - 希望 - 计算作者版税。
现在,我跟踪订单的销售情况。每个订单has_many:line_items。保存新订单项后,我会计算给定产品的总销售额,因此我的总销售额会计算在内。
每位作者都有基于合同的多种版税规则。例如,销售0至5000份,他们获得10%。 5001到10,000,他们得到20%。起初,我正在计算作者每个订单项的份额。它运作良好,但后来我意识到我的应用程序正在根据总销售额选择应用哪个版税规则。如果我发布一个大订单,作者的版税可能会以整个订单项的高专利费率计算,实际上,版税应根据较低和较高的专利费率计算(如,一行item推送通过版税规则断点的总销售额。)
所以我的问题是如何最好地解决这个问题。我已经探索过使用范围,但这对我来说有点新鲜,代码变得有点复杂。以下是我用来将给定合约的所有版税规则纳入数组的公认笨重的代码:
def royalty_rate
@product = Product.find_by_id(product_id)
@total_sold = @product.total_sold
@rules = Contract.find_by_product_id(@product).royalties
... where next?
end
@rules对每个版税规则都有:lower和:upper,所以对于这个产品,第一个:lower将是0而:upper将是5000,然后是第二个:lower将是5001而第二个:upper将是10,000,等等。
任何有关此的帮助或想法将不胜感激。这实际上是我可以使用完全正常工作版本的最后一步。
我使用下面的代码根据total_sold的值选择一个特定的规则,但同样,这会产生累积销售额并选择最高的特许权使用费率而非分割它们的效果。
@rules = @contract.royalties.where("lower <= :total_sold AND upper >= :total_sold", {:total_sold => @total_sold}).limit(1)
提前致谢。
答案 0 :(得分:3)
听起来您需要为每位作者单独存储版税计算规则 - 或者您可能有多个方案,每个作者都与其中一个相关联?
对于第一种情况,可能是这样的:
class Author
has_many :royalty_rules
end
class RoyaltyRule
belongs_to :author
# columns :lower, :upper, :rate
end
因此,当添加作者时,您会为每个层的RoyaltyRule模型添加行。然后,您需要一种方法来计算版税
class Author
def royalty(product)
product = Product.find_by_id(product.id)
units = product.total_sold
amount = 0
royalty_rules.each do |rule|
case units
when 0
when Range.new(rule.lower,rule.upper)
# reached the last applicable rule -- add the part falling within the tier
amount += (units - rule.lower + 1) * rule.rate
break
else
# add the full amount for the tier
amount += (rule.upper - rule.lower + 1) * rule.rate
end
end
amount
end
end
还有一些要测试的规格:
describe Author do
before(:each) do
@author = Author.new
@tier1 = mock('tier1',:lower=>1,:upper=>5000,:rate=>0.10)
@tier2 = mock('tier2',:lower=>5001,:upper=>10000,:rate=>0.20)
@tier3 = mock('tier3',:lower=>10001,:upper=>15000,:rate=>0.30)
@author.stub(:royalty_rules) { [@tier1,@tier2,@tier3] }
end
it "should work for one tier" do
product = mock('product',:total_sold=>1000)
@author.royalty(product).should == 100
end
it "should work for two tiers" do
product = mock('product',:total_sold=>8000)
@author.royalty(product).should == (5000 * 0.10) + (3000 * 0.20)
end
it "should work for three tiers" do
product = mock('product',:total_sold=>14000)
@author.royalty(product).should == (5000 * 0.10) + (5000 * 0.20) + (4000 * 0.30)
end
# edge cases
it "should be zero when units is zero" do
product = mock('product',:total_sold=>0)
@author.royalty(product).should == 0
end
it "should be 500 when units is 5000" do
product = mock('product',:total_sold=>5000)
@author.royalty(product).should == 500
end
it "should be 500.2 when units is 5001" do
product = mock('product',:total_sold=>5001)
@author.royalty(product).should == 500.2
end
end
注意:Author.royalty_rules
需要返回从低到高排序的层。此外,最低层以1而不是0开头,以便于计算。
答案 1 :(得分:1)
所以,我不明白为什么你不能只计算出售的总量?
我会假设他们不需要知道订单的确切时刻,所以为什么不根据昨天的销售量来计算。
例如,在早上(比方说)运行rake任务,在RoyaltyPayments
lib/royalty_payments.rb
的以下模块
执行类似
的操作Module RoyaltyPayments
def royalty_range(total_sold, product_price)
sold_price = total_sold * product_price
leftover = total_sold % 5000
case total_sold
when 0..5000
total_sold * 0.1
when 5001..10000
((sold_price * 0.10)*5000) + ((sold_price * 0.2)*leftover)
else
((sold_price * 0.10)*5000) + (sold_price * 0.20)*5000) + ((sold_price * 0.3)*(total_sold - 10000)
end
end
然后制作lib/tasks/royalty_payments.rake
在该文件中添加如下内容:
include RoyaltyPayments
namespace :royalty_payments
desc "Make royalty calculations"
task :calculate_latest_totals
Product.all.each do |product|
total_sold = product.total_sold
royalty_range(total_sold, product.price)
end
类似的东西。
答案 2 :(得分:0)
您可以执行各种变量,例如。最小..最大
$irb
> min = 0
=> 0
> max = 5000
=> 5000
> min..max
=> 0..5000
> (min..max).class
=> Range
%是Numeric.modulo,请参阅http://www.ruby-doc.org/core/classes/Numeric.html#M000969了解详细信息。