spree_multi_currency 1.0.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.coveralls.yml +0 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +21 -3
- data/README.markdown +8 -0
- data/Rakefile +4 -2
- data/app/assets/javascripts/admin/spree_multi_currency.js +0 -0
- data/app/assets/javascripts/store/spree_multi_currency.js +0 -0
- data/app/assets/stylesheets/admin/spree_multi_currency.css +0 -0
- data/app/assets/stylesheets/store/spree_multi_currency.css +0 -0
- data/app/controllers/spree/admin/currencies_controller.rb +3 -1
- data/app/controllers/spree/admin/currency_converters_controller.rb +2 -0
- data/app/controllers/spree/base_controller_decorator.rb +5 -2
- data/app/controllers/spree/currency_controller.rb +7 -2
- data/app/helpers/base_helper_decorator.rb +8 -0
- data/app/helpers/number_helper_decorator.rb +19 -17
- data/app/models/spree/adjustment_decorator.rb +2 -0
- data/app/models/spree/controller_helper_order.rb +11 -0
- data/app/models/spree/currency.rb +82 -38
- data/app/models/spree/currency_converter.rb +2 -0
- data/app/models/spree/line_item_decorator.rb +19 -6
- data/app/models/spree/money_decorator.rb +15 -0
- data/app/models/spree/order_decorator.rb +63 -55
- data/app/models/spree/product_decorator.rb +33 -0
- data/app/models/spree/stock/estimator_decorator.rb +18 -0
- data/app/models/spree/variant_decorator.rb +93 -16
- data/app/overrides/add_currencies_admin_configurations_menu.rb +8 -7
- data/app/overrides/add_currency_selection.rb +15 -0
- data/app/views/spree/admin/currencies/new.html.erb +4 -4
- data/app/views/spree/admin/currency_converters/edit.html.erb +4 -4
- data/app/views/spree/admin/currency_converters/new.html.erb +3 -3
- data/config/routes.rb +2 -0
- data/db/migrate/20101109134351_create_currencies.rb +2 -0
- data/db/migrate/20101109134453_create_currency_converters.rb +2 -0
- data/db/seeds.rb +2 -0
- data/features/step_definitions/product.rb +2 -0
- data/features/support/env.rb +2 -0
- data/features/support/paths.rb +2 -0
- data/lib/generators/spree_multi_currency/install/install_generator.rb +27 -15
- data/lib/spree_multi_currency.rb +13 -9
- data/lib/spree_multi_currency/engine.rb +6 -0
- data/lib/tasks/spree_multi_currency.rake +57 -38
- data/spec/changing_currency_spec.rb +44 -15
- data/spec/controllers/spree/currency_controller_spec.rb +16 -0
- data/spec/features/buy_spec.rb +103 -0
- data/spec/helpers/number_helper_spec.rb +21 -0
- data/spec/models/spree/currency_spec.rb +32 -0
- data/spec/models/spree/variant_spec.rb +32 -0
- data/spec/spec_helper.rb +50 -10
- data/spree_multi_currency.gemspec +2 -4
- metadata +29 -34
- data.tar.gz.sig +0 -3
- data/Versionfile +0 -9
- metadata.gz.sig +0 -1
@@ -1,13 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
Spree::LineItem.class_eval do
|
2
|
-
extend Spree::MultiCurrency
|
3
|
-
multi_currency :price
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
# redefine spree/core/app/models/spree/line_item.rb
|
6
|
+
def single_money
|
7
|
+
calculate_money(price)
|
8
|
+
end
|
9
|
+
|
10
|
+
# redefine spree/core/app/models/spree/line_item.rb
|
11
|
+
def money
|
12
|
+
calculate_money(amount)
|
7
13
|
end
|
8
14
|
|
9
|
-
def
|
10
|
-
|
15
|
+
def calculate_money(var)
|
16
|
+
current_cur = Spree::Currency.current
|
17
|
+
var_in_current_currency = Spree::Currency.convert(var,
|
18
|
+
currency,
|
19
|
+
current_cur.char_code)
|
20
|
+
Spree::Money.new(var_in_current_currency, { currency: current_cur })
|
11
21
|
end
|
12
22
|
|
23
|
+
alias display_total money
|
24
|
+
alias display_amount money
|
25
|
+
|
13
26
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Spree::Money.class_eval do
|
2
|
+
|
3
|
+
def initialize(amount, options = {})
|
4
|
+
@money = ::Money.parse([amount, Spree::Currency.current.char_code].join)
|
5
|
+
@options = {}
|
6
|
+
@options[:with_currency] = Spree::Config[:display_currency]
|
7
|
+
@options[:symbol_position] = Spree::Config[:currency_symbol_position].to_sym
|
8
|
+
@options[:no_cents] = Spree::Config[:hide_cents]
|
9
|
+
@options[:decimal_mark] = Spree::Config[:currency_decimal_mark]
|
10
|
+
@options[:thousands_separator] = Spree::Config[:currency_thousands_separator]
|
11
|
+
@options.merge!(options)
|
12
|
+
# Must be a symbol because the Money gem doesn't do the conversion
|
13
|
+
@options[:symbol_position] = @options[:symbol_position].to_sym
|
14
|
+
end
|
15
|
+
end
|
@@ -1,83 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Spree::OrderContents.class_eval do
|
4
|
+
|
5
|
+
def add_to_line_item(line_item, variant, quantity, currency = nil, shipment = nil)
|
6
|
+
if line_item
|
7
|
+
line_item.target_shipment = shipment
|
8
|
+
line_item.quantity += quantity.to_i
|
9
|
+
line_item.currency = currency unless currency.nil?
|
10
|
+
line_item.save
|
11
|
+
else
|
12
|
+
line_item = Spree::LineItem.new(quantity: quantity)
|
13
|
+
line_item.target_shipment = shipment
|
14
|
+
line_item.variant = variant
|
15
|
+
if currency
|
16
|
+
line_item.currency = currency unless currency.nil?
|
17
|
+
line_item.price = variant.price_in(currency).amount
|
18
|
+
else
|
19
|
+
line_item.price = variant.price
|
20
|
+
end
|
21
|
+
order.line_items << line_item
|
22
|
+
line_item
|
23
|
+
end
|
24
|
+
|
25
|
+
order.reload
|
26
|
+
line_item
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
1
31
|
Spree::Order.class_eval do
|
2
32
|
extend Spree::MultiCurrency
|
3
33
|
multi_currency :item_total, :total,
|
4
|
-
:
|
5
|
-
:
|
6
|
-
|
34
|
+
rate_at_date: lambda { |t| t.created_at },
|
35
|
+
only_read: true
|
7
36
|
|
8
37
|
def update_totals
|
9
38
|
# update_adjustments
|
10
39
|
self.payment_total = payments.completed.map(&:amount).sum
|
11
|
-
self.item_total = line_items.map(&:
|
40
|
+
self.item_total = line_items.map(&:amount).sum
|
12
41
|
self.adjustment_total = adjustments.map(&:amount).sum
|
13
42
|
self.total = read_attribute(:item_total) + adjustment_total
|
14
43
|
end
|
15
44
|
|
45
|
+
# this will return only the highest shipping cost
|
46
|
+
# if the calculator fixed price (per item) was used.
|
47
|
+
# not tested with any other calculators
|
16
48
|
def rate_hash
|
17
|
-
|
49
|
+
highest_cost = 0
|
50
|
+
available_shipping_methods(:front_end).map do |ship_method|
|
18
51
|
next unless cost = ship_method.calculator.compute(self)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
52
|
+
if cost > highest_cost
|
53
|
+
highest_cost = cost
|
54
|
+
@ship_method = ship_method
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@rate_hash ||= [{ id: @ship_method.id,
|
58
|
+
shipping_method: @ship_method,
|
59
|
+
name: @ship_method.name,
|
60
|
+
cost: highest_cost }]
|
25
61
|
end
|
26
62
|
|
27
63
|
def update!
|
28
64
|
update_totals
|
29
|
-
update_payment_state
|
65
|
+
updater.update_payment_state
|
30
66
|
|
31
67
|
# give each of the shipments a chance to update themselves
|
32
|
-
shipments.each { |shipment| shipment.update!(self) }
|
33
|
-
update_shipment_state
|
34
|
-
update_adjustments
|
35
|
-
# update totals a second time in case updated adjustments
|
68
|
+
shipments.each { |shipment| shipment.update!(self) }
|
69
|
+
updater.update_shipment_state
|
70
|
+
updater.update_adjustments
|
71
|
+
# update totals a second time in case updated adjustments
|
72
|
+
# have an effect on the total
|
36
73
|
update_totals
|
37
74
|
update_attributes_without_callbacks({
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
75
|
+
payment_state: payment_state,
|
76
|
+
shipment_state: shipment_state,
|
77
|
+
item_total: read_attribute(:item_total),
|
78
|
+
adjustment_total: adjustment_total,
|
79
|
+
payment_total: payment_total,
|
80
|
+
total: read_attribute(:total)
|
44
81
|
})
|
45
82
|
|
46
|
-
#ensure checkout payment always matches order total
|
47
|
-
|
48
|
-
|
83
|
+
# ensure checkout payment always matches order total
|
84
|
+
# FIXME implement for partitial payments
|
85
|
+
if payment? && payments.first.checkout? && payments.first.amount != total
|
86
|
+
payments.first.update_attributes_without_callbacks(amount: total)
|
49
87
|
end
|
50
88
|
|
51
89
|
update_hooks.each { |hook| self.send hook }
|
52
90
|
end
|
53
|
-
|
54
|
-
def add_variant(variant, quantity = 1)
|
55
|
-
current_item = contains?(variant)
|
56
|
-
if current_item
|
57
|
-
current_item.quantity += quantity
|
58
|
-
current_item.save
|
59
|
-
else
|
60
|
-
current_item = Spree::LineItem.new(:quantity => quantity)
|
61
|
-
current_item.variant = variant
|
62
|
-
current_item.price = variant.read_attribute(:price)
|
63
|
-
self.line_items << current_item
|
64
|
-
end
|
65
|
-
|
66
|
-
# populate line_items attributes for additional_fields entries
|
67
|
-
# that have populate => [:line_item]
|
68
|
-
Spree::Variant.additional_fields.select { |f| !f[:populate].nil? && f[:populate].include?(:line_item) }.each do |field|
|
69
|
-
value = ''
|
70
|
-
|
71
|
-
if field[:only].nil? || field[:only].include?(:variant)
|
72
|
-
value = variant.send(field[:name].gsub(' ', '_').downcase)
|
73
|
-
elsif field[:only].include?(:product)
|
74
|
-
value = variant.product.send(field[:name].gsub(' ', '_').downcase)
|
75
|
-
end
|
76
|
-
current_item.update_attribute(field[:name].gsub(' ', '_').downcase, value)
|
77
|
-
end
|
78
|
-
|
79
|
-
self.reload
|
80
|
-
current_item
|
81
|
-
end
|
82
|
-
|
83
91
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Spree::Product.class_eval do
|
4
|
+
|
5
|
+
# Can't use add_search_scope for this as it needs a default argument
|
6
|
+
def self.available(available_on = nil, currency = nil)
|
7
|
+
scope = joins(:master => :prices).where("#{Spree::Product.quoted_table_name}.available_on <= ?", available_on || Time.now)
|
8
|
+
unless Spree::Config.show_products_without_price
|
9
|
+
# should render product with any not null price
|
10
|
+
scope = scope.where('spree_prices.amount IS NOT NULL')
|
11
|
+
end
|
12
|
+
scope
|
13
|
+
end
|
14
|
+
|
15
|
+
# FIXME may be not require remove it from array
|
16
|
+
search_scopes.delete(:available)
|
17
|
+
search_scopes << :available
|
18
|
+
end
|
19
|
+
|
20
|
+
Spree::Core::Search::Base.class_eval do
|
21
|
+
def retrieve_products
|
22
|
+
@products_scope = get_base_scope
|
23
|
+
curr_page = page || 1
|
24
|
+
|
25
|
+
@products = @products_scope.includes([:master => :prices])
|
26
|
+
unless Spree::Config.show_products_without_price
|
27
|
+
@products = @products.where("spree_prices.amount IS NOT NULL")
|
28
|
+
end
|
29
|
+
@products = @products.page(curr_page).per(per_page)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Spree::Stock::Estimator.class_eval do
|
2
|
+
|
3
|
+
# currency not make sence
|
4
|
+
# redefined spree/core/app/models/spree/stock/estimator.rb
|
5
|
+
# may be i something miss, but looks like should be
|
6
|
+
# deleted shipping method if calculator have NOT currency
|
7
|
+
def shipping_methods(package)
|
8
|
+
shipping_methods = package.shipping_methods
|
9
|
+
shipping_methods.delete_if do |ship_method|
|
10
|
+
!ship_method.calculator.available?(package) ||
|
11
|
+
!ship_method.include?(order.ship_address) ||
|
12
|
+
ship_method.calculator.preferences[:currency].nil?
|
13
|
+
end
|
14
|
+
shipping_methods
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
@@ -1,22 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
Spree::Variant.class_eval do
|
2
4
|
extend Spree::MultiCurrency
|
3
|
-
multi_currency :
|
5
|
+
multi_currency :cost_price
|
6
|
+
|
7
|
+
# if save variant - require save prices
|
8
|
+
after_save :save_price
|
9
|
+
|
10
|
+
|
11
|
+
# get spree_price for current currency or
|
12
|
+
# basic or
|
13
|
+
# any other
|
14
|
+
def get_price
|
15
|
+
char_code = current_char_code
|
16
|
+
current_price = prices.where(currency: char_code).first
|
17
|
+
if current_price && current_price.amount.present?
|
18
|
+
amount = current_price.amount
|
19
|
+
return amount
|
20
|
+
else
|
21
|
+
basic_char = Spree::Currency.basic.try(:char_code)
|
22
|
+
basic_price = prices.where(currency: basic_char).first
|
23
|
+
if basic_price
|
24
|
+
amount = basic_price.amount
|
25
|
+
return Spree::Currency.conversion_to_current(amount)
|
26
|
+
else
|
27
|
+
spree_price = prices.first
|
28
|
+
amount = spree_price.amount
|
29
|
+
res = Spree::Currency.convert(amount, spree_price.currency, char_code)
|
30
|
+
return res
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# FIXME - may be will used in other classes
|
36
|
+
def current_char_code
|
37
|
+
Spree::Currency.current.try(:char_code) || Spree::Config[:currency]
|
38
|
+
end
|
39
|
+
|
40
|
+
# prices stored in spree_prices
|
41
|
+
def price
|
42
|
+
attr = read_attribute(:price)
|
43
|
+
if attr.nil? && !new_record?
|
44
|
+
get_price
|
45
|
+
else
|
46
|
+
attr
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# assign price
|
51
|
+
# if new record - save to attribute
|
52
|
+
# if saved - create price
|
53
|
+
def price=(value)
|
54
|
+
write_attribute(:price,value)
|
55
|
+
if !new_record?
|
56
|
+
cur = current_char_code
|
57
|
+
base_price = prices.where(currency: cur).first
|
58
|
+
if base_price
|
59
|
+
base_price.amount = value
|
60
|
+
else
|
61
|
+
prices.new(amount: value,currency: cur)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# redefine spree method from spree/core/app/models/spree/variant.rb
|
4
68
|
def price_in(currency)
|
5
|
-
|
6
|
-
|
7
|
-
|
69
|
+
if currency.is_a?(String)
|
70
|
+
char_code = currency
|
71
|
+
else
|
72
|
+
char_code = currency.char_code
|
73
|
+
end
|
74
|
+
res = prices.where( currency: char_code ).first
|
75
|
+
if res.blank? || res.amount.nil? || res.amount.to_i == 0
|
76
|
+
res = Spree::Price.new(variant_id: self.id,
|
77
|
+
currency: currency,
|
78
|
+
amount: get_price)
|
79
|
+
end
|
80
|
+
res
|
8
81
|
end
|
9
|
-
end
|
10
82
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
83
|
+
private
|
84
|
+
|
85
|
+
def save_price
|
86
|
+
char_code = current_char_code
|
87
|
+
spree_price = self.prices.where(currency: char_code).first
|
88
|
+
if spree_price.blank?
|
89
|
+
spree_price = self.prices.new(currency: char_code)
|
90
|
+
end
|
91
|
+
spree_price.amount = read_attribute(:price)
|
92
|
+
if spree_price &&
|
93
|
+
(spree_price.changed? ||
|
94
|
+
spree_price.new_record? ||
|
95
|
+
spree_price.amount.present? )
|
96
|
+
spree_price.save!
|
97
|
+
end
|
98
|
+
end
|
22
99
|
end
|
@@ -1,10 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
:
|
4
|
-
:
|
5
|
-
:
|
6
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
Deface::Override.new(virtual_path: "spree/admin/shared/_configuration_menu",
|
3
|
+
name: "currencies_admin_configurations_menu",
|
4
|
+
insert_bottom: "ul[data-hook='admin_configurations_sidebar_menu']",
|
5
|
+
disabled: false,
|
6
|
+
text: "
|
7
|
+
<% if spree_current_user.has_spree_role?(:admin) %>
|
7
8
|
<%= configurations_sidebar_menu_item t(:currency_settings), admin_currencies_path %>
|
8
9
|
<%= configurations_sidebar_menu_item t(:currency_converters_settings), admin_currency_converters_path %>
|
9
10
|
<% end %>
|
10
|
-
")
|
11
|
+
")
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
Deface::Override.new(virtual_path: "spree/shared/_main_nav_bar",
|
3
|
+
name: "currencies_admin_configurations_menu",
|
4
|
+
insert_bottom: "ul#main-nav-bar",
|
5
|
+
disabled: false,
|
6
|
+
text: "
|
7
|
+
<li><%= select_tag 'currency', options_for_select(Spree::Currency.all_currencies, Spree::Currency.current.char_code) %></li>
|
8
|
+
<script>
|
9
|
+
$('#currency').on('change', function(event){
|
10
|
+
window.location = '/currency/' + $(this).val();
|
11
|
+
|
12
|
+
});
|
13
|
+
</script>
|
14
|
+
|
15
|
+
")
|
@@ -4,13 +4,13 @@
|
|
4
4
|
|
5
5
|
<% content_for :page_actions do %>
|
6
6
|
<li>
|
7
|
-
<%= button_link_to t(
|
7
|
+
<%= button_link_to t('back_to_currencies'), collection_url, :icon => 'icon-arrow-left' %>
|
8
8
|
</li>
|
9
9
|
<% end %>
|
10
10
|
|
11
11
|
<%= form_for(:currency, :url => collection_url) do |f| %>
|
12
12
|
<fieldset class="no-border-top">
|
13
|
-
<%= render
|
14
|
-
<%= render
|
13
|
+
<%= render 'form', :f => f %>
|
14
|
+
<%= render 'spree/admin/shared/new_resource_links' %>
|
15
15
|
</fieldset>
|
16
|
-
<% end %>
|
16
|
+
<% end %>
|
@@ -4,13 +4,13 @@
|
|
4
4
|
|
5
5
|
<% content_for :page_actions do %>
|
6
6
|
<li>
|
7
|
-
<%= button_link_to t(
|
7
|
+
<%= button_link_to t('back_to_currency_converters'), collection_url, :icon => 'icon-arrow-left' %>
|
8
8
|
</li>
|
9
9
|
<% end %>
|
10
10
|
|
11
11
|
<%= form_for(:currency_converter, :url => object_url, :html => { :method => :put }) do |f| %>
|
12
12
|
<fieldset class="no-border-top">
|
13
|
-
<%= render
|
14
|
-
<%= render
|
13
|
+
<%= render 'form', :f => f %>
|
14
|
+
<%= render 'spree/admin/shared/edit_resource_links' %>
|
15
15
|
</fieldset>
|
16
|
-
<% end %>
|
16
|
+
<% end %>
|