Calculation of royalties based on ranges in rails 3

I created an application that tracks book sales and, hopefully, is going to calculate royalties.

Now I am tracking sales in orders. Each order has_many: line_items. When the new position is saved, I calculate the total sales of this product, so I have the total sales.

Each author has several royalty rules based on their contract. For example, from 0 to 5000 copies are sold, they get 10 percent. 5001 to 10,000, they get 20 percent. First, I calculated the author’s share for each position. It worked well, but then I realized that my application chooses which royalty rule to apply based on total sales. If I place a large order, it is possible that royalties will be calculated with a high royalty rate for the entire position, when in fact the royalties should be calculated on the basis of both a lower and a high royalty rate (as in one line the element pushes the total sales, past the recall point of the royalty rule).

So my question is how best to do this. I studied the use of ranges, but this is all a little new to me, and the code is getting a bit complicated. Here, admittedly, is the clumsy code that I use to pull all the royal rules for a given contract into an array:

  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 has: bottom and top for each royalty rule, so for this product the first: the bottom will be 0 and the top will be 5000, then the second: the bottom will be 5001, and the second: the top will be 10,000, etc.

Any help or ideas with this would be appreciated. This is actually the last step for me to get a fully functional version that I can play with.

I used this code below to select a specific rule based on the value of total_sold, but again, which has the effect of cumulative sales and choosing the highest royalty rate instead of splitting them.

    @rules = @contract.royalties.where("lower <= :total_sold AND upper >= :total_sold", {:total_sold => @total_sold}).limit(1)

.

+3
3

, - , , , ?

- :

class Author
  has_many :royalty_rules
end

class RoyaltyRule
  belongs_to :author
  # columns :lower, :upper, :rate
end

, , .

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 .

+3

, , ?

, , , , .

, 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

- .

+1

You can use ranges of variables, for example. min..max

$irb
> min = 0  
=> 0 
> max = 5000  
=> 5000
> min..max 
=> 0..5000 
> (min..max).class
=> Range

% - Numeric.modulo, see http://www.ruby-doc.org/core/classes/Numeric.html#M000969 for details.

0
source

Source: https://habr.com/ru/post/1786048/


All Articles