# encoding: UTF-8 module RedmineCrm class Currency module Formatting def self.included(base) [ [:thousands_separator, :delimiter, RedmineCrm::Settings::Money.thousands_delimiter], [:decimal_mark, :separator, RedmineCrm::Settings::Money.decimal_separator] ].each do |method, name, character| define_i18n_method(method, name, character) end end def self.define_i18n_method(method, name, character) define_method(method) do if self.class.use_i18n begin I18n.t name, :scope => "number.currency.format", :raise => true rescue I18n::MissingTranslationData I18n.t name, :scope =>"number.format", :default => (currency.send(method) || character) end else currency.send(method) || character end end alias_method name, method end def format(value, currency, *rules) # support for old format parameters rules = normalize_formatting_rules(rules) if currency rules = self.localize_formatting_rules(rules, currency) rules = self.translate_formatting_rules(rules, currency.code) if rules[:translate] rules[:decimal_mark] = currency.decimal_mark if rules[:decimal_mark].nil? rules[:decimal_places] = currency.decimal_places rules[:subunit_to_unit] = currency.subunit_to_unit rules[:thousands_separator] = currency.thousands_separator if rules[:thousands_separator].nil? end rules = Currency.default_formatting_rules.merge(rules){|key, v1, v2| v2.nil? ? v1 : v2} # if fractional == 0 if rules[:display_free].respond_to?(:to_str) return rules[:display_free] elsif rules[:display_free] return "free" end # end symbol_value = currency.try(:symbol) || "" formatted = value.abs.to_s # if rules[:rounded_infinite_precision] if currency formatted.gsub!(/#{rules[:decimal_mark]}/, '.') unless '.' == rules[:decimal_mark] formatted = ((BigDecimal(formatted) * currency.subunit_to_unit).round / BigDecimal(currency.subunit_to_unit.to_s)).to_s("F") formatted.gsub!(/\..*/) do |decimal_part| decimal_part << '0' while decimal_part.length < (currency.decimal_places + 1) decimal_part end formatted.gsub!(/\./, rules[:decimal_mark]) unless '.' == rules[:decimal_mark] end sign = value < 0 ? '-' : '' if rules[:no_cents] || (rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0) formatted = "#{formatted.to_i}" end # thousands_separator_value = currency.thousands_separator # Determine thousands_separator if rules.has_key?(:thousands_separator) thousands_separator_value = rules[:thousands_separator] || '' end decimal_mark = rules[:decimal_mark] # Apply thousands_separator formatted.gsub!(regexp_format(formatted, rules, decimal_mark, symbol_value), "\\1#{thousands_separator_value}") symbol_position = symbol_position_from(rules, currency) if currency if rules[:sign_positive] == true && (value >= 0) sign = '+' end if rules[:sign_before_symbol] == true sign_before = sign sign = '' end if symbol_value && !symbol_value.empty? symbol_value = "#{symbol_value}" if rules[:html_wrap_symbol] formatted = if symbol_position == :before symbol_space = rules[:symbol_before_without_space] === false ? " " : "" "#{sign_before}#{symbol_value}#{symbol_space}#{sign}#{formatted}" else symbol_space = rules[:symbol_after_without_space] ? "" : " " "#{sign_before}#{sign}#{formatted}#{symbol_space}#{symbol_value}" end else formatted="#{sign_before}#{sign}#{formatted}" end # apply_decimal_mark_from_rules(formatted, rules) if rules[:with_currency] formatted << " " formatted << '' if rules[:html] formatted << currency.to_s formatted << '' if rules[:html] end formatted end def default_formatting_rules { decimal_mark: RedmineCrm::Settings::Money.decimal_separator || '.', thousands_separator: RedmineCrm::Settings::Money.thousands_delimiter || ',', subunit_to_unit: 100 } end def regexp_format(formatted, rules, decimal_mark, symbol_value) regexp_decimal = Regexp.escape(decimal_mark) if rules[:south_asian_number_formatting] /(\d+?)(?=(\d\d)+(\d)(?:\.))/ else # Symbols may contain decimal marks (E.g "դր.") if formatted.sub(symbol_value.to_s, "") =~ /#{regexp_decimal}/ /(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/ else /(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/ end end end def translate_formatting_rules(rules, iso_code) begin rules[:symbol] = I18n.t iso_code, :scope => "number.currency.symbol", :raise => true rescue I18n::MissingTranslationData # Do nothing end rules end def localize_formatting_rules(rules, currency) if currency.iso_code == "JPY" && I18n.locale == :ja rules[:symbol] = "円" unless rules[:symbol] == false rules[:symbol_position] = :after rules[:symbol_after_without_space] = true elsif currency.iso_code == "CHF" rules[:symbol_before_without_space] = false end rules end def symbol_value_from(rules) if rules.has_key?(:symbol) if rules[:symbol] === true symbol elsif rules[:symbol] rules[:symbol] else "" end elsif rules[:html] currency.html_entity == '' ? currency.symbol : currency.html_entity elsif rules[:disambiguate] and currency.disambiguate_symbol currency.disambiguate_symbol else symbol end end def symbol_position_from(rules, currency) if rules.has_key?(:symbol_position) if [:before, :after].include?(rules[:symbol_position]) return rules[:symbol_position] else raise ArgumentError, ":symbol_position must be ':before' or ':after'" end elsif currency.symbol_first? :before else :after end end private # Cleans up formatting rules. # # @param [Hash] rules # # @return [Hash] def normalize_formatting_rules(rules) if rules.size == 0 rules = {} elsif rules.size == 1 rules = rules.pop rules = { rules => true } if rules.is_a?(Symbol) end rules[:decimal_mark] = rules[:separator] || rules[:decimal_mark] rules[:thousands_separator] = rules[:delimiter] || rules[:thousands_separator] rules end # Applies decimal mark from rules to formatted # # @param [String] formatted # @param [Hash] rules def apply_decimal_mark_from_rules(formatted, rules) if rules.has_key?(:decimal_mark) && rules[:decimal_mark] # && rules[:decimal_mark] != decimal_mark regexp_decimal = Regexp.escape(rules[:decimal_mark]) formatted.sub!(/(.*)(#{regexp_decimal})(.*)\Z/, "\\1#{rules[:decimal_mark]}\\3") end end end end end