module Smerp module Common module FinUtils ## # Number Representation # A integer/decimal/bigdecimal can be a raw value or a percentage value # module NumberRep attr_accessor :type, :value end class RawValue include NumberRep def initialize(value, type) @value = value @type = type end def is_percent? false end alias_method :is_percentage?, :is_percent? def is_value? true end def rep_value case @type when :int @value.to_i when :float, :bigDec @value.to_f else @value end end end class Percent include NumberRep def initialize(value) @type = :percent @value = value end def is_percent? true end alias_method :is_percentage?, :is_percent? def is_value? false end def rep_value @value / 100.0 end end ## # End Number Representation ## ## # Start mixin methods for included class # Designed to be included into class # - Integer # - Float # - BigDecimal # def percentage_equals?(percent) if is_empty?(percent) false else if percent.is_percent? self.send(:percent).rep_value == percent.rep_value else false end end end def percent Percent.new(self.to_f) end def value case self when Integer RawValue.new(self.to_f, :int) when Float RawValue.new(self.to_f, :float) when BigDecimal RawValue.new(self.to_f, :bigDec) else raise FinUtilError, "Unsupported type '#{self.class}'" end end def percent_of(val, rounding = 0) vv = self.to_f / val.to_f * 100.0 if rounding > 0 vv.round(rounding) else vv end end ## End mixin methods # # Markup is % from cost # module Markup include TR::CondUtils def markup(percentage) val = self.to_f case percentage when NumberRep if percentage.is_percentage? res = val * (1+percentage.rep_value) elsif percentage.is_value? res = val + percentage.rep_value else res = val + percentage.to_i end else res = val + percentage.to_i end res end def markup_percentage_from_base(cost) cost = cost.to_f price = self.to_f per = ((price - cost) / cost) * 100.0 per end end # markup # # Margin is % from price # module Margin include TR::CondUtils def margin(percentage) val = self.to_f case percentage when NumberRep if percentage.is_percentage? res = val/(1-percentage.rep_value) elsif percentage.is_value? res = val+percentage.rep_value else res = val + percentage.to_i end else res = val + percentage.to_i end res end def margin_percentage_from_base(cost) cost = cost.to_f price = self.to_f per = ((price - cost) / price) * 100.0 per end end # margin ## # Rounding info ## class RoundingInfo attr_accessor :original_value, :rounding_param, :rounded_value end # make the rounding operation more humanize module RoundingHelper #include TeLogger::TeLogHelper #teLogger_tag :rounding def round_to_nearest(val = 0) v = "#{val}" if v.include?(".") rv = v.split(".")[1].length else rv = (v.length-1)*-1 end res = RoundingInfo.new res.original_value = self.to_f res.rounding_param = val res.rounded_value = self.to_f.round(rv) # rounding in financial is not the same as mathematical rounding if rv < 0 and res.rounded_value > 0 and res.rounded_value < res.original_value # adjusted base value abv = "1#{"0"*(rv*-1)}" # - means left of . # - - become + vv = res.original_value + abv.to_f res.rounded_value = vv.to_f.round(rv) end res end end # Rounding Helper ## End Rounding Info end end end class Integer include Smerp::Common::FinUtils include Smerp::Common::FinUtils::Markup include Smerp::Common::FinUtils::Margin include Smerp::Common::FinUtils::RoundingHelper end class Float include Smerp::Common::FinUtils include Smerp::Common::FinUtils::Markup include Smerp::Common::FinUtils::Margin include Smerp::Common::FinUtils::RoundingHelper end class BigDecimal include Smerp::Common::FinUtils include Smerp::Common::FinUtils::Markup include Smerp::Common::FinUtils::Margin include Smerp::Common::FinUtils::RoundingHelper end