redmine_crm 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/redmine_crm.rb +1 -0
- data/lib/redmine_crm/currency.rb +7 -2
- data/lib/redmine_crm/currency/formatting.rb +221 -0
- data/lib/redmine_crm/money_helper.rb +71 -0
- data/lib/redmine_crm/rcrm_acts_as_taggable.rb +3 -3
- data/lib/redmine_crm/version.rb +1 -1
- data/test/money_helper_test.rb +10 -0
- data/test/tag_test.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c8441e8bec7aa49ee5f7c391e66cc174da87266
|
4
|
+
data.tar.gz: dfddb0ee0c407a3e3d6bc1d6e4ff91bf3e6816df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56a5b5fc3381ee13dff3740e0c0f027b13db220e5571f1027b3829acf74eddeaa76102f9dca14cd3dbe8122c57862130c44af839c4b02115ec8d2f5f4741827a
|
7
|
+
data.tar.gz: 34b79fa7e7ca483f9e3ad606c92e421f8409617ffa7f623d6a5018b26ad4f2280c3577ec464ed48f2f1e75104633e7336d1d5ad6d533f68dc744080890aacdf0
|
data/lib/redmine_crm.rb
CHANGED
data/lib/redmine_crm/currency.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "json"
|
2
2
|
require "redmine_crm/currency/loader"
|
3
3
|
require "redmine_crm/currency/heuristics"
|
4
|
+
require "redmine_crm/currency/formatting"
|
5
|
+
|
4
6
|
module RedmineCrm
|
5
7
|
# Represents a specific currency unit.
|
6
8
|
#
|
@@ -11,6 +13,9 @@ module RedmineCrm
|
|
11
13
|
extend Enumerable
|
12
14
|
extend RedmineCrm::Currency::Loader
|
13
15
|
extend RedmineCrm::Currency::Heuristics
|
16
|
+
extend RedmineCrm::Currency::Formatting
|
17
|
+
|
18
|
+
|
14
19
|
|
15
20
|
# Thrown when a Currency has been registered without all the attributes
|
16
21
|
# which are required for the current action.
|
@@ -177,7 +182,6 @@ module RedmineCrm
|
|
177
182
|
all.each { |c| yield(c) }
|
178
183
|
end
|
179
184
|
|
180
|
-
|
181
185
|
private
|
182
186
|
|
183
187
|
def stringify_keys
|
@@ -296,6 +300,7 @@ module RedmineCrm
|
|
296
300
|
end
|
297
301
|
self.id.to_s.downcase == other_currency_id
|
298
302
|
end
|
303
|
+
|
299
304
|
private :compare_ids
|
300
305
|
|
301
306
|
# Returns a Fixnum hash value based on the +id+ attribute in order to use
|
@@ -396,7 +401,7 @@ module RedmineCrm
|
|
396
401
|
end
|
397
402
|
|
398
403
|
private
|
399
|
-
|
404
|
+
|
400
405
|
def cache
|
401
406
|
self.class.decimal_places_cache
|
402
407
|
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module RedmineCrm
|
3
|
+
class Currency
|
4
|
+
module Formatting
|
5
|
+
def self.included(base)
|
6
|
+
[
|
7
|
+
[:thousands_separator, :delimiter, ","],
|
8
|
+
[:decimal_mark, :separator, "."]
|
9
|
+
].each do |method, name, character|
|
10
|
+
define_i18n_method(method, name, character)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.define_i18n_method(method, name, character)
|
15
|
+
define_method(method) do
|
16
|
+
if self.class.use_i18n
|
17
|
+
begin
|
18
|
+
I18n.t name, :scope => "number.currency.format", :raise => true
|
19
|
+
rescue I18n::MissingTranslationData
|
20
|
+
I18n.t name, :scope =>"number.format", :default => (currency.send(method) || character)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
currency.send(method) || character
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method name, method
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def format(value, currency, *rules)
|
31
|
+
# support for old format parameters
|
32
|
+
rules = normalize_formatting_rules(rules)
|
33
|
+
# byebug
|
34
|
+
rules = Currency.default_formatting_rules.merge(rules)
|
35
|
+
rules = self.localize_formatting_rules(rules, currency)
|
36
|
+
rules = self.translate_formatting_rules(rules, currency.code) if rules[:translate]
|
37
|
+
|
38
|
+
# if fractional == 0
|
39
|
+
if rules[:display_free].respond_to?(:to_str)
|
40
|
+
return rules[:display_free]
|
41
|
+
elsif rules[:display_free]
|
42
|
+
return "free"
|
43
|
+
end
|
44
|
+
# end
|
45
|
+
|
46
|
+
symbol_value = currency.symbol
|
47
|
+
|
48
|
+
formatted = value.abs.to_s
|
49
|
+
|
50
|
+
# if rules[:rounded_infinite_precision]
|
51
|
+
formatted.gsub!(/#{currency.decimal_mark}/, '.') unless '.' == currency.decimal_mark
|
52
|
+
formatted = ((BigDecimal(formatted) * currency.subunit_to_unit).round / BigDecimal(currency.subunit_to_unit.to_s)).to_s("F")
|
53
|
+
formatted.gsub!(/\..*/) do |decimal_part|
|
54
|
+
decimal_part << '0' while decimal_part.length < (currency.decimal_places + 1)
|
55
|
+
decimal_part
|
56
|
+
end
|
57
|
+
formatted.gsub!(/\./, currency.decimal_mark) unless '.' == currency.decimal_mark
|
58
|
+
# end
|
59
|
+
|
60
|
+
sign = value < 0 ? '-' : ''
|
61
|
+
|
62
|
+
if rules[:no_cents] || (rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0)
|
63
|
+
formatted = "#{formatted.to_i}"
|
64
|
+
end
|
65
|
+
|
66
|
+
thousands_separator_value = currency.thousands_separator
|
67
|
+
# Determine thousands_separator
|
68
|
+
if rules.has_key?(:thousands_separator)
|
69
|
+
thousands_separator_value = rules[:thousands_separator] || ''
|
70
|
+
end
|
71
|
+
|
72
|
+
# Apply thousands_separator
|
73
|
+
formatted.gsub!(regexp_format(formatted, rules, currency.decimal_mark, symbol_value),
|
74
|
+
"\\1#{thousands_separator_value}")
|
75
|
+
|
76
|
+
symbol_position = symbol_position_from(rules, currency)
|
77
|
+
|
78
|
+
if rules[:sign_positive] == true && (value >= 0)
|
79
|
+
sign = '+'
|
80
|
+
end
|
81
|
+
|
82
|
+
if rules[:sign_before_symbol] == true
|
83
|
+
sign_before = sign
|
84
|
+
sign = ''
|
85
|
+
end
|
86
|
+
|
87
|
+
if symbol_value && !symbol_value.empty?
|
88
|
+
symbol_value = "<span class=\"currency_symbol\">#{symbol_value}</span>" if rules[:html_wrap_symbol]
|
89
|
+
|
90
|
+
formatted = if symbol_position == :before
|
91
|
+
symbol_space = rules[:symbol_before_without_space] === false ? " " : ""
|
92
|
+
"#{sign_before}#{symbol_value}#{symbol_space}#{sign}#{formatted}"
|
93
|
+
else
|
94
|
+
symbol_space = rules[:symbol_after_without_space] ? "" : " "
|
95
|
+
"#{sign_before}#{sign}#{formatted}#{symbol_space}#{symbol_value}"
|
96
|
+
end
|
97
|
+
else
|
98
|
+
formatted="#{sign_before}#{sign}#{formatted}"
|
99
|
+
end
|
100
|
+
|
101
|
+
apply_decimal_mark_from_rules(formatted, rules)
|
102
|
+
|
103
|
+
if rules[:with_currency]
|
104
|
+
formatted << " "
|
105
|
+
formatted << '<span class="currency">' if rules[:html]
|
106
|
+
formatted << currency.to_s
|
107
|
+
formatted << '</span>' if rules[:html]
|
108
|
+
end
|
109
|
+
formatted
|
110
|
+
end
|
111
|
+
|
112
|
+
def default_formatting_rules
|
113
|
+
# byebug
|
114
|
+
{}
|
115
|
+
end
|
116
|
+
|
117
|
+
def regexp_format(formatted, rules, decimal_mark, symbol_value)
|
118
|
+
regexp_decimal = Regexp.escape(decimal_mark)
|
119
|
+
if rules[:south_asian_number_formatting]
|
120
|
+
/(\d+?)(?=(\d\d)+(\d)(?:\.))/
|
121
|
+
else
|
122
|
+
# Symbols may contain decimal marks (E.g "դր.")
|
123
|
+
if formatted.sub(symbol_value.to_s, "") =~ /#{regexp_decimal}/
|
124
|
+
/(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/
|
125
|
+
else
|
126
|
+
/(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def translate_formatting_rules(rules, iso_code)
|
132
|
+
begin
|
133
|
+
rules[:symbol] = I18n.t iso_code, :scope => "number.currency.symbol", :raise => true
|
134
|
+
rescue I18n::MissingTranslationData
|
135
|
+
# Do nothing
|
136
|
+
end
|
137
|
+
rules
|
138
|
+
end
|
139
|
+
|
140
|
+
def localize_formatting_rules(rules, currency)
|
141
|
+
if currency.iso_code == "JPY" && I18n.locale == :ja
|
142
|
+
rules[:symbol] = "円" unless rules[:symbol] == false
|
143
|
+
rules[:symbol_position] = :after
|
144
|
+
rules[:symbol_after_without_space] = true
|
145
|
+
end
|
146
|
+
rules
|
147
|
+
end
|
148
|
+
|
149
|
+
def symbol_value_from(rules)
|
150
|
+
if rules.has_key?(:symbol)
|
151
|
+
if rules[:symbol] === true
|
152
|
+
symbol
|
153
|
+
elsif rules[:symbol]
|
154
|
+
rules[:symbol]
|
155
|
+
else
|
156
|
+
""
|
157
|
+
end
|
158
|
+
elsif rules[:html]
|
159
|
+
currency.html_entity == '' ? currency.symbol : currency.html_entity
|
160
|
+
elsif rules[:disambiguate] and currency.disambiguate_symbol
|
161
|
+
currency.disambiguate_symbol
|
162
|
+
else
|
163
|
+
symbol
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def symbol_position_from(rules, currency)
|
168
|
+
if rules.has_key?(:symbol_position)
|
169
|
+
if [:before, :after].include?(rules[:symbol_position])
|
170
|
+
return rules[:symbol_position]
|
171
|
+
else
|
172
|
+
raise ArgumentError, ":symbol_position must be ':before' or ':after'"
|
173
|
+
end
|
174
|
+
elsif currency.symbol_first?
|
175
|
+
:before
|
176
|
+
else
|
177
|
+
:after
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
# Cleans up formatting rules.
|
184
|
+
#
|
185
|
+
# @param [Hash] rules
|
186
|
+
#
|
187
|
+
# @return [Hash]
|
188
|
+
def normalize_formatting_rules(rules)
|
189
|
+
if rules.size == 0
|
190
|
+
rules = {}
|
191
|
+
elsif rules.size == 1
|
192
|
+
rules = rules.pop
|
193
|
+
rules = { rules => true } if rules.is_a?(Symbol)
|
194
|
+
end
|
195
|
+
if !rules.include?(:decimal_mark) && rules.include?(:separator)
|
196
|
+
rules[:decimal_mark] = rules[:separator]
|
197
|
+
end
|
198
|
+
if !rules.include?(:thousands_separator) && rules.include?(:delimiter)
|
199
|
+
rules[:thousands_separator] = rules[:delimiter]
|
200
|
+
end
|
201
|
+
rules
|
202
|
+
end
|
203
|
+
|
204
|
+
# Applies decimal mark from rules to formatted
|
205
|
+
#
|
206
|
+
# @param [String] formatted
|
207
|
+
# @param [Hash] rules
|
208
|
+
def apply_decimal_mark_from_rules(formatted, rules)
|
209
|
+
if rules.has_key?(:decimal_mark) && rules[:decimal_mark] &&
|
210
|
+
rules[:decimal_mark] != decimal_mark
|
211
|
+
|
212
|
+
regexp_decimal = Regexp.escape(decimal_mark)
|
213
|
+
formatted.sub!(/(.*)(#{regexp_decimal})(.*)\Z/,
|
214
|
+
"\\1#{rules[:decimal_mark]}\\3")
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# byebug
|
2
|
+
# require 'action_controller'
|
3
|
+
require 'action_view'
|
4
|
+
|
5
|
+
module RedmineCrm
|
6
|
+
module MoneyHelper
|
7
|
+
|
8
|
+
def object_price(obj, price_field = :price)
|
9
|
+
price_to_currency(obj.try(price_field), obj.currency, :symbol => true).to_s if obj.respond_to?(:currency)
|
10
|
+
end
|
11
|
+
|
12
|
+
def prices_collection_by_currency(prices_collection, options={})
|
13
|
+
return [] if prices_collection.blank? || prices_collection == 0
|
14
|
+
prices = prices_collection
|
15
|
+
prices.reject!{|k, v| v.to_i == 0} if options[:hide_zeros]
|
16
|
+
prices.collect{|k, v| content_tag(:span, price_to_currency(v, k, :symbol => true), :style => "white-space: nowrap;")}.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
def deal_currency_icon(currency)
|
20
|
+
case currency.to_s.upcase
|
21
|
+
when 'EUR'
|
22
|
+
"icon-money-euro"
|
23
|
+
when 'GBP'
|
24
|
+
"icon-money-pound"
|
25
|
+
when 'JPY'
|
26
|
+
"icon-money-yen"
|
27
|
+
else
|
28
|
+
"icon-money-dollar"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def collection_for_currencies_select(default_currency = ContactsSetting.default_currency)
|
33
|
+
major_currencies_collection(default_currency)
|
34
|
+
end
|
35
|
+
|
36
|
+
def major_currencies_collection(default_currency)
|
37
|
+
currencies = []
|
38
|
+
currencies << default_currency.to_s unless default_currency.blank?
|
39
|
+
currencies |= ContactsSetting.major_currencies
|
40
|
+
currencies.map do |c|
|
41
|
+
currency = RedmineCrm::Currency.find(c)
|
42
|
+
["#{currency.iso_code} (#{currency.symbol})", currency.iso_code] if currency
|
43
|
+
end.compact.uniq
|
44
|
+
end
|
45
|
+
|
46
|
+
def all_currencies
|
47
|
+
Currency.table.inject([]) do |array, (id, attributes)|
|
48
|
+
array ||= []
|
49
|
+
array << ["#{attributes[:name]}" + (attributes[:symbol].blank? ? "" : " (#{attributes[:symbol]})"), attributes[:iso_code]]
|
50
|
+
array
|
51
|
+
end.sort{|x, y| x[0] <=> y[0]}
|
52
|
+
end
|
53
|
+
|
54
|
+
def price_to_currency(price, currency=RedmineCrm::Currency.find("USD"), options={})
|
55
|
+
return '' if price.blank?
|
56
|
+
|
57
|
+
if currency.is_a? String
|
58
|
+
currency = RedmineCrm::Currency.find(currency)
|
59
|
+
else
|
60
|
+
currency = RedmineCrm::Currency.find("USD")
|
61
|
+
end
|
62
|
+
RedmineCrm::Currency.format(price.to_f, currency)
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
unless ActionView::Base.included_modules.include?(RedmineCrm::MoneyHelper)
|
70
|
+
ActionView::Base.send(:include, RedmineCrm::MoneyHelper)
|
71
|
+
end
|
@@ -93,7 +93,7 @@ module RedmineCrm
|
|
93
93
|
|
94
94
|
return [] if related_models.blank?
|
95
95
|
|
96
|
-
related_ids = related_models.
|
96
|
+
related_ids = related_models.map{|c| c.id }.join(",")
|
97
97
|
Tag.select( #find(:all, options.merge({
|
98
98
|
"#{Tag.table_name}.*, COUNT(#{Tag.table_name}.id) AS count").joins(
|
99
99
|
"JOIN #{Tagging.table_name} ON #{Tagging.table_name}.taggable_type = '#{base_class.name}'
|
@@ -112,7 +112,7 @@ module RedmineCrm
|
|
112
112
|
# :conditions - A piece of SQL conditions to add to the query
|
113
113
|
def find_tagged_with(*args)
|
114
114
|
options = find_options_for_find_tagged_with(*args)
|
115
|
-
options.blank? ? [] : select(options[:select]).where(options[:conditions]).joins(options[:joins]).order(options[:order])
|
115
|
+
options.blank? ? [] : select(options[:select]).where(options[:conditions]).joins(options[:joins]).order(options[:order]).to_a
|
116
116
|
# find(:all, options)
|
117
117
|
end
|
118
118
|
alias_method :tagged_with, :find_tagged_with
|
@@ -148,7 +148,7 @@ module RedmineCrm
|
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
151
|
-
{ :select => "DISTINCT #{table_name}
|
151
|
+
{ :select => "DISTINCT #{table_name}.* ",
|
152
152
|
:joins => joins.join(" "),
|
153
153
|
:conditions => conditions.join(" AND ")
|
154
154
|
}.reverse_merge!(options)
|
data/lib/redmine_crm/version.rb
CHANGED
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class MoneyHelperTest < ActiveSupport::TestCase
|
4
|
+
include RedmineCrm::MoneyHelper
|
5
|
+
|
6
|
+
def test_price_to_currency
|
7
|
+
assert_equal '$3,265.65', price_to_currency(3265.65, "USD")
|
8
|
+
assert_equal "3.265,65 ₽", price_to_currency(3265.65, "RUB")
|
9
|
+
end
|
10
|
+
end
|
data/test/tag_test.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redmine_crm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RedmineCRM
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: plugins for Redmine
|
14
14
|
email:
|
@@ -27,8 +27,10 @@ files:
|
|
27
27
|
- lib/generators/redmine_crm_migration/templates/migration.rb
|
28
28
|
- lib/redmine_crm.rb
|
29
29
|
- lib/redmine_crm/currency.rb
|
30
|
+
- lib/redmine_crm/currency/formatting.rb
|
30
31
|
- lib/redmine_crm/currency/heuristics.rb
|
31
32
|
- lib/redmine_crm/currency/loader.rb
|
33
|
+
- lib/redmine_crm/money_helper.rb
|
32
34
|
- lib/redmine_crm/rcrm_acts_as_taggable.rb
|
33
35
|
- lib/redmine_crm/tag.rb
|
34
36
|
- lib/redmine_crm/tag_list.rb
|
@@ -45,6 +47,7 @@ files:
|
|
45
47
|
- test/fixtures/tags.yml
|
46
48
|
- test/fixtures/user.rb
|
47
49
|
- test/fixtures/users.yml
|
50
|
+
- test/money_helper_test.rb
|
48
51
|
- test/schema.rb
|
49
52
|
- test/tag_test.rb
|
50
53
|
- test/tagging_test.rb
|
@@ -84,6 +87,7 @@ test_files:
|
|
84
87
|
- test/fixtures/tags.yml
|
85
88
|
- test/fixtures/user.rb
|
86
89
|
- test/fixtures/users.yml
|
90
|
+
- test/money_helper_test.rb
|
87
91
|
- test/schema.rb
|
88
92
|
- test/tag_test.rb
|
89
93
|
- test/tagging_test.rb
|