fairmondo/fairmondo

View on GitHub
app/abaci/transport_abacus.rb

Summary

Maintainability
A
0 mins
Test Coverage
#   Copyright (c) 2012-2017, Fairmondo eG.  This file is
#   licensed under the GNU Affero General Public License version 3 or later.
#   See the COPYRIGHT file for details.

class TransportAbacus
  attr_reader :single_transports, :unified_transport, :total_transport, :free_transport, :free_transport_at_price

  def self.calculate business_transaction_abacus
    abacus = TransportAbacus.new(business_transaction_abacus)
    abacus.check_free_transport
    abacus.calculate_single_transports
    abacus.calculate_unified_transport if business_transaction_abacus.line_item_group.unified_transport?
    abacus.calculate_total_transport
    abacus
  end

  def check_free_transport
    @free_transport_at_price = @line_item_group.free_transport_at_price
    @free_transport = (@free_transport_at_price &&  @business_transaction_abacus.total_retail_price >= @free_transport_at_price)
  end

  def calculate_single_transports
    result = @business_transaction_abacus.single_transports.map do |bt|
      [bt, calculate_single_transport_for(bt)]
    end
    @single_transports = result.to_h
  end

  def calculate_unified_transport
    number_of_items = @business_transaction_abacus.unified_transport.map(&:quantity_bought).sum
    shipments = self.class.number_of_shipments(number_of_items, @line_item_group.unified_transport_maximum_articles)
    transport_price = transport_price_for(@line_item_group.unified_transport_price, shipments)
    total_retail_price = @business_transaction_abacus.unified_transport.map { |bt| retail_price(bt) }.sum
    @unified_transport = {
      method: :unified,
      business_transactions: @business_transaction_abacus.unified_transport,
      provider: @line_item_group.unified_transport_provider,
      shipments: shipments,
      per_shipment: @line_item_group.unified_transport_price,
      transport_price: transport_price,
      total: total_retail_price + transport_price
    }
  end

  def calculate_total_transport
    @total_transport = Money.new(0)
    @single_transports.each_pair { |_k, v| @total_transport += v[:transport_price] }
    @total_transport += @unified_transport[:transport_price] if @unified_transport
  end

  def self.number_of_shipments quantity, maximum_per_shipment
    return 0 if maximum_per_shipment == 0
    quantity.fdiv(maximum_per_shipment).ceil
  end

  private

  def initialize business_transaction_abacus
    @line_item_group = business_transaction_abacus.line_item_group
    @business_transaction_abacus = business_transaction_abacus
  end

  def calculate_single_transport_for bt
    single_transport_price, transport_number = bt.article.transport_details_for bt.selected_transport.to_sym
    shipments = self.class.number_of_shipments(bt.quantity_bought, transport_number)
    transport_price = transport_price_for(single_transport_price, shipments)
    cash_on_delivery_price = self.class.cash_on_delivery_price(bt, shipments)
    total_without_cash_on_delivery = retail_price(bt) + transport_price
    total = total_without_cash_on_delivery + (cash_on_delivery_price || Money.new(0))
    {
      method: bt.selected_transport.to_sym,
      provider: bt.article.transport_provider(bt.selected_transport),
      shipments: shipments,
      per_shipment: single_transport_price,
      transport_price: transport_price,
      cash_on_delivery: cash_on_delivery_price,
      total_without_cash_on_delivery: total_without_cash_on_delivery,
      total: total
    }
  end

  def transport_price_for single_transport_price, number_of_shipments
    @free_transport ? Money.new(0) : (single_transport_price * number_of_shipments)
  end

  def self.cash_on_delivery_price business_transaction, number_of_shipments
    return nil unless business_transaction.selected_payment.cash_on_delivery?
    business_transaction.article_payment_cash_on_delivery_price * number_of_shipments
  end

  def retail_price business_transaction
    @business_transaction_abacus.prices[business_transaction][:retail_price]
  end
end