redmine_crm 0.0.4

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.
@@ -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