根据铁轨3的范围计算特许权使用费

时间:2011-01-18 02:26:26

标签: ruby-on-rails ruby ruby-on-rails-3 range

我已经建立了一个跟踪书籍销售的应用程序,并且 - 希望 - 计算作者版税。

现在,我跟踪订单的销售情况。每个订单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)

提前致谢。

3 个答案:

答案 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了解详细信息。