redmine_crm 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ module RedmineCrm
2
+ class RedmineCrmMigrationGenerator < Rails::Generator::Base
3
+ def manifest
4
+ record do |m|
5
+ m.migration_template 'migration.rb', 'db/migrate'
6
+ end
7
+ end
8
+
9
+ def file_name
10
+ "redmine_crm_migration"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ class RedminCrmMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.column :name, :string
5
+ end
6
+
7
+ create_table :taggings do |t|
8
+ t.column :tag_id, :integer
9
+ t.column :taggable_id, :integer
10
+
11
+ # You should make sure that the column created is
12
+ # long enough to store the required class names.
13
+ t.column :taggable_type, :string
14
+
15
+ t.column :created_at, :datetime
16
+ end
17
+
18
+ add_index :taggings, :tag_id
19
+ add_index :taggings, [:taggable_id, :taggable_type]
20
+ end
21
+
22
+ def self.down
23
+ drop_table :taggings
24
+ drop_table :tags
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ require "redmine_crm/version"
2
+
3
+ require "redmine_crm/tag"
4
+ require "redmine_crm/tag_list"
5
+ require "redmine_crm/tagging"
6
+ require "redmine_crm/rcrm_acts_as_taggable"
7
+ require "redmine_crm/tags_helper"
8
+ require "redmine_crm/currency"
@@ -0,0 +1,433 @@
1
+ require "json"
2
+ require "redmine_crm/currency/loader"
3
+ require "redmine_crm/currency/heuristics"
4
+ module RedmineCrm
5
+ # Represents a specific currency unit.
6
+ #
7
+ # @see http://en.wikipedia.org/wiki/Currency
8
+ # @see http://iso4217.net/
9
+ class Currency
10
+ include Comparable
11
+ extend Enumerable
12
+ extend RedmineCrm::Currency::Loader
13
+ extend RedmineCrm::Currency::Heuristics
14
+
15
+ # Thrown when a Currency has been registered without all the attributes
16
+ # which are required for the current action.
17
+ class MissingAttributeError < StandardError
18
+ def initialize(method, currency, attribute)
19
+ super(
20
+ "Can't call Currency.#{method} - currency '#{currency}' is missing "\
21
+ "the attribute '#{attribute}'"
22
+ )
23
+ end
24
+ end
25
+
26
+ # Thrown when an unknown currency is requested.
27
+ class UnknownCurrency < ArgumentError; end
28
+
29
+ class << self
30
+
31
+ # Lookup a currency with given +id+ an returns a +Currency+ instance on
32
+ # success, +nil+ otherwise.
33
+ #
34
+ # @param [String, Symbol, #to_s] id Used to look into +table+ and
35
+ # retrieve the applicable attributes.
36
+ #
37
+ # @return [Money::Currency]
38
+ #
39
+ # @example
40
+ # Money::Currency.find(:eur) #=> #<Money::Currency id: eur ...>
41
+ # Money::Currency.find(:foo) #=> nil
42
+ def find(id)
43
+ id = id.to_s.downcase.to_sym
44
+ new(id)
45
+ rescue UnknownCurrency
46
+ nil
47
+ end
48
+
49
+ # Lookup a currency with given +num+ as an ISO 4217 numeric and returns an
50
+ # +Currency+ instance on success, +nil+ otherwise.
51
+ #
52
+ # @param [#to_s] num used to look into +table+ in +iso_numeric+ and find
53
+ # the right currency id.
54
+ #
55
+ # @return [Money::Currency]
56
+ #
57
+ # @example
58
+ # Money::Currency.find_by_iso_numeric(978) #=> #<Money::Currency id: eur ...>
59
+ # Money::Currency.find_by_iso_numeric('001') #=> nil
60
+ def find_by_iso_numeric(num)
61
+ num = num.to_s
62
+ id, _ = self.table.find{|key, currency| currency[:iso_numeric] == num}
63
+ new(id)
64
+ rescue UnknownCurrency
65
+ nil
66
+ end
67
+
68
+ # Wraps the object in a +Currency+ unless it's already a +Currency+
69
+ # object.
70
+ #
71
+ # @param [Object] object The object to attempt and wrap as a +Currency+
72
+ # object.
73
+ #
74
+ # @return [Money::Currency]
75
+ #
76
+ # @example
77
+ # c1 = Money::Currency.new(:usd)
78
+ # Money::Currency.wrap(nil) #=> nil
79
+ # Money::Currency.wrap(c1) #=> #<Money::Currency id: usd ...>
80
+ # Money::Currency.wrap("usd") #=> #<Money::Currency id: usd ...>
81
+ def wrap(object)
82
+ if object.nil?
83
+ nil
84
+ elsif object.is_a?(Currency)
85
+ object
86
+ else
87
+ Currency.new(object)
88
+ end
89
+ end
90
+
91
+ # List of known currencies.
92
+ #
93
+ # == monetary unit
94
+ # The standard unit of value of a currency, as the dollar in the United States or the peso in Mexico.
95
+ # http://www.answers.com/topic/monetary-unit
96
+ # == fractional monetary unit, subunit
97
+ # A monetary unit that is valued at a fraction (usually one hundredth) of the basic monetary unit
98
+ # http://www.answers.com/topic/fractional-monetary-unit-subunit
99
+ #
100
+ # See http://en.wikipedia.org/wiki/List_of_circulating_currencies and
101
+ # http://search.cpan.org/~tnguyen/Locale-Currency-Format-1.28/Format.pm
102
+ def table
103
+ @table ||= load_currencies
104
+ end
105
+
106
+ # List the currencies imported and registered
107
+ # @return [Array]
108
+ #
109
+ # @example
110
+ # Money::Currency.iso_codes()
111
+ # [#<Currency ..USD>, 'CAD', 'EUR']...
112
+ def all
113
+ table.keys.map do |curr|
114
+ c = Currency.new(curr)
115
+ if c.priority.nil?
116
+ raise MissingAttributeError.new(:all, c.id, :priority)
117
+ end
118
+ c
119
+ end.sort_by(&:priority)
120
+ end
121
+
122
+ # We need a string-based validator before creating an unbounded number of
123
+ # symbols.
124
+ # http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol#11
125
+ # https://github.com/RubyMoney/money/issues/132
126
+ #
127
+ # @return [Set]
128
+ def stringified_keys
129
+ @stringified_keys ||= stringify_keys
130
+ end
131
+
132
+ # Register a new currency
133
+ #
134
+ # @param curr [Hash] information about the currency
135
+ # @option priority [Numeric] a numerical value you can use to sort/group
136
+ # the currency list
137
+ # @option iso_code [String] the international 3-letter code as defined
138
+ # by the ISO 4217 standard
139
+ # @option iso_numeric [Integer] the international 3-digit code as
140
+ # defined by the ISO 4217 standard
141
+ # @option name [String] the currency name
142
+ # @option symbol [String] the currency symbol (UTF-8 encoded)
143
+ # @option subunit [String] the name of the fractional monetary unit
144
+ # @option subunit_to_unit [Numeric] the proportion between the unit and
145
+ # the subunit
146
+ # @option separator [String] character between the whole and fraction
147
+ # amounts
148
+ # @option delimiter [String] character between each thousands place
149
+ def register(curr)
150
+ key = curr.fetch(:iso_code).downcase.to_sym
151
+ table if !@table
152
+ @table[key] = curr
153
+ @stringified_keys = stringify_keys
154
+ end
155
+
156
+
157
+ # Unregister a currency.
158
+ #
159
+ # @param [Object] curr A Hash with the key `:iso_code`, or the ISO code
160
+ # as a String or Symbol.
161
+ #
162
+ # @return [Boolean] true if the currency previously existed, false
163
+ # if it didn't.
164
+ def unregister(curr)
165
+ if curr.is_a?(Hash)
166
+ key = curr.fetch(:iso_code).downcase.to_sym
167
+ else
168
+ key = curr.downcase.to_sym
169
+ end
170
+ existed = @table.delete(key)
171
+ @stringified_keys = stringify_keys
172
+ existed ? true : false
173
+ end
174
+
175
+
176
+ def each
177
+ all.each { |c| yield(c) }
178
+ end
179
+
180
+
181
+ private
182
+
183
+ def stringify_keys
184
+ table.keys.each_with_object(Set.new) { |k, set| set.add(k.to_s.downcase) }
185
+ end
186
+ end
187
+
188
+ # @!attribute [r] id
189
+ # @return [Symbol] The symbol used to identify the currency, usually THE
190
+ # lowercase +iso_code+ attribute.
191
+ # @!attribute [r] priority
192
+ # @return [Integer] A numerical value you can use to sort/group the
193
+ # currency list.
194
+ # @!attribute [r] iso_code
195
+ # @return [String] The international 3-letter code as defined by the ISO
196
+ # 4217 standard.
197
+ # @!attribute [r] iso_numeric
198
+ # @return [String] The international 3-numeric code as defined by the ISO
199
+ # 4217 standard.
200
+ # @!attribute [r] name
201
+ # @return [String] The currency name.
202
+ # @!attribute [r] symbol
203
+ # @return [String] The currency symbol (UTF-8 encoded).
204
+ # @!attribute [r] disambiguate_symbol
205
+ # @return [String] Alternative currency used if symbol is ambiguous
206
+ # @!attribute [r] html_entity
207
+ # @return [String] The html entity for the currency symbol
208
+ # @!attribute [r] subunit
209
+ # @return [String] The name of the fractional monetary unit.
210
+ # @!attribute [r] subunit_to_unit
211
+ # @return [Integer] The proportion between the unit and the subunit
212
+ # @!attribute [r] decimal_mark
213
+ # @return [String] The decimal mark, or character used to separate the
214
+ # whole unit from the subunit.
215
+ # @!attribute [r] The
216
+ # @return [String] character used to separate thousands grouping of the
217
+ # whole unit.
218
+ # @!attribute [r] symbol_first
219
+ # @return [Boolean] Should the currency symbol precede the amount, or
220
+ # should it come after?
221
+ # @!attribute [r] smallest_denomination
222
+ # @return [Integer] Smallest amount of cash possible (in the subunit of
223
+ # this currency)
224
+
225
+ attr_reader :id, :priority, :iso_code, :iso_numeric, :name, :symbol,
226
+ :disambiguate_symbol, :html_entity, :subunit, :subunit_to_unit, :decimal_mark,
227
+ :thousands_separator, :symbol_first, :smallest_denomination
228
+
229
+ alias_method :separator, :decimal_mark
230
+ alias_method :delimiter, :thousands_separator
231
+ alias_method :eql?, :==
232
+
233
+ # Create a new +Currency+ object.
234
+ #
235
+ # @param [String, Symbol, #to_s] id Used to look into +table+ and retrieve
236
+ # the applicable attributes.
237
+ #
238
+ # @return [Money::Currency]
239
+ #
240
+ # @example
241
+ # Money::Currency.new(:usd) #=> #<Money::Currency id: usd ...>
242
+ def initialize(id)
243
+ id = id.to_s.downcase
244
+ unless self.class.stringified_keys.include?(id)
245
+ raise UnknownCurrency, "Unknown currency '#{id}'"
246
+ end
247
+ @id = id.to_sym
248
+ initialize_data!
249
+ end
250
+
251
+ # Compares +self+ with +other_currency+ against the value of +priority+
252
+ # attribute.
253
+ #
254
+ # @param [Money::Currency] other_currency The currency to compare to.
255
+ #
256
+ # @return [-1,0,1] -1 if less than, 0 is equal to, 1 if greater than
257
+ #
258
+ # @example
259
+ # c1 = Money::Currency.new(:usd)
260
+ # c2 = Money::Currency.new(:jpy)
261
+ # c1 <=> c2 #=> 1
262
+ # c2 <=> c1 #=> -1
263
+ # c1 <=> c1 #=> 0
264
+ def <=>(other_currency)
265
+ # <=> returns nil when one of the values is nil
266
+ comparison = self.priority <=> other_currency.priority || 0
267
+
268
+ if comparison == 0
269
+ self.id <=> other_currency.id
270
+ else
271
+ comparison
272
+ end
273
+ end
274
+
275
+ # Compares +self+ with +other_currency+ and returns +true+ if the are the
276
+ # same or if their +id+ attributes match.
277
+ #
278
+ # @param [Money::Currency] other_currency The currency to compare to.
279
+ #
280
+ # @return [Boolean]
281
+ #
282
+ # @example
283
+ # c1 = Money::Currency.new(:usd)
284
+ # c2 = Money::Currency.new(:jpy)
285
+ # c1 == c1 #=> true
286
+ # c1 == c2 #=> false
287
+ def ==(other_currency)
288
+ self.equal?(other_currency) || compare_ids(other_currency)
289
+ end
290
+
291
+ def compare_ids(other_currency)
292
+ other_currency_id = if other_currency.is_a?(Currency)
293
+ other_currency.id.to_s.downcase
294
+ else
295
+ other_currency.to_s.downcase
296
+ end
297
+ self.id.to_s.downcase == other_currency_id
298
+ end
299
+ private :compare_ids
300
+
301
+ # Returns a Fixnum hash value based on the +id+ attribute in order to use
302
+ # functions like & (intersection), group_by, etc.
303
+ #
304
+ # @return [Fixnum]
305
+ #
306
+ # @example
307
+ # Money::Currency.new(:usd).hash #=> 428936
308
+ def hash
309
+ id.hash
310
+ end
311
+
312
+ # Returns a human readable representation.
313
+ #
314
+ # @return [String]
315
+ #
316
+ # @example
317
+ # Money::Currency.new(:usd) #=> #<Currency id: usd ...>
318
+ def inspect
319
+ "#<#{self.class.name} id: #{id}, priority: #{priority}, symbol_first: #{symbol_first}, thousands_separator: #{thousands_separator}, html_entity: #{html_entity}, decimal_mark: #{decimal_mark}, name: #{name}, symbol: #{symbol}, subunit_to_unit: #{subunit_to_unit}, exponent: #{exponent}, iso_code: #{iso_code}, iso_numeric: #{iso_numeric}, subunit: #{subunit}, smallest_denomination: #{smallest_denomination}>"
320
+ end
321
+
322
+ # Returns a string representation corresponding to the upcase +id+
323
+ # attribute.
324
+ #
325
+ # --
326
+ # DEV: id.to_s.upcase corresponds to iso_code but don't use ISO_CODE for consistency.
327
+ #
328
+ # @return [String]
329
+ #
330
+ # @example
331
+ # Money::Currency.new(:usd).to_s #=> "USD"
332
+ # Money::Currency.new(:eur).to_s #=> "EUR"
333
+ def to_s
334
+ id.to_s.upcase
335
+ end
336
+
337
+ # Returns a string representation corresponding to the upcase +id+
338
+ # attribute. Useful in cases where only implicit conversions are made.
339
+ #
340
+ # @return [String]
341
+ #
342
+ # @example
343
+ # Money::Currency.new(:usd).to_str #=> "USD"
344
+ # Money::Currency.new(:eur).to_str #=> "EUR"
345
+ def to_str
346
+ id.to_s.upcase
347
+ end
348
+
349
+ # Returns a symbol representation corresponding to the upcase +id+
350
+ # attribute.
351
+ #
352
+ # @return [Symbol]
353
+ #
354
+ # @example
355
+ # Money::Currency.new(:usd).to_sym #=> :USD
356
+ # Money::Currency.new(:eur).to_sym #=> :EUR
357
+ def to_sym
358
+ id.to_s.upcase.to_sym
359
+ end
360
+
361
+ # Conversation to +self+.
362
+ #
363
+ # @return [self]
364
+ def to_currency
365
+ self
366
+ end
367
+
368
+ # Returns currency symbol or iso code for currencies with no symbol.
369
+ #
370
+ # @return [String]
371
+ def code
372
+ symbol || iso_code
373
+ end
374
+
375
+ def symbol_first?
376
+ !!@symbol_first
377
+ end
378
+
379
+ # Returns the number of digits after the decimal separator.
380
+ #
381
+ # @return [Float]
382
+ def exponent
383
+ Math.log10(@subunit_to_unit)
384
+ end
385
+
386
+ # Cache decimal places for subunit_to_unit values. Common ones pre-cached.
387
+ def self.decimal_places_cache
388
+ @decimal_places_cache ||= {1 => 0, 10 => 1, 100 => 2, 1000 => 3}
389
+ end
390
+
391
+ # The number of decimal places needed.
392
+ #
393
+ # @return [Integer]
394
+ def decimal_places
395
+ cache[subunit_to_unit] ||= calculate_decimal_places(subunit_to_unit)
396
+ end
397
+
398
+ private
399
+
400
+ def cache
401
+ self.class.decimal_places_cache
402
+ end
403
+
404
+ # If we need to figure out how many decimal places we need we
405
+ # use repeated integer division.
406
+ def calculate_decimal_places(num)
407
+ i = 1
408
+ while num >= 10
409
+ num /= 10
410
+ i += 1 if num >= 10
411
+ end
412
+ i
413
+ end
414
+
415
+ def initialize_data!
416
+ data = self.class.table[@id]
417
+ @alternate_symbols = data[:alternate_symbols]
418
+ @decimal_mark = data[:decimal_mark]
419
+ @disambiguate_symbol = data[:disambiguate_symbol]
420
+ @html_entity = data[:html_entity]
421
+ @iso_code = data[:iso_code]
422
+ @iso_numeric = data[:iso_numeric]
423
+ @name = data[:name]
424
+ @priority = data[:priority]
425
+ @smallest_denomination = data[:smallest_denomination]
426
+ @subunit = data[:subunit]
427
+ @subunit_to_unit = data[:subunit_to_unit]
428
+ @symbol = data[:symbol]
429
+ @symbol_first = data[:symbol_first]
430
+ @thousands_separator = data[:thousands_separator]
431
+ end
432
+ end
433
+ end