A summary of the implementation of the interval proportional algorithm

1: Interval find/call selection (weight range and shipping cost)
PROFIT = {
    0...10 => ->(o){ 50 },
    10...30 => ->(o){ 40 },
    30...50 => ->(o){ o },
    50...200 => ->(o){ o * 0.5 },
    200...1000 => ->(o){ o * 0.4 },
    1000..BigDecimal::INFINITY => ->(o){ o * 0.3 }
  }

  def compute_profit_price
    result = PROFIT.find { |k, _| k.include? import_price }
    self.profit_price = result[1].call(import_price).to_d.ceil(2)
    self.save
  end


Two: eval hash table acquisition (amount range and bonus points)
GROSS_RATE =
 {"trader_grade_general" => [[" _ < 10000",0],
  ["10000 <= _ && _ < 15000",0.03],
  ["15000 <= _  && _ < 20000",0.05],
  ["20000 <= _  && _ < 25000",0.07],
  ["25000 <= _ && _ < 30000",0.09],
  ["30000 <= _  && _< 35000",0.11],
  ["35000 <= _  && _< 40000",0.13],
  ["_ >= 40000",0.05,40000]],
 "trader_grade_high" => [[" _ < 10000",0],
  ["10000 <= _ && _ < 15000",0.04],
  ["15000 <= _  && _ < 20000",0.06],
  ["20000 <= _  && _ < 25000",0.08],
  ["25000 <= _ && _ < 30000",0.1],
  ["30000 <= _  && _< 35000",0.12],
  ["35000 <= _  && _< 40000",0.14],
  ["40000 <= _  && _< 45000",0.16],
  ["45000 <= _  && _< 50000",0.18],
  ["_ >= 50000",0.06,50000]] }

  AMOUNT_RATE =
  [["_ < 500",0.01],
  ["500 <= _ && _ <2000",0.009],
  ["2000 <= _ && _ <5000",0.008],
  ["5000 <= _ && _ < 10000",0.007],
  ["10000 <= _ && _ <50000",0.006],
  ["50000 <= _ && _ < 100000",0.005],
  ["100000 <= _ && _ < 500000",0.004],
  ["_ > 500000",0.003]]	



# Gross profit of sales amount
  def self.get_gross_rate(trader_type,amount)
    return_rtb_rate = 0
    base_trpe = GROSS_RATE[trader_type] || GROSS_RATE["trader_grade_general"]
    base_trpe.each_with_index do |rtb_rate,index|
      _ = amount
      if eval(rtb_rate[0])
      	case_amount = (index + 1 == base_trpe.size) ? (amount - rtb_rate[2]) : (amount % 5000)
        return_rtb_rate += rtb_rate[1] * case_amount
        break
      else
        return_rtb_rate += rtb_rate[1] * 5000
      end
    end
    return return_rtb_rate
  end


  # Gross Profit Bonus
  def self.get_amount_rate(amount)
    return_rtb_rate = 0
    AMOUNT_RATE.each do |rtb_rate|
      _ = amount
      if eval(rtb_rate[0])
        return_rtb_rate = amount * rtb_rate[1]
        break
      end
    end
    return return_rtb_rate
  end


Three: Database-based model design (product packaging range and quotation cost)

#Database serialization field record
{:levels=>"24.999-49.999,49.999-99.999,99.999-199.999,199.999-499.999,499.999-", :prices=>"48,46,42,41,37", :area_prices=>""}
{:levels=>"2.999-99.999,99.999-499.999,499.999-", :prices=>"53,50,46", :area_prices=>"53,50,46"}


def price_of_package(package, package_unit, area = nil)
    return nil if self.rules.blank?
    return nil if !eval(self.rules).is_a?(Hash)
    price = nil
    #packaging unit conversion
    converted_package = convert_unit(package, package_unit, self.package_unit, self.direct_ship_chemical&.density)
    return nil if converted_package.nil?

    arr_levels = eval(self.rules)[:levels].split(',')
    arr_prices = eval(self.rules)[:prices].split(',')
    if area.present? && self.areas.present? && self.areas.include?(area)
       arr_prices = eval(self.rules)[:area_prices].split(',')
    end

    #bulk packaging
    if self.bulk_package?
      arr_levels.each.with_index do |p, i|
        arr_p = p.split('-') # 10-20 or 500-
        if converted_package > arr_p[0].to_d && ( (arr_p[1].present? && converted_package <= arr_p[1].to_d) || arr_p[1].blank? )
          price = arr_prices[i].to_d and break
        end
        # If it exceeds the maximum package set, take the price of the maximum package
        if p == arr_levels.last
          price = arr_prices[i].to_d and break
        end
      end
    elsif self.buyout_package?
      #standard packaging
      ls = arr_levels.sort{|x, y| x.to_f <=> y.to_f }
      ps = arr_prices.sort{|x, y| x.to_f <=> y.to_f }
      level_prices = ls.map.with_index{|x, i| [x.to_f, ps[i].to_f]}
      result = level_prices.select{|p| p[0] >= converted_package }
      price = result.present? ? result.first[1] : nil
    else
      # 1. If it exceeds the maximum package and is an integer multiple of the maximum package, take the maximum package * multiple
      # 2. Less than the largest package, take the price of the largest value in the range
      ls = arr_levels.sort{|x, y| x.to_f <=> y.to_f }
      ps = arr_prices.sort{|x, y| x.to_f <=> y.to_f }
      level_prices = ls.map.with_index{|x, i| [x.to_f, ps[i].to_f]}
      min = level_prices.first
      max = level_prices.last
      if converted_package > max[0]
        return nil if max[0] == 0
        price = converted_package * (max[1] / max[0])
      elsif converted_package < min[0]
        price = nil
      else
        front = level_prices.select{|p| p[0] <= converted_package }.last
        back = level_prices.select{|p| p[0] >= converted_package }.first
        if front[0] == back[0]
          price = front[1]
        else
          if converted_package <= (front[0] + back[0]) / 2.to_f
            price = converted_package * (front[1] / front[0])
          else
            price = converted_package * (back[1] / back[0])
          end
        end
      end
    end
    price
  end


Four: Designing a whole table and the advantages and disadvantages of

serialized fields are easy to set and store. (The program code is required to be de-parsed)
The whole table is convenient for statistics and configuration addition. (extra plan)
serialize :extra, Array

 def compute_price(amount, extra_hash = {})
    extra_hash.stringify_keys!

    if self.contain_max
      range = { 'min-lte': amount.to_d, 'max-gte': amount.to_d }
    else
      range = { 'min-lte': amount.to_d, 'max-gt': amount.to_d }
    end

    query = range.merge(extra_hash.slice(*extra))
    charge = self.charges.default_where(query).first
    if charge
      charge.subtotal = charge.final_price(amount)
      charge.default_subtotal = charge.subtotal
    else
      charge = self.charges.build
      charge.subtotal = 0
    end
    charge
  end


#serves records the charge type, serve_charges records the charge type interval
#serve_charges: The fields such as nation/zone/danger in the server are reverse-parsed and matched with the extra serialized field of serves.

create_table "serve_charges", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.bigint "serve_id"
    t.decimal "min", precision: 12, scale: 4, default: "0.0"
    t.decimal "max", precision: 12, scale: 4, default: "99999999.9999"
    t.decimal "price", precision: 10, scale: 2
    t.string "type"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "nation"
    t.string "zone"
    t.string "danger"
    t.string "is_use_courier"
    t.string "good_type"
    t.string "incoterms"
    t.decimal "base_price", precision: 10, scale: 2, default: "0.0"
    t.index ["serve_id"], name: "index_serve_charges_on_serve_id"
  end

  create_table "serves", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string "type"
    t.string "unit"
    t.string "name"
    t.string "scope"
    t.boolean "verified", default: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "default", default: false
    t.boolean "overall", default: true
    t.string "extra"
    t.boolean "contain_max", default: false
    t.string "deal_type"
    t.bigint "deal_id"
    t.index ["deal_type", "deal_id"], name: "index_serves_on_deal_type_and_deal_id"
  end

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326292968&siteId=291194637