multi_currencies 1.0.0 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.markdown +77 -0
- data/Rakefile +2 -0
- data/app/controllers/currency_controller.rb +14 -0
- data/app/controllers/spree/base_controller_decorator.rb +12 -0
- data/app/decorators/line_item_decorator.rb +9 -0
- data/app/decorators/order_decorator.rb +45 -0
- data/app/decorators/variant_decorator.rb +4 -0
- data/app/helpers/number_helper_decorator.rb +41 -0
- data/app/models/currency.rb +75 -22
- data/app/views/admin/currencies/_form.html.erb +1 -1
- data/app/views/admin/currencies/index.html.erb +1 -1
- data/app/views/admin/currency_converters/_form.html.erb +1 -1
- data/config/locales/currency_rub.yml +10 -0
- data/config/locales/currency_usd.yml +21 -0
- data/config/locales/en_multi_currency.yml +26 -0
- data/config/locales/ru_multi_currency.yml +26 -0
- data/config/routes.rb +8 -0
- data/db/migrate/20101109134351_create_currencies.rb +24 -0
- data/db/migrate/20101109134453_create_currency_converters.rb +15 -0
- data/db/seeds.rb +2 -0
- data/lib/multi_currencies.rb +0 -57
- data/lib/multi_currency.rb +33 -0
- data/lib/tasks/multi_currencies.rake +90 -14
- data/multi_currencies.gemspec +31 -0
- data/spec/spec_helper.rb +31 -0
- metadata +97 -30
- data/README.md +0 -13
- data/app/helpers/admin/currencies_helper.rb +0 -2
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
= Multi Currency
|
2
|
+
|
3
|
+
Support different currency and recalculate price from one to another
|
4
|
+
===========================================
|
5
|
+
Installation
|
6
|
+
---------
|
7
|
+
Add to Gemfile
|
8
|
+
gem "multi_currencies", :git => "git://github.com/pronix/spree-multi-currency.git"
|
9
|
+
|
10
|
+
Run
|
11
|
+
---
|
12
|
+
rake multi_currencies:install:migrations
|
13
|
+
rake db:migrate
|
14
|
+
|
15
|
+
Load currencies:
|
16
|
+
---------------
|
17
|
+
rake multi_currencies:currency:iso4217 # Load currency ISO4217 http://en.wikipedia.org/wiki/ISO_4217
|
18
|
+
rake multi_currencies:currency:okv # Общероссийский классификатор валют...
|
19
|
+
|
20
|
+
Load rates:
|
21
|
+
----------
|
22
|
+
rake multi_currencies:rates:cbr # Курс Сбербанка РФ http://www.cbr.ru
|
23
|
+
rake "multi_currencies:rates:ecb[load_currencies]" # Rates from European Central Bank
|
24
|
+
rake "multi_currencies:rates:google[currency,load_currencies]" # Rates from Google
|
25
|
+
|
26
|
+
|
27
|
+
Settings
|
28
|
+
---------
|
29
|
+
In admin block, configuration menu add two tables currency and currency conversion rate
|
30
|
+
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.
|
31
|
+
In Exchange Rates, provides information on the price of the currency on a specified date to the basic currency(from russian central bank).
|
32
|
+
In the exchange rates set date, currency, and face value of the currency in the base currency.
|
33
|
+
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):
|
34
|
+
rake multi_currencies:rates:cbr, as in this problem, loading the list of currencies.
|
35
|
+
|
36
|
+
В справочнике Валюты заносим список валют, указываем одну из валют основной (в этой валюте хранятся все цены). Каждой валюте назначаем соответствующую локаль.
|
37
|
+
В справчнике Курсы валют, содержиться информация о цене валюты на определенную дату к основной валюте.
|
38
|
+
В курсе валют указываеться дата, валюта, номинал и стоимость валюты в основной валюте.
|
39
|
+
Для заполнения курса валют, можно воспользоватьбся задачей загрузки курса валют с сайта ЦБ(http://www.cbr.ru):
|
40
|
+
rake multi_currencies:rates:cbr, так же в этой задаче идет загрузка списка валют.
|
41
|
+
|
42
|
+
Смена валюты
|
43
|
+
-------------
|
44
|
+
По умолчанию валюта выбирается от текущей локали сайта.
|
45
|
+
Так же можно сменить локаль по адресу http://[domain]/currency/[isocode], <%= link_to "eur", currency_path(:eur) %>
|
46
|
+
|
47
|
+
isocode: eur, usd, rub (цифровой код прописанные в справочнике валюты)
|
48
|
+
После смены валюты через url перестает работать смена валюты на основание текущей локали.
|
49
|
+
|
50
|
+
Формат вывода валюты
|
51
|
+
--------------------
|
52
|
+
Формат для валюты прописан в локализации, для каждой валюты нужно описать свою локализацию (прописаны eur, usd, rub):
|
53
|
+
Пример для usd, eur
|
54
|
+
---
|
55
|
+
currency_USD: &usd
|
56
|
+
number:
|
57
|
+
currency:
|
58
|
+
format:
|
59
|
+
format: "%u%n"
|
60
|
+
unit: "$"
|
61
|
+
separator: "."
|
62
|
+
delimiter: ","
|
63
|
+
precision: 2
|
64
|
+
significant: false
|
65
|
+
strip_insignificant_zeros: false
|
66
|
+
|
67
|
+
currency_EUR:
|
68
|
+
<<: *usd
|
69
|
+
number:
|
70
|
+
currency:
|
71
|
+
format:
|
72
|
+
format: "%u%n"
|
73
|
+
unit: "€"
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class CurrencyController < Spree::BaseController
|
2
|
+
|
3
|
+
def set
|
4
|
+
if @currency = Currency.find_by_char_code(params[:id].to_s.upcase)
|
5
|
+
session[:currency_id] = params[:id].to_s.upcase.to_sym
|
6
|
+
Currency.current!(@currency)
|
7
|
+
flash.notice = t(:currency_changed)
|
8
|
+
else
|
9
|
+
flash[:error] = t(:currency_not_found)
|
10
|
+
end
|
11
|
+
|
12
|
+
redirect_back_or_default(root_path)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
Order.class_eval do
|
2
|
+
extend MultiCurrency
|
3
|
+
multi_currency :item_total, :total,
|
4
|
+
:rate_at_date => lambda{ |t| t.created_at },
|
5
|
+
:only_read => true
|
6
|
+
|
7
|
+
|
8
|
+
def update_totals
|
9
|
+
# update_adjustments
|
10
|
+
self.payment_total = payments.completed.map(&:amount).sum
|
11
|
+
self.item_total = line_items.map(&:raw_amount).sum
|
12
|
+
self.adjustment_total = adjustments.map(&:amount).sum
|
13
|
+
self.total = read_attribute(:item_total) + adjustment_total
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def update!
|
18
|
+
update_totals
|
19
|
+
update_payment_state
|
20
|
+
|
21
|
+
# give each of the shipments a chance to update themselves
|
22
|
+
shipments.each { |shipment| shipment.update!(self) }#(&:update!)
|
23
|
+
update_shipment_state
|
24
|
+
update_adjustments
|
25
|
+
# update totals a second time in case updated adjustments have an effect on the total
|
26
|
+
update_totals
|
27
|
+
update_attributes_without_callbacks({
|
28
|
+
:payment_state => payment_state,
|
29
|
+
:shipment_state => shipment_state,
|
30
|
+
:item_total => read_attribute(:item_total),
|
31
|
+
:adjustment_total => adjustment_total,
|
32
|
+
:payment_total => payment_total,
|
33
|
+
:total => read_attribute(:total)
|
34
|
+
})
|
35
|
+
|
36
|
+
#ensure checkout payment always matches order total
|
37
|
+
if payment and payment.checkout? and payment.amount != total
|
38
|
+
payment.update_attributes_without_callbacks(:amount => total)
|
39
|
+
end
|
40
|
+
|
41
|
+
update_hooks.each { |hook| self.send hook }
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActionView
|
2
|
+
module Helpers
|
3
|
+
module NumberHelper
|
4
|
+
|
5
|
+
def number_to_currency(number, options = {})
|
6
|
+
return nil if number.nil?
|
7
|
+
|
8
|
+
options.symbolize_keys!
|
9
|
+
options[:locale] = "currency_#{Currency.current.char_code}"
|
10
|
+
defaults = I18n.translate('number.format', :locale => options[:locale], :default => {})
|
11
|
+
currency = I18n.translate('number.currency.format', :locale => options[:locale], :default => {})
|
12
|
+
defaults = DEFAULT_CURRENCY_VALUES.merge(defaults).merge!(currency)
|
13
|
+
defaults[:negative_format] = "-" + options[:format] if options[:format]
|
14
|
+
options = defaults.merge!(options)
|
15
|
+
|
16
|
+
unit = options.delete(:unit)
|
17
|
+
format = options.delete(:format)
|
18
|
+
|
19
|
+
if number.to_f < 0
|
20
|
+
format = options.delete(:negative_format)
|
21
|
+
number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
value = number_with_precision(number, options.merge(:raise => true))
|
26
|
+
format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
|
27
|
+
rescue InvalidNumberError => e
|
28
|
+
if options[:raise]
|
29
|
+
raise
|
30
|
+
else
|
31
|
+
formatted_number = format.gsub(/%n/, e.number).gsub(/%u/, unit)
|
32
|
+
e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/app/models/currency.rb
CHANGED
@@ -1,43 +1,96 @@
|
|
1
|
+
require "money"
|
1
2
|
class Currency < ActiveRecord::Base
|
2
|
-
|
3
|
+
|
4
|
+
has_many :currency_converters do
|
5
|
+
def get_rate(date)
|
6
|
+
last(:conditions => ["date_req <= ?", date])
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
default_scope :order => "currencies.locale"
|
11
|
+
scope :locale, lambda{|str| where("locale like ?", "%#{str}%")}
|
12
|
+
after_save :reset_basic_currency
|
13
|
+
|
14
|
+
def basic!
|
15
|
+
self.class.update_all(:basic => false) && update_attribute(:basic, true)
|
16
|
+
end
|
17
|
+
|
18
|
+
def locale=(locales)
|
19
|
+
write_attribute(:locale, [locales].flatten.compact.join(','))
|
20
|
+
end
|
21
|
+
|
22
|
+
def locale(need_split = true)
|
23
|
+
need_split ? read_attribute(:locale).to_s.split(',') : read_attribute(:locale).to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
# Сбрасываем для всех валют флаг "основная", кроме текущей если она установлена как основная
|
27
|
+
#
|
28
|
+
def reset_basic_currency
|
29
|
+
self.class.where("id != ?", self.id).update_all(:basic => false) if self.basic?
|
30
|
+
end
|
31
|
+
|
3
32
|
class << self
|
4
|
-
|
5
|
-
|
6
|
-
|
33
|
+
|
34
|
+
# Текущая валюта
|
35
|
+
#
|
36
|
+
def current(current_locale = I18n.locale )
|
37
|
+
@current ||= locale(current_locale).first
|
38
|
+
@current
|
39
|
+
end
|
40
|
+
|
41
|
+
def current!(current_locale = nil )
|
42
|
+
@current = current_locale.is_a?(Currency) ? current_locale : locale(current_locale||I18n.locale).first
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_rate(options= {})
|
46
|
+
current(options[:locale] || I18n.locale)
|
47
|
+
basic
|
48
|
+
|
49
|
+
if @rate = @current.currency_converters.get_rate(options[:date] || Time.now)
|
50
|
+
add_rate(@basic.char_code, @current.char_code, @rate.nominal/@rate.value.to_f)
|
51
|
+
add_rate(@current.char_code, @basic.char_code, @rate.value.to_f)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def convert(value, from, to)
|
57
|
+
( Money.new(value.to_f * 10000, from).exchange_to(to).to_f / 100).round(2)
|
7
58
|
end
|
8
59
|
|
9
60
|
# Конвертируем сумму к валюте текущей локале
|
10
61
|
# Если валюта или локаль не найдена то возвращается та же сумма
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
current_currency = current(_locale).currency_converters.last(:conditions => ["date_req <= ?", _date])
|
17
|
-
return price if current_currency.blank?
|
18
|
-
(price / current_currency.value.to_f) * current_currency.nominal.to_i
|
62
|
+
# Money.new(value.to_f, "Основаня").exchange_to("К Текущей").to_f
|
63
|
+
#Currency.conversion_to_current(100, :locale => "ru")
|
64
|
+
def conversion_to_current(value, options = { })
|
65
|
+
load_rate(options)
|
66
|
+
convert(value, @basic.char_code, @current.char_code)
|
19
67
|
end
|
20
68
|
|
21
69
|
# Конвертируем значение из валюты текущей локали к основной валюте
|
22
70
|
# в параметрах можно указать локаль из которой можно делать конвертацияю :locale
|
23
71
|
# и дату курса :date, курс берется последний найденный до указанной даты
|
24
|
-
#
|
72
|
+
#Currency.conversion_from_current(100, :locale => "ru")
|
25
73
|
def conversion_from_current(value, options={})
|
26
|
-
|
27
|
-
|
28
|
-
return value if current(_locale).nil? || current(_locale).basic?
|
29
|
-
current_currency = current(_locale).currency_converters.last(:conditions => ["date_req <= ?", _date])
|
30
|
-
return value if current_currency.blank?
|
31
|
-
return (value/current_currency.nominal.to_f)*current_currency.value
|
74
|
+
load_rate(options)
|
75
|
+
convert(value, @current.char_code, @basic.char_code)
|
32
76
|
end
|
33
77
|
|
78
|
+
|
34
79
|
# Основная валюта
|
35
80
|
def basic
|
36
|
-
first(:conditions => { :basic => true })
|
37
|
-
|
81
|
+
@basic ||= first(:conditions => { :basic => true })
|
82
|
+
end
|
38
83
|
|
39
84
|
def get(num_code, options ={ })
|
40
85
|
find_by_num_code(num_code) || create(options)
|
41
86
|
end
|
42
|
-
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def add_rate(from, to, rate)
|
91
|
+
Money.add_rate(from, to, rate.to_f ) unless Money.default_bank.get_rate(from, to)
|
92
|
+
end
|
93
|
+
|
94
|
+
end # end class << self
|
95
|
+
|
43
96
|
end
|
@@ -32,7 +32,7 @@
|
|
32
32
|
<td><%= t("currency_locale") %>: </td>
|
33
33
|
<td>
|
34
34
|
<%= error_message_on :currency, :locale %>
|
35
|
-
<%= f.select :locale, I18n.available_locales.map{|x| [x,x] } %>
|
35
|
+
<%= f.select :locale, I18n.available_locales.map{|x| [x,x] }, {}, { :multiple => true, :size => 10 } %>
|
36
36
|
</td>
|
37
37
|
</tr>
|
38
38
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
currency_USD: &usd
|
3
|
+
number:
|
4
|
+
currency:
|
5
|
+
format:
|
6
|
+
format: "%u%n"
|
7
|
+
unit: "$"
|
8
|
+
separator: "."
|
9
|
+
delimiter: ","
|
10
|
+
precision: 2
|
11
|
+
significant: false
|
12
|
+
strip_insignificant_zeros: false
|
13
|
+
|
14
|
+
currency_EUR:
|
15
|
+
<<: *usd
|
16
|
+
number:
|
17
|
+
currency:
|
18
|
+
format:
|
19
|
+
format: "%u%n"
|
20
|
+
unit: "€"
|
21
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
currency_changed: Currency changed
|
4
|
+
currency_not_found: Currency not found
|
5
|
+
currency_settings: Currency Settings
|
6
|
+
currency_converters_settings: Currency Converters
|
7
|
+
currency_description: Reference Currency
|
8
|
+
currency_converters_description: Currency Exchange Rates
|
9
|
+
add_currency_converter: Add currency exchange rate
|
10
|
+
currency_converter: Currency Converter
|
11
|
+
currency_converter_date_req: Date
|
12
|
+
currency_converter_currency: Currency
|
13
|
+
currency_converter_nominal: Nominal
|
14
|
+
currency_converter_value: Value
|
15
|
+
new_currency_converter: New
|
16
|
+
editing_currency_converter: Edit
|
17
|
+
add_currency: Add currency
|
18
|
+
list_currency: List of currencies
|
19
|
+
currency_name: Currency
|
20
|
+
currency_code: Code
|
21
|
+
currency_num_code: ISO number code
|
22
|
+
currency_char_code: ISO char code
|
23
|
+
currency_locale: Locale
|
24
|
+
currency_basic: Basic currency
|
25
|
+
editing_currency: Edit
|
26
|
+
new_currency: Add
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
ru:
|
3
|
+
currency_changed: Валюта изменена
|
4
|
+
currency_not_found: Валюта не найдена
|
5
|
+
currency_settings: Валюты
|
6
|
+
currency_converters_settings: Курсы валют
|
7
|
+
currency_description: Настройка справочника валют
|
8
|
+
currency_converters_description: Настройка справочника курса валют
|
9
|
+
add_currency_converter: Добавить курс
|
10
|
+
currency_converter: Курсы валют
|
11
|
+
currency_converter_date_req: Дата
|
12
|
+
currency_converter_currency: Валюта
|
13
|
+
currency_converter_nominal: Номинал
|
14
|
+
currency_converter_value: Значение
|
15
|
+
new_currency_converter: Новый курс валюты
|
16
|
+
editing_currency_converter: Редактирование курса валюты
|
17
|
+
add_currency: Добавить валюту
|
18
|
+
list_currency: Список валют
|
19
|
+
currency_name: Валюта
|
20
|
+
currency_code: Код
|
21
|
+
currency_num_code: Цифровой код
|
22
|
+
currency_char_code: Строковый код
|
23
|
+
currency_locale: Локаль
|
24
|
+
currency_basic: Основная
|
25
|
+
editing_currency: Редактирование валюты
|
26
|
+
new_currency: Добавление валюты
|
data/config/routes.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class CreateCurrencies < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :currencies do |t|
|
4
|
+
t.string :num_code, :null => false
|
5
|
+
t.string :char_code, :null => false
|
6
|
+
t.string :name, :null => false
|
7
|
+
t.boolean :basic, :default => false
|
8
|
+
t.string :locale
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :currencies
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# <Valute ID="R01235">
|
19
|
+
# <NumCode>840</NumCode>
|
20
|
+
# <CharCode>USD</CharCode>
|
21
|
+
# <Nominal>1</Nominal>
|
22
|
+
# <Name>Доллар США</Name>
|
23
|
+
# <Value>30,8612</Value>
|
24
|
+
# </Valute>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateCurrencyConverters < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :currency_converters do |t|
|
4
|
+
t.integer :currency_id, :null => false
|
5
|
+
t.datetime :date_req, :null => false
|
6
|
+
t.float :nominal, :null => false, :default => 1
|
7
|
+
t.float :value, :null => false
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.down
|
13
|
+
drop_table :currency_converters
|
14
|
+
end
|
15
|
+
end
|
data/db/seeds.rb
ADDED
data/lib/multi_currencies.rb
CHANGED
@@ -10,63 +10,6 @@ module MultiCurrencies
|
|
10
10
|
Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c|
|
11
11
|
Rails.env.production? ? require(c) : load(c)
|
12
12
|
end
|
13
|
-
Variant.class_eval do
|
14
|
-
|
15
|
-
def price
|
16
|
-
Currency.conversion_to_current(read_attribute(:price))
|
17
|
-
end
|
18
|
-
|
19
|
-
def price=(value)
|
20
|
-
conversion_value = Currency.conversion_from_current(value)
|
21
|
-
write_attribute(:price, conversion_value)
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
Order.class_eval do
|
29
|
-
def total
|
30
|
-
Currency.conversion_to_current(read_attribute(:total))
|
31
|
-
end
|
32
|
-
|
33
|
-
def total=(value)
|
34
|
-
conversion_value = Currency.conversion_from_current(value)
|
35
|
-
write_attribute(:total, conversion_value)
|
36
|
-
end
|
37
|
-
|
38
|
-
def item_total
|
39
|
-
Currency.conversion_to_current(read_attribute(:item_total))
|
40
|
-
end
|
41
|
-
|
42
|
-
def item_total=(value)
|
43
|
-
conversion_value = Currency.conversion_from_current(value)
|
44
|
-
write_attribute(:item_total, conversion_value)
|
45
|
-
end
|
46
|
-
|
47
|
-
def credit_total
|
48
|
-
Currency.conversion_to_current(read_attribute(:credit_total))
|
49
|
-
end
|
50
|
-
|
51
|
-
def credit_total=(value)
|
52
|
-
conversion_value = Currency.conversion_from_current(value)
|
53
|
-
write_attribute(:credit_total, conversion_value)
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
LineItem.class_eval do
|
60
|
-
def price
|
61
|
-
Currency.conversion_to_current(read_attribute(:price))
|
62
|
-
end
|
63
|
-
|
64
|
-
def price=(value)
|
65
|
-
conversion_value = Currency.conversion_from_current(value)
|
66
|
-
write_attribute(:price, conversion_value)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
13
|
end
|
71
14
|
|
72
15
|
config.to_prepare &method(:activate).to_proc
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MultiCurrency
|
2
|
+
|
3
|
+
# Order.class_eval do
|
4
|
+
# extend MultiCurrency
|
5
|
+
# multi_currency :item_total, :total,
|
6
|
+
# :rate_at_date => lambda{ |t| t.created_at },
|
7
|
+
# :only_read => true
|
8
|
+
# only_read - выполнять перевод из одной валюты в другую только для вывода
|
9
|
+
# rate_at_date - использовать курс валюты на дату
|
10
|
+
def multi_currency(*args)
|
11
|
+
options = args.extract_options!
|
12
|
+
[args].flatten.compact.each do |number_field|
|
13
|
+
|
14
|
+
define_method(number_field.to_sym) do
|
15
|
+
if options.has_key?(:rate_at_date) && options[:rate_at_date].is_a?(Proc)
|
16
|
+
Currency.conversion_to_current(read_attribute(number_field.to_sym),
|
17
|
+
{ :date => options[:rate_at_date].call(self) })
|
18
|
+
else
|
19
|
+
Currency.conversion_to_current(read_attribute(number_field.to_sym))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
unless options[:only_read]
|
24
|
+
define_method(:"#{number_field}=") do |value|
|
25
|
+
write_attribute(number_field.to_sym, Currency.conversion_from_current(value))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -1,20 +1,96 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'nokogiri'
|
1
3
|
# add custom rake tasks here
|
2
4
|
namespace :multi_currencies do
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
|
6
|
+
namespace :currency do
|
7
|
+
desc "Общероссийский классификатор валют (сокращ. ОКВ) - http://ru.wikipedia.org/wiki/Общероссийский_классификатор_валют"
|
8
|
+
|
9
|
+
task :okv => :environment do
|
10
|
+
url = "http://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%89%D0%B5%D1%80%D0%BE%D1%81%D1%81%D0%B8%D0%B9%D1%81%D0%BA%D0%B8%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82%D0%BE%D1%80_%D0%B2%D0%B0%D0%BB%D1%8E%D1%82"
|
11
|
+
data = Nokogiri::HTML.parse(open(url))
|
12
|
+
keys = [:char_code, :num_code, :discharge, :name, :countries ]
|
13
|
+
data.css("table:first tr")[1..-1].map{ |d|
|
14
|
+
Hash[*keys.zip(d.css("td").map {|x| x.text.strip }).flatten]
|
15
|
+
}.each { |n|
|
16
|
+
Currency.find_by_num_code(n[:num_code]) || Currency.create(n.except(:discharge).except(:countries))
|
17
|
+
}
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Load currency ISO4217 http://en.wikipedia.org/wiki/ISO_4217"
|
22
|
+
task :iso4217 => :environment do
|
23
|
+
url = "http://en.wikipedia.org/wiki/ISO_4217"
|
24
|
+
data = Nokogiri::HTML.parse(open(url))
|
25
|
+
keys = [:char_code, :num_code, :discharge, :name, :countries ]
|
26
|
+
data.css("table:eq(2) tr")[1..-1].map{|d|
|
27
|
+
Hash[*keys.zip(d.css("td").map {|x| x.text.strip }).flatten]
|
28
|
+
}.each { |n|
|
29
|
+
Currency.find_by_num_code(n[:num_code]) || Currency.create(n.except(:discharge).except(:countries))
|
30
|
+
}
|
17
31
|
end
|
32
|
+
|
18
33
|
end
|
19
34
|
|
35
|
+
namespace :rates do
|
36
|
+
desc "Курс Сбербанка РФ http://www.cbr.ru"
|
37
|
+
task :cbr => :environment do
|
38
|
+
rub = Currency.get("643", { :num_code => "643", :char_code => "RUB", :name => "Российский рубль"})
|
39
|
+
rub.basic!
|
40
|
+
url = "http://www.cbr.ru/scripts/XML_daily.asp?date_req=#{Time.now.strftime('%d/%m/%Y')}"
|
41
|
+
data = Nokogiri::XML.parse(open(url))
|
42
|
+
date_str = data.xpath("//ValCurs").attr("Date").to_s
|
43
|
+
date = Date.strptime(date_str, (date_str =~ /\./ ? '%d.%m.%Y' : '%d/%m/%y'))
|
44
|
+
data.xpath("//ValCurs/Valute").each do |valute|
|
45
|
+
char_code = valute.xpath("./CharCode").text.to_s
|
46
|
+
num_code = valute.xpath("./NumCode").text.to_s
|
47
|
+
name = valute.xpath("./Name").text.to_s
|
48
|
+
value = valute.xpath("./Value").text.gsub(',','.').to_f
|
49
|
+
nominal = valute.xpath("./Nominal").text
|
50
|
+
currency = Currency.get(num_code, { :num_code => num_code, :char_code => char_code, :name => name})
|
51
|
+
currency && CurrencyConverter.add(currency, date, value, nominal)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Rates from European Central Bank"
|
56
|
+
task :ecb, [:load_currencies] => :environment do |t, args|
|
57
|
+
Rake::Task["multi_currencies:currency:iso4217"].invoke if args.load_currencies
|
58
|
+
euro = Currency.get("978", { :num_code => "978", :char_code => "EUR", :name => "Euro"})
|
59
|
+
euro.basic!
|
60
|
+
url = 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml'
|
61
|
+
data = Nokogiri::XML.parse(open(url))
|
62
|
+
date = Date.strptime(data.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube').attr("time").to_s, "%Y-%m-%d")
|
63
|
+
data.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube//xmlns:Cube').each do |exchange_rate|
|
64
|
+
char_code = exchange_rate.attribute("currency").value.to_s.strip
|
65
|
+
value, nimonal = exchange_rate.attribute("rate").value.to_f, 1
|
66
|
+
currency = Currency.find_by_char_code(char_code)
|
67
|
+
currency && CurrencyConverter.add(currency, date, value, nominal)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Rates from Google"
|
73
|
+
task :google, [:currency, :load_currencies] => :environment do |t, args|
|
74
|
+
Rake::Task["multi_currencies:currency:iso4217"].invoke if args.load_currencies
|
75
|
+
default_currency = Currency.where(" char_code = :currency_code or num_code = :currency_code",
|
76
|
+
:currency_code => args.currency || 978).first ||
|
77
|
+
Currency.get("978", { :num_code => "978", :char_code => "EUR", :name => "Euro"})
|
78
|
+
default_currency.basic!
|
79
|
+
date = Time.now
|
80
|
+
Currency.all.each { |currency|
|
81
|
+
unless currency == default_currency
|
82
|
+
url = "http://www.google.com/ig/calculator?hl=en&q=1#{ currency.char_code }%3D%3F#{ default_currency.char_code }"
|
83
|
+
@data = JSON.parse(open(url).read.gsub(/lhs:|rhs:|error:|icc:/){ |x| "\"#{x[0..-2]}\":"})
|
84
|
+
if @data["error"].blank?
|
85
|
+
@value = BigDecimal(@data["rhs"].split(' ')[0])
|
86
|
+
CurrencyConverter.add(currency, date, @value, 1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
20
95
|
end
|
96
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.name = 'multi_currencies'
|
7
|
+
s.version = '1.0.2'
|
8
|
+
s.summary = 'Add gem summary here'
|
9
|
+
s.required_ruby_version = '>= 1.8.7'
|
10
|
+
s.authors = ["Maxim"]
|
11
|
+
s.email = ["parallel588@gmail.com"]
|
12
|
+
s.homepage = ""
|
13
|
+
s.summary = %q{spree-multi-currency}
|
14
|
+
s.description = %q{spree-multi-currency}
|
15
|
+
|
16
|
+
s.rubyforge_project = "spree-multi-currency"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
|
22
|
+
s.require_path = ['lib']
|
23
|
+
|
24
|
+
|
25
|
+
s.add_dependency('spree_core', '>= 0.30.0')
|
26
|
+
s.add_dependency('nokogiri', '>= 1.4.4')
|
27
|
+
s.add_dependency('money', '>= 3.6.1')
|
28
|
+
s.add_dependency('json', '>= 1.5.1')
|
29
|
+
|
30
|
+
s.add_development_dependency("rspec", ">= 2.5.0")
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# This file is copied to ~/spec when you run 'ruby script/generate rspec'
|
2
|
+
# from the project root directory.
|
3
|
+
ENV["RAILS_ENV"] ||= 'test'
|
4
|
+
require File.expand_path("../../../config/environment", __FILE__)
|
5
|
+
require 'rspec/rails'
|
6
|
+
require 'fabrication'
|
7
|
+
|
8
|
+
# Requires supporting files with custom matchers and macros, etc,
|
9
|
+
# in ./support/ and its subdirectories.
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
# == Mock Framework
|
14
|
+
#
|
15
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
16
|
+
#
|
17
|
+
# config.mock_with :mocha
|
18
|
+
# config.mock_with :flexmock
|
19
|
+
# config.mock_with :rr
|
20
|
+
config.mock_with :rspec
|
21
|
+
|
22
|
+
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
23
|
+
|
24
|
+
#config.include Devise::TestHelpers, :type => :controller
|
25
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
26
|
+
# examples within a transaction, comment the following line or assign false
|
27
|
+
# instead of true.
|
28
|
+
config.use_transactional_fixtures = true
|
29
|
+
end
|
30
|
+
|
31
|
+
@configuration ||= AppConfiguration.find_or_create_by_name("Default configuration")
|
metadata
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multi_currencies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 2
|
10
|
+
version: 1.0.2
|
11
11
|
platform: ruby
|
12
|
-
authors:
|
13
|
-
|
12
|
+
authors:
|
13
|
+
- Maxim
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-04-24 00:00:00 +04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -42,17 +42,65 @@ dependencies:
|
|
42
42
|
requirements:
|
43
43
|
- - ">="
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
hash:
|
45
|
+
hash: 15
|
46
46
|
segments:
|
47
47
|
- 1
|
48
48
|
- 4
|
49
|
+
- 4
|
50
|
+
version: 1.4.4
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: money
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 29
|
62
|
+
segments:
|
49
63
|
- 3
|
64
|
+
- 6
|
50
65
|
- 1
|
51
|
-
version:
|
66
|
+
version: 3.6.1
|
52
67
|
type: :runtime
|
53
|
-
version_requirements: *
|
54
|
-
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: json
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 1
|
78
|
+
segments:
|
79
|
+
- 1
|
80
|
+
- 5
|
81
|
+
- 1
|
82
|
+
version: 1.5.1
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rspec
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 27
|
94
|
+
segments:
|
95
|
+
- 2
|
96
|
+
- 5
|
97
|
+
- 0
|
98
|
+
version: 2.5.0
|
99
|
+
type: :development
|
100
|
+
version_requirements: *id005
|
101
|
+
description: spree-multi-currency
|
55
102
|
email:
|
103
|
+
- parallel588@gmail.com
|
56
104
|
executables: []
|
57
105
|
|
58
106
|
extensions: []
|
@@ -60,35 +108,54 @@ extensions: []
|
|
60
108
|
extra_rdoc_files: []
|
61
109
|
|
62
110
|
files:
|
63
|
-
-
|
111
|
+
- .gitignore
|
112
|
+
- Gemfile
|
64
113
|
- LICENSE
|
65
|
-
-
|
66
|
-
-
|
67
|
-
-
|
68
|
-
-
|
69
|
-
- app/
|
114
|
+
- README.markdown
|
115
|
+
- Rakefile
|
116
|
+
- app/controllers/admin/currencies_controller.rb
|
117
|
+
- app/controllers/admin/currency_converters_controller.rb
|
118
|
+
- app/controllers/currency_controller.rb
|
119
|
+
- app/controllers/spree/base_controller_decorator.rb
|
120
|
+
- app/decorators/line_item_decorator.rb
|
121
|
+
- app/decorators/order_decorator.rb
|
122
|
+
- app/decorators/variant_decorator.rb
|
70
123
|
- app/helpers/admin/currency_converters_helper.rb
|
124
|
+
- app/helpers/number_helper_decorator.rb
|
71
125
|
- app/models/currency.rb
|
72
126
|
- app/models/currency_converter.rb
|
127
|
+
- app/views/admin/currencies/_form.html.erb
|
73
128
|
- app/views/admin/currencies/edit.html.erb
|
74
129
|
- app/views/admin/currencies/index.html.erb
|
75
|
-
- app/views/admin/currencies/_form.html.erb
|
76
130
|
- app/views/admin/currencies/new.html.erb
|
131
|
+
- app/views/admin/currency_converters/_form.html.erb
|
77
132
|
- app/views/admin/currency_converters/edit.html.erb
|
78
133
|
- app/views/admin/currency_converters/index.html.erb
|
79
|
-
- app/views/admin/currency_converters/_form.html.erb
|
80
134
|
- app/views/admin/currency_converters/new.html.erb
|
81
|
-
-
|
82
|
-
-
|
135
|
+
- config/locales/currency_rub.yml
|
136
|
+
- config/locales/currency_usd.yml
|
137
|
+
- config/locales/en_multi_currency.yml
|
138
|
+
- config/locales/ru_multi_currency.yml
|
139
|
+
- config/routes.rb
|
140
|
+
- db/migrate/20101109134351_create_currencies.rb
|
141
|
+
- db/migrate/20101109134453_create_currency_converters.rb
|
142
|
+
- db/seeds.rb
|
143
|
+
- lib/multi_currencies.rb
|
144
|
+
- lib/multi_currencies_hooks.rb
|
145
|
+
- lib/multi_currency.rb
|
146
|
+
- lib/tasks/install.rake
|
147
|
+
- lib/tasks/multi_currencies.rake
|
148
|
+
- multi_currencies.gemspec
|
149
|
+
- spec/spec_helper.rb
|
83
150
|
has_rdoc: true
|
84
|
-
homepage:
|
151
|
+
homepage: ""
|
85
152
|
licenses: []
|
86
153
|
|
87
154
|
post_install_message:
|
88
155
|
rdoc_options: []
|
89
156
|
|
90
157
|
require_paths:
|
91
|
-
- lib
|
158
|
+
- - lib
|
92
159
|
required_ruby_version: !ruby/object:Gem::Requirement
|
93
160
|
none: false
|
94
161
|
requirements:
|
@@ -109,12 +176,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
176
|
segments:
|
110
177
|
- 0
|
111
178
|
version: "0"
|
112
|
-
requirements:
|
113
|
-
|
114
|
-
rubyforge_project:
|
115
|
-
rubygems_version: 1.
|
179
|
+
requirements: []
|
180
|
+
|
181
|
+
rubyforge_project: spree-multi-currency
|
182
|
+
rubygems_version: 1.4.2
|
116
183
|
signing_key:
|
117
184
|
specification_version: 3
|
118
|
-
summary:
|
119
|
-
test_files:
|
120
|
-
|
185
|
+
summary: spree-multi-currency
|
186
|
+
test_files:
|
187
|
+
- spec/spec_helper.rb
|
data/README.md
DELETED