spree_multi_currency 1.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.
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/db/seeds.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Use this file to load your own seed data from extensions.
2
+ # See the db/seeds.rb file in the Spree core for some ideas on what you can do here.
@@ -0,0 +1,32 @@
1
+ Feature: Visiting products and change currency
2
+
3
+ Background:
4
+ Given the following taxonomies exist:
5
+ | name |
6
+ | Brand |
7
+ | Categories |
8
+ Given the custom taxons and custom products exist
9
+
10
+ Scenario: show page
11
+ When I go to the home page
12
+ When change currency to "usd"
13
+ When I click first link from selector "ul.product-listing a"
14
+ Then I should see "$17.99"
15
+ When change currency to "rub"
16
+ When I click first link from selector "ul.product-listing a"
17
+ Then I should see "494.76 руб."
18
+
19
+ Scenario: make order and change currency between each step
20
+ When I go to the home page
21
+ And I click first link from selector "ul.product-listing a"
22
+ And push "add to cart"
23
+ When click checkout
24
+ When I fill in "order_email" with "test@test.com"
25
+ And I press Continue
26
+ And I fill shipping address with correct data
27
+ And I fill billing address with correct data
28
+ And I press Save and Continue
29
+ And I choose "UPS Ground" as shipping method
30
+ And I enter valid credit card details
31
+ Then I should see "630"
32
+ Then show page
@@ -0,0 +1,124 @@
1
+ Given /^the custom taxons and custom products exist$/ do
2
+ Fixtures.reset_cache
3
+ fixtures_folder = File.join(Rails.root.to_s, 'db', 'sample')
4
+ fixtures = Dir[File.join(fixtures_folder, '*.yml')].map {|f| File.basename(f, '.yml') }
5
+ Fixtures.create_fixtures(fixtures_folder, fixtures)
6
+ =begin
7
+ PaymentMethod.create(
8
+ :name => 'Credit Card',
9
+ :description => 'Bogus payment gateway for development.',
10
+ :environment => 'cucumber',
11
+ :active => true,
12
+ :type => Gateway::Bogus)
13
+ =end
14
+ end
15
+
16
+ Then /^verify products listing for top search result$/ do
17
+ page.all('ul.product-listing li').size.should == 1
18
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
19
+ tmp.delete("")
20
+ tmp.sort!.should == ["Ruby on Rails Ringer T-shirt $17.99"]
21
+ end
22
+
23
+ Then /^verify products listing for Ruby on Rails brand$/ do
24
+ page.all('ul.product-listing li').size.should == 7
25
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
26
+ tmp.delete("")
27
+ array = ["Ruby on Rails Bag $22.99",
28
+ "Ruby on Rails Baseball Jersey $19.99",
29
+ "Ruby on Rails Jr. Spaghetti $19.99",
30
+ "Ruby on Rails Mug $13.99",
31
+ "Ruby on Rails Ringer T-shirt $17.99",
32
+ "Ruby on Rails Stein $16.99",
33
+ "Ruby on Rails Tote $15.99"]
34
+ tmp.sort!.should == array
35
+ end
36
+
37
+ Then /^verify products listing for Ruby brand$/ do
38
+ page.all('ul.product-listing li').size.should == 1
39
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
40
+ tmp.delete("")
41
+ tmp.sort!.should == ["Ruby Baseball Jersey $19.99"]
42
+ end
43
+
44
+ Then /^verify products listing for Apache brand$/ do
45
+ page.all('ul.product-listing li').size.should == 1
46
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
47
+ tmp.delete("")
48
+ tmp.sort!.should == ["Apache Baseball Jersey $19.99"]
49
+ end
50
+
51
+ Then /^verify products listing for Clothing category$/ do
52
+ page.all('ul.product-listing li').size.should == 5
53
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
54
+ tmp.delete("")
55
+ tmp.sort!.should == ["Apache Baseball Jersey $19.99",
56
+ "Ruby Baseball Jersey $19.99",
57
+ "Ruby on Rails Baseball Jersey $19.99",
58
+ "Ruby on Rails Jr. Spaghetti $19.99",
59
+ "Ruby on Rails Ringer T-shirt $17.99"]
60
+ end
61
+
62
+ Then /^verify products listing for Bags category$/ do
63
+ page.all('ul.product-listing li').size.should == 2
64
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
65
+ tmp.delete("")
66
+ tmp.sort!.should == ["Ruby on Rails Bag $22.99", "Ruby on Rails Tote $15.99"]
67
+ end
68
+
69
+ Then /^verify products listing for Mugs category$/ do
70
+ page.all('ul.product-listing li').size.should == 2
71
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
72
+ tmp.delete("")
73
+ tmp.sort!.should == ["Ruby on Rails Mug $13.99", "Ruby on Rails Stein $16.99"]
74
+ end
75
+
76
+ Then /^verify products listing for price range search 15-18$/ do
77
+ page.all('ul.product-listing li').size.should == 3
78
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
79
+ tmp.delete("")
80
+ tmp.sort!.should == ["Ruby on Rails Ringer T-shirt $17.99", "Ruby on Rails Stein $16.99", "Ruby on Rails Tote $15.99"]
81
+ end
82
+
83
+ Then /^verify products listing for price range search 18 and above$/ do
84
+ page.all('ul.product-listing li').size.should == 3
85
+ tmp = page.all('ul.product-listing li a').map(&:text).flatten.compact
86
+ tmp.delete("")
87
+ tmp.sort!.should == ["Ruby on Rails Bag $22.99",
88
+ "Ruby on Rails Baseball Jersey $19.99",
89
+ "Ruby on Rails Jr. Spaghetti $19.99"]
90
+ end
91
+
92
+
93
+ Then /^I should get a "(\d+) ([^"]+)" response$/ do |http_status, message|
94
+ #response.status.should == "#{http_status} #{message}" # webrat
95
+ page.driver.status_code.should == http_status.to_i # capybara
96
+ end
97
+
98
+ When /^change currency to "([^"]*)"$/ do |arg1|
99
+ visit "/currency/#{arg1}"
100
+ end
101
+
102
+ Then /^show page$/ do
103
+ save_and_open_page
104
+ end
105
+
106
+ When /^push "add to cart"$/ do
107
+ page.find(:xpath, "//button").click
108
+ end
109
+
110
+ When /^click checkout$/ do
111
+ page.find(:xpath,"//a[@href='/checkout']").click
112
+ end
113
+
114
+ When /^I press Continue$/ do
115
+ page.find(:xpath,"//input[@value='Continue']").click
116
+ end
117
+
118
+ When /^I press Save and Continue$/ do
119
+ page.find(:xpath,"//input[@value='Save and Continue']").click
120
+ end
121
+
122
+ Then /^sleep$/ do
123
+ sleep 100
124
+ end
@@ -0,0 +1,16 @@
1
+ FEATURES_PATH = File.expand_path('../..', __FILE__)
2
+ # require define path to spree project
3
+ ENV['SPREE_GEM_PATH'] = "/home/dima/project/spree"
4
+ # or define spree as gem in Gemfile
5
+ # and decomment this
6
+ # gemfile = Pathname.new("Gemfile").expand_path
7
+ # lockfile = gemfile.dirname.join('Gemfile.lock')
8
+ # definition = Bundler::Definition.build(gemfile, lockfile, nil)
9
+ # sc=definition.index.search "spree"
10
+ # ENV['SPREE_GEM_PATH'] = sc[0].loaded_from.gsub(/\/[a-z_]*.gemspec$/,'')
11
+
12
+
13
+ # load shared env with features
14
+ puts "#{ENV['SPREE_GEM_PATH']}/features/support/env"
15
+ require File.expand_path("#{ENV['SPREE_GEM_PATH']}/features/support/env", __FILE__)
16
+ Capybara.default_driver = :selenium
@@ -0,0 +1,42 @@
1
+ module NavigationHelpers
2
+ # Maps a name to a path. Used by the
3
+ #
4
+ # When /^I go to (.+)$/ do |page_name|
5
+ #
6
+ # step definition in web_steps.rb
7
+ #
8
+ def path_to(page_name)
9
+ case page_name
10
+
11
+ when /the home\s?page/
12
+ '/'
13
+ when /the admin home page/
14
+ admin_path
15
+ when /the sign in page/
16
+ new_user_session_path
17
+ when /the sign up page/
18
+ new_user_registration_path
19
+ when /an invalid taxon page/
20
+ "/t/totally_bogus_taxon"
21
+
22
+
23
+ # Add more mappings here.
24
+ # Here is an example that pulls values out of the Regexp:
25
+ #
26
+ # when /^(.*)'s profile page$/i
27
+ # user_profile_path(User.find_by_login($1))
28
+
29
+ else
30
+ begin
31
+ page_name =~ /the (.*) page/
32
+ path_components = $1.split(/\s+/)
33
+ self.send(path_components.push('path').join('_').to_sym)
34
+ rescue Object => e
35
+ raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
36
+ "Now, go and add a mapping in #{__FILE__}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ World(NavigationHelpers)
@@ -0,0 +1,29 @@
1
+ module SpreeMultiCurrency
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ # def add_javascripts
6
+ # append_file "app/assets/javascripts/store/all.js", "//= require store/spree_multi_lingual\n"
7
+ # append_file "app/assets/javascripts/admin/all.js", "//= require admin/spree_multi_lingual\n"
8
+ # end
9
+ #
10
+ # def add_stylesheets
11
+ # inject_into_file "app/assets/stylesheets/store/all.css", " *= require store/spree_multi_lingual\n", :before => /\*\//, :verbose => true
12
+ # inject_into_file "app/assets/stylesheets/admin/all.css", " *= require admin/spree_multi_lingual\n", :before => /\*\//, :verbose => true
13
+ # end
14
+
15
+ def add_migrations
16
+ run 'bundle exec rake railties:install:migrations FROM=spree_multi_currency'
17
+ end
18
+
19
+ def run_migrations
20
+ res = ask "Would you like to run the migrations now? [Y/n]"
21
+ if res == "" || res.downcase == "y"
22
+ run 'bundle exec rake db:migrate'
23
+ else
24
+ puts "Skipping rake db:migrate, don't forget to run it!"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spree/core'
3
+ require 'spree_multi_currency/engine'
4
+ module Spree::MultiCurrency
5
+
6
+ # Order.class_eval do
7
+ # extend MultiCurrency
8
+ # multi_currency :item_total, :total,
9
+ # :rate_at_date => lambda{ |t| t.created_at },
10
+ # :only_read => true
11
+ # only_read - define just the getter method (and not the setter)
12
+ # rate_at_date - use the exchange rate at the specified date
13
+ def multi_currency(*args)
14
+ options = args.extract_options!
15
+ [args].flatten.compact.each do |number_field|
16
+ define_method(number_field.to_sym) do
17
+ if options.has_key?(:rate_at_date) && options[:rate_at_date].is_a?(Proc)
18
+ Spree::Currency.conversion_to_current(
19
+ read_attribute(number_field.to_sym),
20
+ { :date => options[:rate_at_date].call(self) }
21
+ )
22
+ else
23
+ Spree::Currency.conversion_to_current(read_attribute(number_field.to_sym))
24
+ end
25
+ end
26
+
27
+ define_method("base_#{number_field}") do
28
+ read_attribute(number_field.to_sym)
29
+ end
30
+
31
+ unless options[:only_read]
32
+ define_method(:"#{number_field}=") do |value|
33
+ write_attribute(number_field.to_sym, Spree::Currency.conversion_from_current(value))
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,34 @@
1
+ module SpreeMultiCurrency
2
+ # mattr_reader :languages
3
+ # @@languages = [:en]
4
+ #
5
+ # def self.languages=(locales=[])
6
+ # I18n.available_locales = locales
7
+ # @@languages = locales
8
+ # end
9
+
10
+ class Engine < Rails::Engine
11
+ engine_name 'spree_multi_currency'
12
+
13
+ config.autoload_paths += %W(#{config.root}/lib)
14
+
15
+ # use rspec for tests
16
+ config.generators do |g|
17
+ g.test_framework :rspec
18
+ end
19
+
20
+ def self.activate
21
+ Dir.glob(File.join(File.dirname(__FILE__), "../../app/**/*_decorator*.rb")) do |c|
22
+ Rails.configuration.cache_classes ? require(c) : load(c)
23
+ end
24
+ end
25
+
26
+ # after rails config/initializers loading, setup spree_multi_lingual's language by getting
27
+ # I18n.available_locales but it returns only [:en]
28
+ # initializer "spree_multi_lingual.environment", :after => :load_config_initializers do |app|
29
+ # SpreeMultiLingual.languages = I18n.available_locales
30
+ # end
31
+
32
+ config.to_prepare &method(:activate).to_proc
33
+ end
34
+ end
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+
3
+ require 'open-uri'
4
+ require 'nokogiri'
5
+ # add custom rake tasks here
6
+ namespace :spree_multi_currency do
7
+
8
+ namespace :currency do
9
+ desc "Общероссийский классификатор валют (сокращ. ОКВ) - http://ru.wikipedia.org/wiki/Общероссийский_классификатор_валют"
10
+
11
+ task :okv => :environment do
12
+ 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"
13
+ data = Nokogiri::HTML.parse(open(url))
14
+ keys = [:char_code, :num_code, :discharge, :name, :countries ]
15
+ data.css("table:first tr")[1..-1].map{ |d|
16
+ Hash[*keys.zip(d.css("td").map {|x| x.text.strip }).flatten]
17
+ }.each { |n|
18
+ Spree::Currency.find_by_num_code(n[:num_code]) || Spree::Currency.create(n.except(:discharge).except(:countries))
19
+ }
20
+
21
+ end
22
+
23
+ desc "Load currency ISO4217 http://en.wikipedia.org/wiki/ISO_4217"
24
+ task :iso4217 => :environment do
25
+ url = "http://en.wikipedia.org/wiki/ISO_4217"
26
+ data = Nokogiri::HTML.parse(open(url))
27
+ keys = [:char_code, :num_code, :discharge, :name, :countries ]
28
+ data.css("table:eq(2) tr")[1..-1].map{|d|
29
+ Hash[*keys.zip(d.css("td").map {|x| x.text.strip }).flatten]
30
+ }.each { |n|
31
+ Spree::Currency.find_by_num_code(n[:num_code]) || Spree::Currency.create(n.except(:discharge).except(:countries))
32
+ }
33
+ end
34
+
35
+ end
36
+
37
+ namespace :rates do
38
+ desc "Курс Сбербанка РФ http://www.cbr.ru"
39
+ task :cbr => :environment do
40
+ rub = Spree::Currency.get("643", { :num_code => "643", :char_code => "RUB", :name => "Российский рубль"})
41
+ rub.basic!
42
+ url = "http://www.cbr.ru/scripts/XML_daily.asp?date_req=#{Time.now.strftime('%d/%m/%Y')}"
43
+ data = Nokogiri::XML.parse(open(url))
44
+ date_str = data.xpath("//ValCurs").attr("Date").to_s
45
+ date = Date.strptime(date_str, (date_str =~ /\./ ? '%d.%m.%Y' : '%d/%m/%y'))
46
+ data.xpath("//ValCurs/Valute").each do |valute|
47
+ char_code = valute.xpath("./CharCode").text.to_s
48
+ num_code = valute.xpath("./NumCode").text.to_s
49
+ name = valute.xpath("./Name").text.to_s
50
+ value = valute.xpath("./Value").text.gsub(',','.').to_f
51
+ nominal = valute.xpath("./Nominal").text
52
+ currency = Spree::Currency.get(num_code, { :num_code => num_code, :char_code => char_code, :name => name})
53
+ currency && Spree::CurrencyConverter.add(currency, date, value, nominal)
54
+ end
55
+ end
56
+
57
+ desc "Rates from European Central Bank"
58
+ task :ecb, [:load_currencies] => :environment do |t, args|
59
+ Rake::Task["spree_multi_currency:currency:iso4217"].invoke if args.load_currencies
60
+ euro = Spree::Currency.get("978", { :num_code => "978", :char_code => "EUR", :name => "Euro"})
61
+ euro.basic!
62
+ url = 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml'
63
+ data = Nokogiri::XML.parse(open(url))
64
+ date = Date.strptime(data.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube').attr("time").to_s, "%Y-%m-%d")
65
+ data.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube//xmlns:Cube').each do |exchange_rate|
66
+ char_code = exchange_rate.attribute("currency").value.to_s.strip
67
+ nominal, value = exchange_rate.attribute("rate").value.to_f, 1
68
+ currency = Spree::Currency.find_by_char_code(char_code)
69
+ currency && Spree::CurrencyConverter.add(currency, date, value, nominal)
70
+ end
71
+
72
+ end
73
+
74
+ desc "Rates from Google"
75
+ task :google, [:currency, :load_currencies] => :environment do |t, args|
76
+ Rake::Task["spree_multi_currency:currency:iso4217"].invoke if args.load_currencies
77
+ default_currency = Spree::Currency.where("char_code = :currency_code or num_code = :currency_code", :currency_code => args.currency.upcase || 978).first ||
78
+ Spree::Currency.get("978", { :num_code => "978", :char_code => "EUR", :name => "Euro"})
79
+ default_currency.basic!
80
+ date = Time.now
81
+ puts "Loads currency data from Google using #{default_currency}"
82
+ Spree::Currency.all.each do |currency|
83
+ unless currency == default_currency
84
+ url = "http://www.google.com/ig/calculator?hl=en&q=1#{ currency.char_code }%3D%3F#{ default_currency.char_code }"
85
+ puts url
86
+ @data = JSON.parse(open(url).read.gsub(/lhs:|rhs:|error:|icc:/){ |x| "\"#{x[0..-2]}\":"})
87
+ if @data["error"].blank?
88
+ @value = BigDecimal(@data["rhs"].split(' ')[0])
89
+ Spree::CurrencyConverter.add(currency, date, @value, 1)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+
97
+
98
+ end
99
+
data/script/rails ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ ENGINE_PATH = File.expand_path('../..', __FILE__)
5
+ load File.expand_path('../../spec/dummy/script/rails', __FILE__)
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Currencies changing" do
4
+
5
+ it "changes price when changing locale" do
6
+ rub = Spree::Currency.create(:name => "rubles", :char_code => "RUB", :num_code => 623, :locale => "ru", :basic => false)
7
+ usd = Spree::Currency.create(:name => "dollars", :char_code => "USD", :num_code => 624, :locale => "en", :basic => true)
8
+ Spree::CurrencyConverter.create(:nominal => 32, :value => 1.0, :currency => rub, :date_req => Time.now)
9
+ product = FactoryGirl.create(:product, :price => 1)
10
+ variant = Spree::Variant.last
11
+ I18n.locale = "en"
12
+ variant.price.should eql 1.0
13
+ I18n.locale = "ru"
14
+ Spree::Currency.current!
15
+ Spree::Currency.current.should eql rub
16
+ variant.price.to_f.should eql 32.0
17
+ end
18
+ end