redmine_crm 0.0.23 → 0.0.53

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/README.md +166 -33
  4. data/Rakefile +3 -12
  5. data/app/controllers/redmine_crm_controller.rb +26 -0
  6. data/app/views/redmine_crm/_money.html.erb +44 -0
  7. data/app/views/redmine_crm/settings.html.erb +10 -0
  8. data/bitbucket-pipelines.yml +54 -0
  9. data/config/currency_iso.json +12 -0
  10. data/config/locales/en.yml +13 -0
  11. data/config/locales/ru.yml +13 -0
  12. data/config/routes.rb +5 -0
  13. data/doc/CHANGELOG +123 -2
  14. data/lib/redmine_crm/acts_as_draftable/draft.rb +40 -0
  15. data/lib/redmine_crm/acts_as_draftable/rcrm_acts_as_draftable.rb +172 -0
  16. data/lib/redmine_crm/acts_as_list/list.rb +282 -0
  17. data/lib/redmine_crm/{rcrm_acts_as_taggable.rb → acts_as_taggable/rcrm_acts_as_taggable.rb} +112 -93
  18. data/lib/redmine_crm/acts_as_taggable/tag.rb +81 -0
  19. data/lib/redmine_crm/acts_as_taggable/tag_list.rb +111 -0
  20. data/lib/redmine_crm/acts_as_taggable/tagging.rb +16 -0
  21. data/lib/redmine_crm/acts_as_viewed/rcrm_acts_as_viewed.rb +274 -0
  22. data/lib/redmine_crm/{rcrm_acts_as_votable.rb → acts_as_votable/rcrm_acts_as_votable.rb} +15 -14
  23. data/lib/redmine_crm/acts_as_votable/rcrm_acts_as_voter.rb +20 -0
  24. data/lib/redmine_crm/{votable.rb → acts_as_votable/votable.rb} +54 -65
  25. data/lib/redmine_crm/{vote.rb → acts_as_votable/vote.rb} +6 -8
  26. data/lib/redmine_crm/{voter.rb → acts_as_votable/voter.rb} +29 -34
  27. data/lib/redmine_crm/assets_manager.rb +43 -0
  28. data/lib/redmine_crm/colors_helper.rb +192 -0
  29. data/lib/redmine_crm/compatibility/application_controller_patch.rb +33 -0
  30. data/lib/redmine_crm/currency/formatting.rb +5 -8
  31. data/lib/redmine_crm/currency/heuristics.rb +1 -1
  32. data/lib/redmine_crm/currency/loader.rb +5 -6
  33. data/lib/redmine_crm/currency.rb +28 -17
  34. data/lib/redmine_crm/engine.rb +4 -0
  35. data/lib/redmine_crm/helpers/external_assets_helper.rb +19 -0
  36. data/lib/redmine_crm/helpers/form_tag_helper.rb +76 -0
  37. data/lib/redmine_crm/helpers/tags_helper.rb +1 -3
  38. data/lib/redmine_crm/helpers/vote_helper.rb +29 -32
  39. data/lib/redmine_crm/hooks/views_layouts_hook.rb +11 -0
  40. data/lib/redmine_crm/liquid/drops/issues_drop.rb +191 -0
  41. data/lib/redmine_crm/liquid/drops/news_drop.rb +54 -0
  42. data/lib/redmine_crm/liquid/drops/projects_drop.rb +86 -0
  43. data/lib/redmine_crm/liquid/drops/time_entries_drop.rb +65 -0
  44. data/lib/redmine_crm/liquid/drops/users_drop.rb +68 -0
  45. data/lib/redmine_crm/liquid/filters/arrays.rb +187 -0
  46. data/lib/redmine_crm/liquid/filters/base.rb +217 -0
  47. data/lib/redmine_crm/liquid/filters/colors.rb +31 -0
  48. data/lib/redmine_crm/money_helper.rb +17 -18
  49. data/lib/redmine_crm/settings/money.rb +46 -0
  50. data/lib/redmine_crm/settings.rb +53 -0
  51. data/lib/redmine_crm/version.rb +1 -1
  52. data/lib/redmine_crm.rb +60 -21
  53. data/redmine_crm.gemspec +12 -6
  54. data/test/acts_as_draftable/draft_test.rb +29 -0
  55. data/test/acts_as_draftable/rcrm_acts_as_draftable_test.rb +178 -0
  56. data/test/{acts_as_taggable_test.rb → acts_as_taggable/rcrm_acts_as_taggable_test.rb} +117 -156
  57. data/test/acts_as_taggable/tag_list_test.rb +34 -0
  58. data/test/acts_as_taggable/tag_test.rb +72 -0
  59. data/test/acts_as_taggable/tagging_test.rb +15 -0
  60. data/test/{viewed_test.rb → acts_as_viewed/rcrm_acts_as_viewed_test.rb} +17 -15
  61. data/test/acts_as_votable/rcrm_acts_as_votable_test.rb +19 -0
  62. data/test/acts_as_votable/rcrm_acts_as_voter_test.rb +14 -0
  63. data/test/{votable_model_test.rb → acts_as_votable/votable_test.rb} +34 -5
  64. data/test/{voter_model_test.rb → acts_as_votable/voter_test.rb} +8 -8
  65. data/test/currency_test.rb +10 -10
  66. data/test/database.yml +14 -14
  67. data/test/fixtures/issues.yml +13 -1
  68. data/test/fixtures/news.yml +8 -0
  69. data/test/fixtures/projects.yml +10 -0
  70. data/test/fixtures/users.yml +6 -2
  71. data/test/liquid/drops/issues_drop_test.rb +34 -0
  72. data/test/liquid/drops/news_drop_test.rb +38 -0
  73. data/test/liquid/drops/projects_drop_test.rb +44 -0
  74. data/test/liquid/drops/uses_drop_test.rb +36 -0
  75. data/test/liquid/filters/arrays_filter_test.rb +31 -0
  76. data/test/liquid/filters/base_filter_test.rb +63 -0
  77. data/test/liquid/filters/colors_filter_test.rb +33 -0
  78. data/test/liquid/liquid_helper.rb +34 -0
  79. data/test/models/issue.rb +14 -0
  80. data/test/models/news.rb +3 -0
  81. data/test/models/project.rb +8 -0
  82. data/test/{fixtures → models}/user.rb +5 -1
  83. data/test/{fixtures → models}/vote_classes.rb +0 -21
  84. data/test/money_helper_test.rb +5 -5
  85. data/test/schema.rb +33 -10
  86. data/test/test_helper.rb +20 -72
  87. data/vendor/assets/images/money.png +0 -0
  88. data/vendor/assets/images/vcard.png +0 -0
  89. data/vendor/assets/javascripts/Chart.bundle.min.js +16 -0
  90. data/vendor/assets/javascripts/select2.js +2 -0
  91. data/vendor/assets/javascripts/select2_helpers.js +192 -0
  92. data/vendor/assets/stylesheets/money.css +3 -0
  93. data/vendor/assets/stylesheets/select2.css +424 -0
  94. metadata +190 -40
  95. data/lib/redmine_crm/rcrm_acts_as_viewed.rb +0 -287
  96. data/lib/redmine_crm/rcrm_acts_as_voter.rb +0 -27
  97. data/lib/redmine_crm/tag.rb +0 -81
  98. data/lib/redmine_crm/tag_list.rb +0 -112
  99. data/lib/redmine_crm/tagging.rb +0 -20
  100. data/test/fixtures/issue.rb +0 -14
  101. data/test/tag_test.rb +0 -64
  102. data/test/tagging_test.rb +0 -14
  103. data/test/votable_test.rb +0 -17
@@ -0,0 +1,187 @@
1
+ module RedmineCrm
2
+ module Liquid
3
+ module Filters
4
+ module Arrays
5
+
6
+ # Get the first element of the passed in array
7
+ #
8
+ # Example:
9
+ # {{ product.images | first | to_img }}
10
+ #
11
+ # {{ product.images | first: 3 }}
12
+ #
13
+ def first(array, count=1)
14
+ (count > 1 ? array.first(count) : array.first) if array.respond_to?(:first)
15
+ end
16
+
17
+ # Convert the input into json string
18
+ #
19
+ # input - The Array or Hash to be converted
20
+ #
21
+ # Returns the converted json string
22
+ def jsonify(input)
23
+ as_liquid(input).to_json
24
+ end
25
+
26
+ # Group an array of items by a property
27
+ #
28
+ # input - the inputted Enumerable
29
+ # property - the property
30
+ #
31
+ # Returns an array of Hashes, each looking something like this:
32
+ # {"name" => "larry"
33
+ # "items" => [...] } # all the items where `property` == "larry"
34
+ def group_by(input, property)
35
+ if groupable?(input)
36
+ input.group_by { |item| item_property(item, property).to_s }.each_with_object([]) do |item, array|
37
+ array << {
38
+ "name" => item.first,
39
+ "items" => item.last,
40
+ "size" => item.last.size
41
+ }
42
+ end
43
+ else
44
+ input
45
+ end
46
+ end
47
+
48
+ # Filter an array of objects
49
+ #
50
+ # input - the object array
51
+ # property - property within each object to filter by
52
+ # value - desired value
53
+ #
54
+ # Returns the filtered array of objects
55
+ def where(input, property, value, operator='==')
56
+ return input unless input.respond_to?(:select)
57
+ input = input.values if input.is_a?(Hash)
58
+ if operator == '=='
59
+ input.select do |object|
60
+ Array(item_property(object, property)).map(&:to_s).include?(value.to_s)
61
+ end || []
62
+ elsif operator == '<>'
63
+ input.select do |object|
64
+ !Array(item_property(object, property)).map(&:to_s).include?(value.to_s)
65
+ end || []
66
+ elsif operator == '>'
67
+ input.select do |object|
68
+ item_property_value = item_property(object, property)
69
+ item_property_value && item_property_value > value
70
+ end || []
71
+ elsif operator == '<'
72
+ input.select do |object|
73
+ item_property_value = item_property(object, property)
74
+ item_property_value && item_property_value < value
75
+ end || []
76
+ elsif operator == 'match'
77
+ input.select do |object|
78
+ Array(item_property(object, property)).map(&:to_s).any?{|i| i.match(value.to_s)}
79
+ end || []
80
+ elsif operator == 'any'
81
+ input.select do |object|
82
+ item_property(object, property).present?
83
+ end || []
84
+ elsif operator == 'none'
85
+ input.select do |object|
86
+ item_property(object, property).blank?
87
+ end || []
88
+ else
89
+ []
90
+ end
91
+ end
92
+
93
+ # Filter an array of objects by tags
94
+ #
95
+ # input - the object array
96
+ # tags - quoted tags list divided by comma
97
+ # match - (all- defaut, any, exclude)
98
+ #
99
+ # Returns the filtered array of objects
100
+ def tagged_with(input, tags, match='all')
101
+ return input unless input.respond_to?(:select)
102
+ input = input.values if input.is_a?(Hash)
103
+ tag_list = tags.is_a?(Array) ? tags.sort : tags.split(',').map(&:strip).sort
104
+ case match
105
+ when "all"
106
+ input.select do |object|
107
+ object.respond_to?(:tag_list) &&
108
+ (tag_list - Array(item_property(object, 'tag_list')).map(&:to_s).sort).empty?
109
+ end || []
110
+ when "any"
111
+ input.select do |object|
112
+ object.respond_to?(:tag_list) &&
113
+ (tag_list & Array(item_property(object, 'tag_list')).map(&:to_s).sort).any?
114
+ end || []
115
+ when "exclude"
116
+ input.select do |object|
117
+ object.respond_to?(:tag_list) &&
118
+ (tag_list & Array(item_property(object, 'tag_list')).map(&:to_s).sort).empty?
119
+ end || []
120
+ else
121
+ []
122
+ end
123
+ end
124
+
125
+ # Sort an array of objects
126
+ #
127
+ # input - the object array
128
+ # property - property within each object to filter by
129
+ # nils ('first' | 'last') - nils appear before or after non-nil values
130
+ #
131
+ # Returns the filtered array of objects
132
+ def sort(input, property = nil, nils = "first")
133
+ if input.nil?
134
+ raise ArgumentError, "Cannot sort a null object."
135
+ end
136
+ if property.nil?
137
+ input.sort
138
+ else
139
+ if nils == "first"
140
+ order = - 1
141
+ elsif nils == "last"
142
+ order = + 1
143
+ else
144
+ raise ArgumentError, "Invalid nils order: " \
145
+ "'#{nils}' is not a valid nils order. It must be 'first' or 'last'."
146
+ end
147
+
148
+ sort_input(input, property, order)
149
+ end
150
+ end
151
+
152
+ def pop(array, input = 1)
153
+ return array unless array.is_a?(Array)
154
+ new_ary = array.dup
155
+ new_ary.pop(input.to_i || 1)
156
+ new_ary
157
+ end
158
+
159
+ def push(array, input)
160
+ return array unless array.is_a?(Array)
161
+ new_ary = array.dup
162
+ new_ary.push(input)
163
+ new_ary
164
+ end
165
+
166
+ def shift(array, input = 1)
167
+ return array unless array.is_a?(Array)
168
+ new_ary = array.dup
169
+ new_ary.shift(input.to_i || 1)
170
+ new_ary
171
+ end
172
+
173
+ def unshift(array, input)
174
+ return array unless array.is_a?(Array)
175
+ new_ary = array.dup
176
+ new_ary.unshift(input)
177
+ new_ary
178
+ end
179
+
180
+ private
181
+
182
+ end # module ArrayFilters
183
+ end
184
+ end
185
+
186
+ ::Liquid::Template.register_filter(RedmineCrm::Liquid::Filters::Arrays)
187
+ end
@@ -0,0 +1,217 @@
1
+ require 'uri'
2
+ require 'rack'
3
+ require 'json'
4
+ require 'date'
5
+ require 'liquid'
6
+
7
+ module RedmineCrm
8
+ module Liquid
9
+ module Filters
10
+ module Base
11
+ include RedmineCrm::MoneyHelper
12
+
13
+ def textilize(input)
14
+ RedCloth3.new(input).to_html
15
+ end
16
+
17
+ def default(input, value)
18
+ input.blank? ? value : input
19
+ end
20
+
21
+ def underscore(input)
22
+ input.to_s.gsub(' ', '_').gsub('/', '_').underscore
23
+ end
24
+
25
+ def dasherize(input)
26
+ input.to_s.gsub(' ', '-').gsub('/', '-').dasherize
27
+ end
28
+
29
+ def shuffle(array)
30
+ array.to_a.shuffle
31
+ end
32
+
33
+ def random(input)
34
+ rand(input.to_i)
35
+ end
36
+
37
+ def md5(input)
38
+ Digest::MD5.hexdigest(input) unless input.blank?
39
+ end
40
+
41
+ # example:
42
+ # {{ "http:://www.example.com?key=hello world" | encode }}
43
+ #
44
+ # => http%3A%3A%2F%2Fwww.example.com%3Fkey%3Dhello+world
45
+ def encode(input)
46
+ ::Rack::Utils.escape(input)
47
+ end
48
+
49
+ # example:
50
+ # {{ today | plus_days: 2 }}
51
+ def plus_days(input, distanse)
52
+ return '' if input.nil?
53
+ days = distanse.to_i
54
+ input.to_date + days.days rescue 'Invalid date'
55
+ end
56
+
57
+ # example:
58
+ # {{ today | date_range: '2015-12-12' }}
59
+ def date_range(input, distanse)
60
+ return '' if input.nil?
61
+ (input.to_date - distanse.to_date).to_i rescue 'Invalid date'
62
+ end
63
+
64
+ # example:
65
+ # {{ now | utc }}
66
+ def utc(input)
67
+ return '' if input.nil?
68
+ input.to_time.utc rescue 'Invalid date'
69
+ end
70
+
71
+ def modulo(input, operand)
72
+ apply_operation(input, operand, :%)
73
+ end
74
+
75
+ def round(input, n = 0)
76
+ result = to_number(input).round(to_number(n))
77
+ result = result.to_f if result.is_a?(BigDecimal)
78
+ result = result.to_i if n == 0
79
+ result
80
+ end
81
+
82
+ def ceil(input)
83
+ to_number(input).ceil.to_i
84
+ end
85
+
86
+ def floor(input)
87
+ to_number(input).floor.to_i
88
+ end
89
+
90
+ def currency(input, currency_code = 'USD')
91
+ price_to_currency(input, currency_code, :converted => false)
92
+ end
93
+
94
+ def call_method(input, method_name)
95
+ if input.respond_to?(method_name)
96
+ input.method(method_name).call
97
+ end
98
+ end
99
+
100
+ def custom_field(input, field_name)
101
+ if input.respond_to?(:custom_field_values)
102
+ custom_value = input.custom_field_values.detect { |cfv| cfv.custom_field.name == field_name }
103
+ custom_value.custom_field.format.formatted_custom_value(nil, custom_value)
104
+ end
105
+ end
106
+
107
+ def custom_fields(input)
108
+ if input.respond_to?(:custom_field_values)
109
+ input.custom_field_values.map { |cfv| cfv.custom_field.name }
110
+ end
111
+ end
112
+
113
+ def attachment(input, file_name)
114
+ if input.respond_to?(:attachments)
115
+ if input.attachments.is_a?(Hash)
116
+ attachment = input.attachments[file_name]
117
+ else
118
+ attachment = input.attachments.detect{|a| a.file_name == file_name}
119
+ end
120
+ AttachmentDrop.new attachment if attachment
121
+ end
122
+ end
123
+
124
+ protected
125
+
126
+ # Convert an array of properties ('key:value') into a hash
127
+ # Ex: ['width:50', 'height:100'] => { :width => '50', :height => '100' }
128
+ def args_to_options(*args)
129
+ options = {}
130
+ args.flatten.each do |a|
131
+ if (a =~ /^(.*):(.*)$/)
132
+ options[$1.to_sym] = $2
133
+ end
134
+ end
135
+ options
136
+ end
137
+
138
+ # Write options (Hash) into a string according to the following pattern:
139
+ # <key1>="<value1>", <key2>="<value2", ...etc
140
+ def inline_options(options = {})
141
+ return '' if options.empty?
142
+ (options.stringify_keys.sort.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' '
143
+ end
144
+
145
+ def sort_input(input, property, order)
146
+ input.sort do |apple, orange|
147
+ apple_property = item_property(apple, property)
148
+ orange_property = item_property(orange, property)
149
+
150
+ if !apple_property.nil? && orange_property.nil?
151
+ - order
152
+ elsif apple_property.nil? && !orange_property.nil?
153
+ + order
154
+ else
155
+ apple_property <=> orange_property
156
+ end
157
+ end
158
+ end
159
+
160
+ def time(input)
161
+ case input
162
+ when Time
163
+ input.clone
164
+ when Date
165
+ input.to_time
166
+ when String
167
+ Time.parse(input) rescue Time.at(input.to_i)
168
+ when Numeric
169
+ Time.at(input)
170
+ else
171
+ raise Errors::InvalidDateError,
172
+ "Invalid Date: '#{input.inspect}' is not a valid datetime."
173
+ end.localtime
174
+ end
175
+
176
+ def groupable?(element)
177
+ element.respond_to?(:group_by)
178
+ end
179
+
180
+ def item_property(item, property)
181
+ if item.respond_to?(:to_liquid)
182
+ property.to_s.split(".").reduce(item.to_liquid) do |subvalue, attribute|
183
+ subvalue[attribute]
184
+ end
185
+ elsif item.respond_to?(:data)
186
+ item.data[property.to_s]
187
+ else
188
+ item[property.to_s]
189
+ end
190
+ end
191
+
192
+ def as_liquid(item)
193
+ case item
194
+ when Hash
195
+ pairs = item.map { |k, v| as_liquid([k, v]) }
196
+ Hash[pairs]
197
+ when Array
198
+ item.map { |i| as_liquid(i) }
199
+ else
200
+ if item.respond_to?(:to_liquid)
201
+ liquidated = item.to_liquid
202
+ # prevent infinite recursion for simple types (which return `self`)
203
+ if liquidated == item
204
+ item
205
+ else
206
+ as_liquid(liquidated)
207
+ end
208
+ else
209
+ item
210
+ end
211
+ end
212
+ end
213
+ end
214
+ ::Liquid::Template.register_filter(RedmineCrm::Liquid::Filters::Base)
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,31 @@
1
+ require 'liquid'
2
+
3
+ module RedmineCrm
4
+ module Liquid
5
+ module Filters
6
+ module Colors
7
+ def darken_color(input, value=0.4)
8
+ RedmineCrm::ColorsHelper.darken_color(input, value.to_f)
9
+ end
10
+
11
+ def lighten_color(input, value=0.6)
12
+ RedmineCrm::ColorsHelper.lighten_color(input, value.to_f)
13
+ end
14
+
15
+ def contrasting_text_color(input)
16
+ RedmineCrm::ColorsHelper.contrasting_text_color(input)
17
+ end
18
+
19
+ def hex_color(input)
20
+ RedmineCrm::ColorsHelper.hex_color(input)
21
+ end
22
+
23
+ def convert_to_brightness_value(input)
24
+ RedmineCrm::ColorsHelper.convert_to_brightness_value(input)
25
+ end
26
+
27
+ end
28
+ ::Liquid::Template.register_filter(RedmineCrm::Liquid::Filters::Colors)
29
+ end
30
+ end
31
+ end
@@ -1,16 +1,14 @@
1
- # byebug
2
- # require 'action_controller'
3
1
  require 'action_view'
2
+ require 'redmine_crm/settings/money'
4
3
 
5
4
  module RedmineCrm
6
5
  module MoneyHelper
7
-
8
6
  def object_price(obj, price_field = :price, options = {})
9
7
  options.merge!({:symbol => true})
10
8
  price_to_currency(obj.try(price_field), obj.currency, options).to_s if obj.respond_to?(:currency)
11
9
  end
12
10
 
13
- def prices_collection_by_currency(prices_collection, options={})
11
+ def prices_collection_by_currency(prices_collection, options = {})
14
12
  return [] if prices_collection.blank? || prices_collection == 0
15
13
  prices = prices_collection
16
14
  prices.reject!{|k, v| v.to_i == 0} if options[:hide_zeros]
@@ -20,17 +18,17 @@ module RedmineCrm
20
18
  def deal_currency_icon(currency)
21
19
  case currency.to_s.upcase
22
20
  when 'EUR'
23
- "icon-money-euro"
21
+ 'icon-money-euro'
24
22
  when 'GBP'
25
- "icon-money-pound"
23
+ 'icon-money-pound'
26
24
  when 'JPY'
27
- "icon-money-yen"
25
+ 'icon-money-yen'
28
26
  else
29
- "icon-money-dollar"
27
+ 'icon-money-dollar'
30
28
  end
31
29
  end
32
30
 
33
- def collection_for_currencies_select(default_currency = 'USD', major_currencies = %w(USD EUR GBP RUB CHF))
31
+ def collection_for_currencies_select(default_currency = RedmineCrm::Settings::Money.default_currency, major_currencies = RedmineCrm::Settings::Money.major_currencies)
34
32
  currencies = []
35
33
  currencies << default_currency.to_s unless default_currency.blank?
36
34
  currencies |= major_currencies
@@ -41,24 +39,25 @@ module RedmineCrm
41
39
  end
42
40
 
43
41
  def all_currencies
44
- Currency.table.inject([]) do |array, (id, attributes)|
42
+ Currency.table.inject([]) do |array, (_, attributes)|
45
43
  array ||= []
46
- array << ["#{attributes[:name]}" + (attributes[:symbol].blank? ? "" : " (#{attributes[:symbol]})"), attributes[:iso_code]]
44
+ array << [attributes[:name].to_s + (attributes[:symbol].blank? ? '' : " (#{attributes[:symbol]})"), attributes[:iso_code]]
47
45
  array
48
46
  end.sort{|x, y| x[0] <=> y[0]}
49
47
  end
50
48
 
51
- def price_to_currency(price, currency="USD", options={})
49
+ def price_to_currency(price, currency = RedmineCrm::Settings::Money.default_currency, options = {})
52
50
  return '' if price.blank?
53
51
 
54
- currency = "USD" unless currency.is_a?(String)
55
- currency = RedmineCrm::Currency.find(currency)
52
+ currency = 'USD' unless currency.is_a?(String)
53
+ default_options = {
54
+ decimal_mark: RedmineCrm::Settings::Money.decimal_separator,
55
+ thousands_separator: RedmineCrm::Settings::Money.thousands_delimiter
56
+ }.reject { |_k, v| v.nil? }
56
57
 
57
- return RedmineCrm::Currency.format(price.to_f, currency, options)# if currency
58
- price.to_s
58
+ currency = RedmineCrm::Currency.find(currency)
59
+ RedmineCrm::Currency.format(price.to_f, currency, default_options.merge(options))
59
60
  end
60
-
61
-
62
61
  end
63
62
  end
64
63
 
@@ -0,0 +1,46 @@
1
+ require 'redmine_crm/settings'
2
+
3
+ module RedmineCrm
4
+ class Settings
5
+ class Money
6
+ TAX_TYPE_EXCLUSIVE = 1
7
+ TAX_TYPE_INCLUSIVE = 2
8
+
9
+ class << self
10
+ def default_currency
11
+ RedmineCrm::Settings['default_currency'] || 'USD'
12
+ end
13
+
14
+ def major_currencies
15
+ currencies = RedmineCrm::Settings['major_currencies'].to_s.split(',').select { |c| !c.blank? }.map(&:strip)
16
+ currencies = %w[USD EUR GBP RUB CHF] if currencies.blank?
17
+ currencies.compact.uniq
18
+ end
19
+
20
+ def default_tax
21
+ RedmineCrm::Settings['default_tax'].to_f
22
+ end
23
+
24
+ def tax_type
25
+ ((['1', '2'] & [RedmineCrm::Settings['tax_type'].to_s]).first || TAX_TYPE_EXCLUSIVE).to_i
26
+ end
27
+
28
+ def tax_exclusive?
29
+ tax_type == TAX_TYPE_EXCLUSIVE
30
+ end
31
+
32
+ def thousands_delimiter
33
+ ([' ', ',', '.'] & [RedmineCrm::Settings['thousands_delimiter']]).first
34
+ end
35
+
36
+ def decimal_separator
37
+ ([',', '.'] & [RedmineCrm::Settings['decimal_separator']]).first
38
+ end
39
+
40
+ def disable_taxes?
41
+ RedmineCrm::Settings['disable_taxes'].to_i > 0
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ module RedmineCrm
2
+ class Settings
3
+ SECTIONS = {
4
+ 'money' => { id: :money, label: :label_redmine_crm_money, partial: 'money' }
5
+ }.freeze
6
+
7
+ class << self
8
+ def initialize_gem_settings
9
+ return if !Object.const_defined?('Setting') || Setting.respond_to?(:plugin_redmine_crm)
10
+
11
+ if Setting.respond_to?(:define_setting)
12
+ Setting.send(:define_setting, 'plugin_redmine_crm', 'default' => default_settings, 'serialized' => true)
13
+ elsif Setting.respond_to?(:available_settings)
14
+ Setting.available_settings['plugin_redmine_crm'] = { 'default' => default_settings, 'serialized' => true }
15
+ Setting.class.send(:define_method, 'plugin_redmine_crm', -> { Setting['plugin_redmine_crm'] })
16
+ Setting.class.send(:define_method, 'plugin_redmine_crm=', lambda do |val|
17
+ setting = find_or_default('plugin_redmine_crm')
18
+ setting.value = val || ''
19
+ @cached_settings['plugin_redmine_crm'] = nil
20
+ setting.save(validate: false)
21
+ setting.value
22
+ end)
23
+ end
24
+ @settings_initialized
25
+ end
26
+
27
+ # Use apply instead attrs assign because it can rewrite other attributes
28
+ def apply=(values)
29
+ initialize_gem_settings unless @settings_initialized
30
+
31
+ Setting.plugin_redmine_crm = Setting.plugin_redmine_crm.merge(values)
32
+ end
33
+
34
+ def values
35
+ initialize_gem_settings unless @settings_initialized
36
+ Object.const_defined?('Setting') ? Setting.plugin_redmine_crm : {}
37
+ end
38
+
39
+ def [](value)
40
+ initialize_gem_settings unless @settings_initialized
41
+ return Setting.plugin_redmine_crm[value] if Object.const_defined?('Setting')
42
+
43
+ nil
44
+ end
45
+
46
+ private
47
+
48
+ def default_settings
49
+ {}
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,3 @@
1
1
  module RedmineCrm
2
- VERSION = "0.0.23"
2
+ VERSION = '0.0.53'
3
3
  end