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.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/README.md +166 -33
- data/Rakefile +3 -12
- data/app/controllers/redmine_crm_controller.rb +26 -0
- data/app/views/redmine_crm/_money.html.erb +44 -0
- data/app/views/redmine_crm/settings.html.erb +10 -0
- data/bitbucket-pipelines.yml +54 -0
- data/config/currency_iso.json +12 -0
- data/config/locales/en.yml +13 -0
- data/config/locales/ru.yml +13 -0
- data/config/routes.rb +5 -0
- data/doc/CHANGELOG +123 -2
- data/lib/redmine_crm/acts_as_draftable/draft.rb +40 -0
- data/lib/redmine_crm/acts_as_draftable/rcrm_acts_as_draftable.rb +172 -0
- data/lib/redmine_crm/acts_as_list/list.rb +282 -0
- data/lib/redmine_crm/{rcrm_acts_as_taggable.rb → acts_as_taggable/rcrm_acts_as_taggable.rb} +112 -93
- data/lib/redmine_crm/acts_as_taggable/tag.rb +81 -0
- data/lib/redmine_crm/acts_as_taggable/tag_list.rb +111 -0
- data/lib/redmine_crm/acts_as_taggable/tagging.rb +16 -0
- data/lib/redmine_crm/acts_as_viewed/rcrm_acts_as_viewed.rb +274 -0
- data/lib/redmine_crm/{rcrm_acts_as_votable.rb → acts_as_votable/rcrm_acts_as_votable.rb} +15 -14
- data/lib/redmine_crm/acts_as_votable/rcrm_acts_as_voter.rb +20 -0
- data/lib/redmine_crm/{votable.rb → acts_as_votable/votable.rb} +54 -65
- data/lib/redmine_crm/{vote.rb → acts_as_votable/vote.rb} +6 -8
- data/lib/redmine_crm/{voter.rb → acts_as_votable/voter.rb} +29 -34
- data/lib/redmine_crm/assets_manager.rb +43 -0
- data/lib/redmine_crm/colors_helper.rb +192 -0
- data/lib/redmine_crm/compatibility/application_controller_patch.rb +33 -0
- data/lib/redmine_crm/currency/formatting.rb +5 -8
- data/lib/redmine_crm/currency/heuristics.rb +1 -1
- data/lib/redmine_crm/currency/loader.rb +5 -6
- data/lib/redmine_crm/currency.rb +28 -17
- data/lib/redmine_crm/engine.rb +4 -0
- data/lib/redmine_crm/helpers/external_assets_helper.rb +19 -0
- data/lib/redmine_crm/helpers/form_tag_helper.rb +76 -0
- data/lib/redmine_crm/helpers/tags_helper.rb +1 -3
- data/lib/redmine_crm/helpers/vote_helper.rb +29 -32
- data/lib/redmine_crm/hooks/views_layouts_hook.rb +11 -0
- data/lib/redmine_crm/liquid/drops/issues_drop.rb +191 -0
- data/lib/redmine_crm/liquid/drops/news_drop.rb +54 -0
- data/lib/redmine_crm/liquid/drops/projects_drop.rb +86 -0
- data/lib/redmine_crm/liquid/drops/time_entries_drop.rb +65 -0
- data/lib/redmine_crm/liquid/drops/users_drop.rb +68 -0
- data/lib/redmine_crm/liquid/filters/arrays.rb +187 -0
- data/lib/redmine_crm/liquid/filters/base.rb +217 -0
- data/lib/redmine_crm/liquid/filters/colors.rb +31 -0
- data/lib/redmine_crm/money_helper.rb +17 -18
- data/lib/redmine_crm/settings/money.rb +46 -0
- data/lib/redmine_crm/settings.rb +53 -0
- data/lib/redmine_crm/version.rb +1 -1
- data/lib/redmine_crm.rb +60 -21
- data/redmine_crm.gemspec +12 -6
- data/test/acts_as_draftable/draft_test.rb +29 -0
- data/test/acts_as_draftable/rcrm_acts_as_draftable_test.rb +178 -0
- data/test/{acts_as_taggable_test.rb → acts_as_taggable/rcrm_acts_as_taggable_test.rb} +117 -156
- data/test/acts_as_taggable/tag_list_test.rb +34 -0
- data/test/acts_as_taggable/tag_test.rb +72 -0
- data/test/acts_as_taggable/tagging_test.rb +15 -0
- data/test/{viewed_test.rb → acts_as_viewed/rcrm_acts_as_viewed_test.rb} +17 -15
- data/test/acts_as_votable/rcrm_acts_as_votable_test.rb +19 -0
- data/test/acts_as_votable/rcrm_acts_as_voter_test.rb +14 -0
- data/test/{votable_model_test.rb → acts_as_votable/votable_test.rb} +34 -5
- data/test/{voter_model_test.rb → acts_as_votable/voter_test.rb} +8 -8
- data/test/currency_test.rb +10 -10
- data/test/database.yml +14 -14
- data/test/fixtures/issues.yml +13 -1
- data/test/fixtures/news.yml +8 -0
- data/test/fixtures/projects.yml +10 -0
- data/test/fixtures/users.yml +6 -2
- data/test/liquid/drops/issues_drop_test.rb +34 -0
- data/test/liquid/drops/news_drop_test.rb +38 -0
- data/test/liquid/drops/projects_drop_test.rb +44 -0
- data/test/liquid/drops/uses_drop_test.rb +36 -0
- data/test/liquid/filters/arrays_filter_test.rb +31 -0
- data/test/liquid/filters/base_filter_test.rb +63 -0
- data/test/liquid/filters/colors_filter_test.rb +33 -0
- data/test/liquid/liquid_helper.rb +34 -0
- data/test/models/issue.rb +14 -0
- data/test/models/news.rb +3 -0
- data/test/models/project.rb +8 -0
- data/test/{fixtures → models}/user.rb +5 -1
- data/test/{fixtures → models}/vote_classes.rb +0 -21
- data/test/money_helper_test.rb +5 -5
- data/test/schema.rb +33 -10
- data/test/test_helper.rb +20 -72
- data/vendor/assets/images/money.png +0 -0
- data/vendor/assets/images/vcard.png +0 -0
- data/vendor/assets/javascripts/Chart.bundle.min.js +16 -0
- data/vendor/assets/javascripts/select2.js +2 -0
- data/vendor/assets/javascripts/select2_helpers.js +192 -0
- data/vendor/assets/stylesheets/money.css +3 -0
- data/vendor/assets/stylesheets/select2.css +424 -0
- metadata +190 -40
- data/lib/redmine_crm/rcrm_acts_as_viewed.rb +0 -287
- data/lib/redmine_crm/rcrm_acts_as_voter.rb +0 -27
- data/lib/redmine_crm/tag.rb +0 -81
- data/lib/redmine_crm/tag_list.rb +0 -112
- data/lib/redmine_crm/tagging.rb +0 -20
- data/test/fixtures/issue.rb +0 -14
- data/test/tag_test.rb +0 -64
- data/test/tagging_test.rb +0 -14
- 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
|
-
|
21
|
+
'icon-money-euro'
|
24
22
|
when 'GBP'
|
25
|
-
|
23
|
+
'icon-money-pound'
|
26
24
|
when 'JPY'
|
27
|
-
|
25
|
+
'icon-money-yen'
|
28
26
|
else
|
29
|
-
|
27
|
+
'icon-money-dollar'
|
30
28
|
end
|
31
29
|
end
|
32
30
|
|
33
|
-
def collection_for_currencies_select(default_currency =
|
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, (
|
42
|
+
Currency.table.inject([]) do |array, (_, attributes)|
|
45
43
|
array ||= []
|
46
|
-
array << [
|
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=
|
49
|
+
def price_to_currency(price, currency = RedmineCrm::Settings::Money.default_currency, options = {})
|
52
50
|
return '' if price.blank?
|
53
51
|
|
54
|
-
currency =
|
55
|
-
|
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
|
-
|
58
|
-
price.
|
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
|
data/lib/redmine_crm/version.rb
CHANGED