2011-01-18 16 views
3

私は書籍の販売を追跡するアプリケーションを構築しました。著者のロイヤルティを計算することを願っています。レールの範囲に基づいてロイヤリティを計算する3

今、私は注文の売上を追跡します。それぞれの注文has_many:line_items。新しい広告申込情報が保存されると、特定の商品の合計販売数が計算されるため、合計販売数が算出されます。

各著者は契約に基づいて複数のロイヤルティルールを持っています。たとえば、0〜5000部のコピーが販売された場合、10%が得られます。 5001〜10,000、彼らは20%を取得します。最初は、広告申込情報の1人あたりのシェアを計算していました。それはうまくいっていたが、その後、私のアプリは総売上高に基づいてどのロイヤルティルールを適用するかを選択していることに気づいた。大規模な注文を投稿すると、著者のロイヤルティはロイヤルティの高いロイヤルティで計算される可能性があります。実際ロイヤルティはロイヤリティの低い方と高い方の両方に基づいて計算する必要があります(1行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は0、:upperは5000、次にsecond:lowerは5001、second:upperは10,000などとなります。

これに関するヘルプやアイデアをいただければ幸いです。実際に私がプレイできる完全に機能するバージョンを手に入れる最後のステップです。

私はtotal_soldの値に基づいて特定のルールを選択するためにこのコードを使用しましたが、累積売上を取り、それらを分割するのではなく最高のロイヤリティレートを選択するという効果があります。

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

ありがとうございます。

答えて

3

著者ごとにロイヤルティ計算ルールを個別に保存する必要があるようですが、複数のスキームがあり、それぞれの作成者がその1つに関連付けられているようですね。最初のケースでは

、このような多分何か:

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を。また、最も簡単な計算のために、最下位ティアは0ではなく1で始まります。

+0

私の使用料は実際に契約モデルの下に保管されています。だから私がすでにやっていることはとても似ています。著者has_many:契約とhas_many royalty_rulesの契約。だから、著者has_many:royalty_rules、:through =>:契約によって、著者のロイヤルティーを得る。 Author.royalty_rulesを設定する必要はありませんので、必要な3つの列を引き出すだけです。 – Slick23

+0

したがって、上記のコードはコンソールで動作しています。しかし、私はそれを私の見解に入れてみると、未定義のメソッドtotal_sold for nil:NilClassが返されます。これは返されている製品の1つに問題があると信じさせます。 total_sold列は、すべて開発データベース内のすべての製品の値を持ちます。 – Slick23

+1

更新:私はそれを把握する。プロダクトIDがこのメソッドにどのように供給されたかには若干の問題がありましたが、現在は修正されており、素晴らしいことです。ご協力いただきありがとうございます! – Slick23

1

なぜ、私は売り上げ総量を計算できないのですか?

私は彼らが注文の正確な時点で知る必要はないと仮定しますので、昨日の時点で販売された数量に基づいて計算してください。

例えば、午前中に、rakeタスクを実行します(さんが言わせて)lib/royalty_payments.rb

でファイルにRoyaltyPaymentsと呼ばれる以下のモジュールを使用しています

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 

そういうもの。

+0

ありがとうございます...私はそれがどのように機能しており、それはうまくいくでしょう。しかし、私は本当の疑問は、それらをハードコーディングするのではなく、モデルの範囲をどのように引っ張るのかということです。たとえば、0..5000をハードコーディングする代わりに、それらの数値を取り出して、Royaltyモデルの:lowerと:upperの列から外側の制限に移入する必要があります。それは私が問題を抱えていることです。製品や著者ごとに範囲が変わります。 – Slick23

+0

ああ、私が得意でないことの1つは、ちょうど残りの= total_sold%5000は何ですか? – Slick23

+0

%は基本的に剰余を除算して返します。もしあなたが10499%5000を持っていれば戻り値は499になります。これらの値はどうやって保管していますか?契約モデルでは、範囲が格納されていると仮定しますか?私たちはこれらの数字を著者ごとに外挿して、この方法に加えることができます。 – pjammer

関連する問題