spree_multi_currency 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data.tar.gz.sig +3 -0
  2. data/.gitignore +5 -0
  3. data/Gemfile +11 -0
  4. data/LICENSE +0 -0
  5. data/README.markdown +196 -0
  6. data/Rakefile +28 -0
  7. data/Versionfile +9 -0
  8. data/app/controllers/spree/admin/currencies_controller.rb +11 -0
  9. data/app/controllers/spree/admin/currency_converters_controller.rb +4 -0
  10. data/app/controllers/spree/base_controller_decorator.rb +12 -0
  11. data/app/controllers/spree/currency_controller.rb +16 -0
  12. data/app/helpers/number_helper_decorator.rb +40 -0
  13. data/app/models/spree/adjustment_decorator.rb +4 -0
  14. data/app/models/spree/currency.rb +119 -0
  15. data/app/models/spree/currency_converter.rb +14 -0
  16. data/app/models/spree/line_item_decorator.rb +13 -0
  17. data/app/models/spree/order_decorator.rb +83 -0
  18. data/app/models/spree/variant_decorator.rb +22 -0
  19. data/app/overrides/add_currencies_admin_configurations_menu.rb +10 -0
  20. data/app/views/spree/admin/currencies/_form.html.erb +40 -0
  21. data/app/views/spree/admin/currencies/edit.html.erb +16 -0
  22. data/app/views/spree/admin/currencies/index.html.erb +44 -0
  23. data/app/views/spree/admin/currencies/new.html.erb +16 -0
  24. data/app/views/spree/admin/currency_converters/_form.html.erb +35 -0
  25. data/app/views/spree/admin/currency_converters/edit.html.erb +16 -0
  26. data/app/views/spree/admin/currency_converters/index.html.erb +42 -0
  27. data/app/views/spree/admin/currency_converters/new.html.erb +16 -0
  28. data/config/cucumber.yml +10 -0
  29. data/config/locales/currencies.yml +51 -0
  30. data/config/locales/en_multi_currency.yml +28 -0
  31. data/config/locales/ru_multi_currency.yml +26 -0
  32. data/config/routes.rb +8 -0
  33. data/db/migrate/20101109134351_create_currencies.rb +25 -0
  34. data/db/migrate/20101109134453_create_currency_converters.rb +15 -0
  35. data/db/sample/currencies.yml +1603 -0
  36. data/db/sample/currency_converters.yml +689 -0
  37. data/db/seeds.rb +2 -0
  38. data/features/products.feature +32 -0
  39. data/features/step_definitions/product.rb +124 -0
  40. data/features/step_definitions/ror_ringer.jpeg +0 -0
  41. data/features/support/env.rb +16 -0
  42. data/features/support/paths.rb +42 -0
  43. data/lib/generators/spree_multi_currency/install/install_generator.rb +29 -0
  44. data/lib/spree_multi_currency.rb +40 -0
  45. data/lib/spree_multi_currency/engine.rb +34 -0
  46. data/lib/tasks/spree_multi_currency.rake +99 -0
  47. data/script/rails +5 -0
  48. data/spec/changing_currency_spec.rb +18 -0
  49. data/spec/spec_helper.rb +17 -0
  50. data/spree_multi_currency.gemspec +32 -0
  51. metadata +226 -0
  52. metadata.gz.sig +1 -0
data.tar.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ 4� �A5�-8�cQb���E$!� ���DA+Cr{|����N��:�4��f�tt��~��l.��k?��A���~
2
+ ��ڑ�����9{�T7ab�SSH6e_3íS9]~K ��3��J������tuh�&�h�p��7�*��;� ��g�O��kD��� ��ݡ�V�A��N]��g�j��k͢.K�� j��mgn\!؛vB�j���a�ﺴ�%D�p}Y�
3
+ z�Y�X���+dP�
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ spec/dummy
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source :rubygems
2
+
3
+ gem 'sqlite3'
4
+
5
+ group :test do
6
+ gem 'rspec-rails', '= 2.6.1'
7
+ gem 'factory_girl'
8
+ gem 'faker'
9
+ end
10
+
11
+ gemspec
data/LICENSE ADDED
File without changes
data/README.markdown ADDED
@@ -0,0 +1,196 @@
1
+ # Spree Multi-Currency
2
+
3
+ Support different currency and recalculate price from one to another
4
+
5
+ Installation
6
+ ---------
7
+ Add to Gemfile
8
+
9
+ gem "spree_multi_currency", :git => "git://github.com/pronix/spree-multi-currency.git"
10
+
11
+ Run
12
+ ---
13
+ Install the migrations for two new tables (currencies and currency conversion rates):
14
+
15
+ rake spree_multi_currency:install:migrations
16
+ rake db:migrate
17
+
18
+ Load currencies:
19
+ ---------------
20
+ Load up the list of all international currencies with corresponding codes:
21
+
22
+ rake spree_multi_currency:currency:iso4217 # Load currency ISO4217 table from Wikipedia http://en.wikipedia.org/wiki/ISO_4217
23
+ rake spree_multi_currency:currency:okv # Central Bank of Russian Federation
24
+
25
+ This step is not obligatory, i.e. you can manually fill up the 'currencies' table, but it's more practical to load the list with rake task above (and be sure the codes are OK), and then remove the currencies you don't want to support.
26
+
27
+
28
+ If you want get amount in base currency use base_total
29
+
30
+ Load rates:
31
+ ----------
32
+ *Warning* Rates are being calculated relative to currency configured as 'basic'. It is therefore obligatory to visit Spree admin panel (or use Rails console) and edit one of the currencies to be the 'basic' one.
33
+
34
+ Basic currency is also the one considered to be stored as product prices, shipment rates etc., from which all the other ones will be calculated using the rates.
35
+
36
+ After setting the basic currency, time to load the rates using one of the rake tasks below. There are three sources of conversion rates supported by this extension:
37
+
38
+ 1. Rates from Central Bank of Russian Federation http://www.cbr.ru. These assume Russian Ruble is your basic currency:
39
+
40
+ rake spree_multi_currency:rates:cbr
41
+
42
+ 2. Rates from European Central Bank. These assume Euro is your basic currency:
43
+
44
+ rake spree_multi_currency:rates:ecb
45
+
46
+ 3. Rates from Google.
47
+
48
+ rake spree_multi_currency:rates:google[currency]
49
+
50
+ The argument in square brackets is the iso code of your basic currency, so to load rates when US Dollar is your basic currency, use
51
+
52
+ rake spree_multi_currency:rates:google[usd]
53
+
54
+ There's also an optional square-bracket-enclosed parameter "load_currencies" for :rates tasks above, but it just loads up currencies table from Wikipedia, so is not needed at this point.
55
+
56
+ Settings
57
+ ---------
58
+ In Spree Admin Panel, Configuration tab, two new options appear: Currency Settings and Currency Converters.
59
+
60
+ It's best to leave Currency Converters as-is, to be populated and updated by rake spree_multi_currency:rates tasks.
61
+
62
+ Within Currency Settings, like mentioned above, it is essential to set one currency as the Basic one. It's also necessary to set currency's locale for every locale you want to support (again, one locale - one currency).
63
+ Feel free to go through currencies and delete the ones you don't want to support -- it will make everything easier to manage (and the :rates rake tasks will execute faster).
64
+
65
+ Changing Currency in store
66
+ --------------------------
67
+ Self-explanatory:
68
+
69
+ http://[domain]/currency/[isocode]
70
+ <%= link_to "eur", currency_path(:eur) %>
71
+
72
+
73
+ Translation files
74
+ --------------------
75
+ To have custom currency symbols and formatters, you need to have a corresponding entry in one of locale files, with main key like currency_XXX, where XXX is the 3-letter iso code of given currency.
76
+
77
+ If you won't have it, all the other currencies will be rendered using default formatters and symbols, which can (will) lead to confusion and inconsistency. It is recommended to create locale entries for all currencies you want to support at your store and delete all the other currencies.
78
+
79
+ Example for usd, eur
80
+
81
+ --
82
+ currency_USD: &usd
83
+ number:
84
+ currency:
85
+ format:
86
+ format: "%u%n"
87
+ unit: "$"
88
+ separator: "."
89
+ delimiter: ","
90
+ precision: 2
91
+ significant: false
92
+ strip_insignificant_zeros: false
93
+
94
+ currency_EUR:
95
+ <<: *usd
96
+ number:
97
+ currency:
98
+ format:
99
+ format: "%u%n"
100
+ unit: "€"
101
+
102
+
103
+
104
+
105
+ = Multi Currency
106
+
107
+ Support different currency and recalculate price from one to another
108
+ ===========================================
109
+ Installation
110
+ ---------
111
+ Add to Gemfile
112
+ gem "spree_multi_currency", :git => "git://github.com/pronix/spree-multi-currency.git"
113
+
114
+ Run
115
+ ---
116
+ rake spree_multi_currency:install:migrations
117
+ rake db:migrate
118
+
119
+ Load currencies:
120
+ ---------------
121
+ rake spree_multi_currency:currency:iso4217 # Load currency ISO4217 http://en.wikipedia.org/wiki/ISO_4217
122
+ rake spree_multi_currency:currency:okv # Общероссийский классификатор валют...
123
+
124
+ Load rates:
125
+ ----------
126
+ rake spree_multi_currency:rates:cbr # Курс Сбербанка РФ http://www.cbr.ru
127
+ rake "spree_multi_currency:rates:ecb[load_currencies]" # Rates from European Central Bank
128
+ for example rake spree_multi_currency:rates:google[USD]
129
+ rake "spree_multi_currency:rates:google[currency,load_currencies]" # Rates from Google
130
+
131
+
132
+ Settings
133
+ ---------
134
+ In admin block, configuration menu add two tables currency and currency conversion rate
135
+ In reference currency enters the list of currencies, indicate if one of the major currencies (in the currency keeps all prices). Each currency assign corresponding locale.
136
+ In Exchange Rates, provides information on the price of the currency on a specified date to the basic currency(from russian central bank).
137
+ In the exchange rates set date, currency, and face value of the currency in the base currency.
138
+ To fill in the exchange rate, you can use task for download exchange rates from the site of the Central Bank (http://www.cbr.ru):
139
+ rake spree_multi_currencies:rates:cbr, as in this problem, loading the list of currencies.
140
+
141
+ В справочнике Валюты заносим список валют, указываем одну из валют основной (в этой валюте хранятся все цены). Каждой валюте назначаем соответствующую локаль.
142
+ В справчнике Курсы валют, содержиться информация о цене валюты на определенную дату к основной валюте.
143
+ В курсе валют указываеться дата, валюта, номинал и стоимость валюты в основной валюте.
144
+ Для заполнения курса валют, можно воспользоватьбся задачей загрузки курса валют с сайта ЦБ(http://www.cbr.ru):
145
+ rake spree_multi_currencies:rates:cbr, так же в этой задаче идет загрузка списка валют.
146
+
147
+ Смена валюты
148
+ -------------
149
+ По умолчанию валюта выбирается от текущей локали сайта.
150
+ Так же можно сменить локаль по адресу http://[domain]/currency/[isocode], <%= link_to "eur", currency_path(:eur) %>
151
+
152
+ isocode: eur, usd, rub (цифровой код прописанные в справочнике валюты)
153
+ После смены валюты через url перестает работать смена валюты на основание текущей локали.
154
+
155
+ Формат вывода валюты
156
+ --------------------
157
+ Формат для валюты прописан в локализации, для каждой валюты нужно описать свою локализацию (прописаны eur, usd, rub):
158
+ Пример для usd, eur
159
+ ---
160
+ currency_USD: &usd
161
+ number:
162
+ currency:
163
+ format:
164
+ format: "%u%n"
165
+ unit: "$"
166
+ separator: "."
167
+ delimiter: ","
168
+ precision: 2
169
+ significant: false
170
+ strip_insignificant_zeros: false
171
+
172
+ currency_EUR:
173
+ <<: *usd
174
+ number:
175
+ currency:
176
+ format:
177
+ format: "%u%n"
178
+ unit: "€"
179
+
180
+
181
+ For tests
182
+ _________________________
183
+
184
+ extention require store in ./spree
185
+ in Rakefile defined
186
+ # require define path to spree project
187
+ ENV['SPREE_GEM_PATH'] = "/home/dima/project/spree"
188
+ # or define spree as gem in Gemfile
189
+ # and decomment this
190
+ # gemfile = Pathname.new("Gemfile").expand_path
191
+ # lockfile = gemfile.dirname.join('Gemfile.lock')
192
+ # definition = Bundler::Definition.build(gemfile, lockfile, nil)
193
+ # sc=definition.index.search "spree"
194
+ # ENV['SPREE_GEM_PATH'] = sc[0].loaded_from.gsub(/\/[a-z_]*.gemspec$/,'')
195
+
196
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/packagetask'
4
+ require 'rubygems/package_task'
5
+ require 'rspec/core/rake_task'
6
+ require 'spree/core/testing_support/common_rake'
7
+
8
+ RSpec::Core::RakeTask.new
9
+
10
+ task :default => :spec
11
+
12
+ spec = eval(File.read('spree_multi_currency.gemspec'))
13
+
14
+ Gem::PackageTask.new(spec) do |p|
15
+ p.gem_spec = spec
16
+ end
17
+
18
+ desc "Release to gemcutter"
19
+ task :release do
20
+ version = File.read(File.expand_path("../../SPREE_VERSION", __FILE__)).strip
21
+ cmd = "cd pkg && gem push spree-multi-currency_-#{version}.gem"; puts cmd; system cmd
22
+ end
23
+
24
+ desc "Generates a dummy app for testing"
25
+ task :test_app do
26
+ ENV['LIB_NAME'] = 'spree_multi_currency'
27
+ Rake::Task['common:test_app'].invoke
28
+ end
data/Versionfile ADDED
@@ -0,0 +1,9 @@
1
+ # This file is used to designate compatibilty with different versions of Spree
2
+ # Please see http://spreecommerce.com/documentation/extensions.html#versionfile for details
3
+
4
+ # Examples
5
+ #
6
+ "0.50.x" => { :branch => "master" }
7
+ "0.40.x" => { :tag => "v1.0.0", :version => "1.0.0" }
8
+
9
+
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class Admin::CurrenciesController < Admin::ResourceController
3
+ before_filter :set_currency, :only => [:edit, :update]
4
+
5
+ private
6
+
7
+ def set_currency
8
+ @currency = Spree::Currency.find(params[:id])
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ module Spree
2
+ class Admin::CurrencyConvertersController < Admin::ResourceController
3
+ end
4
+ end
@@ -0,0 +1,12 @@
1
+ Spree::BaseController.class_eval do
2
+ before_filter :set_currency
3
+
4
+ private
5
+
6
+ def set_currency
7
+ if session[:currency_id].present? && (@currency = Spree::Currency.find_by_char_code(session[:currency_id]))
8
+ Spree::Currency.current!(@currency)
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,16 @@
1
+ module Spree
2
+ class CurrencyController < Spree::BaseController
3
+
4
+ def set
5
+ if @currency = Spree::Currency.find_by_char_code(params[:id].to_s.upcase)
6
+ session[:currency_id] = params[:id].to_s.upcase.to_sym
7
+ Spree::Currency.current!(@currency)
8
+ flash.notice = t(:currency_changed)
9
+ else
10
+ flash[:error] = t(:currency_not_found)
11
+ end
12
+
13
+ redirect_back_or_default(root_path)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ module ActionView
2
+ module Helpers
3
+ module NumberHelper
4
+
5
+ def number_to_currency(number, options = {})
6
+ return nil if number.nil?
7
+ options.symbolize_keys!
8
+ options[:locale] = "currency_#{ Spree::Currency.current.try(:char_code) || I18n.default_locale }"
9
+ defaults = I18n.translate('number.format', :locale => options[:locale], :default => {})
10
+ currency = I18n.translate('number.currency.format', :locale => options[:locale], :default => {})
11
+ defaults = DEFAULT_CURRENCY_VALUES.merge(defaults).merge!(currency)
12
+ defaults[:negative_format] = "-" + options[:format] if options[:format]
13
+ options = defaults.merge!(options)
14
+
15
+ unit = options.delete(:unit)
16
+ format = options.delete(:format)
17
+
18
+ if number.to_f < 0
19
+ format = options.delete(:negative_format)
20
+ number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
21
+ end
22
+
23
+ begin
24
+ value = number_with_precision(number, options.merge(:raise => true))
25
+ format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
26
+ rescue InvalidNumberError => e
27
+ if options[:raise]
28
+ raise
29
+ else
30
+ formatted_number = format.gsub(/%n/, e.number).gsub(/%u/, unit)
31
+ e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,4 @@
1
+ Spree::Adjustment.class_eval do
2
+ extend Spree::MultiCurrency
3
+ multi_currency :amount
4
+ end
@@ -0,0 +1,119 @@
1
+ require 'money'
2
+ class OriginMoney < Money
3
+ end
4
+
5
+ module Spree
6
+ class Currency < ActiveRecord::Base
7
+
8
+ has_many :currency_converters do
9
+ def get_rate(date)
10
+ last(:conditions => ["date_req <= ?", date])
11
+ end
12
+ end
13
+
14
+ default_scope :order => "spree_currencies.locale"
15
+ scope :locale, lambda{|str| where("locale like ?", "%#{str}%")}
16
+ after_save :reset_basic_currency
17
+
18
+ attr_accessible :basic, :locale, :char_code, :num_code, :name
19
+
20
+ def basic!
21
+ self.class.update_all(:basic => false) && update_attribute(:basic, true)
22
+ end
23
+
24
+ def locale=(locales)
25
+ write_attribute(:locale, [locales].flatten.compact.join(','))
26
+ end
27
+
28
+ def locale(need_split = true)
29
+ need_split ? read_attribute(:locale).to_s.split(',') : read_attribute(:locale).to_s
30
+ end
31
+
32
+ # We can only have one main currency.
33
+ # Therefore we reset all other currencies but the current if it's the main.
34
+ def reset_basic_currency
35
+ self.class.where("id != ?", self.id).update_all(:basic => false) if self.basic?
36
+ end
37
+
38
+ class << self
39
+
40
+ # Get the current locale
41
+ def current( current_locale = nil )
42
+ @current ||= locale(current_locale || I18n.locale).first
43
+ end
44
+
45
+ def current!(current_locale = nil )
46
+ @current = current_locale.is_a?(Spree::Currency) ? current_locale : locale(current_locale||I18n.locale).first
47
+ end
48
+
49
+ def load_rate(options= {})
50
+ current(options[:locale] || I18n.locale)
51
+ basic
52
+ if @rate = @current.currency_converters.get_rate(options[:date] || Time.now)
53
+ add_rate(@basic.char_code, @current.char_code, @rate.nominal/@rate.value.to_f)
54
+ add_rate(@current.char_code, @basic.char_code, @rate.value.to_f)
55
+ end
56
+ end
57
+
58
+ # Exchanges money between two currencies.
59
+ # E.g. with these args: 150, DKK, GBP returns 16.93
60
+ def convert(value, from, to)
61
+ res = ( OriginMoney.new(value.to_f * 10000, from).exchange_to(to).to_f / 100).round(2)
62
+ #Rails.logger.info "#{value} #{from} == #{res} #{to}"
63
+ res
64
+ end
65
+
66
+ # Converts the basic currency value to a 'localized' value.
67
+ # In the parameters you can specify the locale you wish to convert TO.
68
+ # Usage: Currency.conversion_to_current(100, :locale => "da")
69
+ def conversion_to_current(value, options = { })
70
+ load_rate(options)
71
+ convert(value, @basic.char_code, @current.char_code)
72
+ rescue => ex
73
+ Rails.logger.error " [ Currency ] :#{ex.inspect} \n #{ex.backtrace.join('\n ')}"
74
+ value
75
+ end
76
+
77
+ # Converts the currency value of the current locale to the basic currency.
78
+ # In the parameters you can specify the locale you wish to convert FROM.
79
+ # Usage: Currency.conversion_from_current(100, :locale => "da")
80
+ def conversion_from_current(value, options={})
81
+ load_rate(options)
82
+
83
+ # Replace commas with dots as decimal mark for those languages that use this.
84
+
85
+ # 2,000.00 => 2000.00
86
+ value.gsub!(",","") if (value =~ /\,[0-9]+\./)
87
+
88
+ # 2.000,00 => 2000.00
89
+ value.gsub!(".","").gsub!(",",".") if (value =~ /\.[0-9]+\,/)
90
+
91
+ # 2000,00 => 2000.00
92
+ value.gsub!(",","") if (value =~ /\.[0-9]+\,/)
93
+
94
+ convert(value, @current.char_code, @basic.char_code)
95
+ rescue => ex
96
+ Rails.logger.error " [ Currency ] :#{ex.inspect} \n #{e.backtrace.join('\n ')}"
97
+ value
98
+ end
99
+
100
+
101
+ # Retrieves the main currency.
102
+ def basic
103
+ @basic ||= where(:basic => true).first
104
+ end
105
+
106
+ def get(num_code, options ={ })
107
+ find_by_num_code(num_code) || create(options)
108
+ end
109
+
110
+ private
111
+
112
+ def add_rate(from, to, rate)
113
+ OriginMoney.add_rate(from, to, rate.to_f )
114
+ end
115
+
116
+ end
117
+
118
+ end
119
+ end