spree_multi_currency 1.0.4 → 2.0.0
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.
- 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 %>
|