spree_core 2.2.14 → 2.3.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/{spree.js.coffee → spree.js.coffee.erb} +11 -2
- data/app/controllers/spree/base_controller.rb +1 -0
- data/app/helpers/spree/base_helper.rb +5 -6
- data/app/helpers/spree/orders_helper.rb +4 -0
- data/app/helpers/spree/products_helper.rb +8 -7
- data/app/mailers/spree/base_mailer.rb +2 -2
- data/app/mailers/spree/order_mailer.rb +2 -2
- data/app/mailers/spree/shipment_mailer.rb +1 -1
- data/app/mailers/spree/test_mailer.rb +1 -1
- data/app/models/spree/ability.rb +15 -16
- data/app/models/spree/address.rb +3 -6
- data/app/models/spree/adjustment.rb +2 -2
- data/app/models/spree/alert.rb +1 -1
- data/app/models/spree/app_configuration.rb +17 -20
- data/app/models/spree/asset.rb +2 -2
- data/app/models/spree/base.rb +9 -0
- data/app/models/spree/calculator.rb +1 -1
- data/app/models/spree/calculator/flat_rate.rb +1 -1
- data/app/models/spree/calculator/flexi_rate.rb +1 -1
- data/app/models/spree/calculator/price_sack.rb +1 -3
- data/app/models/spree/calculator/shipping/flat_rate.rb +1 -1
- data/app/models/spree/calculator/shipping/flexi_rate.rb +1 -1
- data/app/models/spree/calculator/shipping/per_item.rb +1 -1
- data/app/models/spree/calculator/shipping/price_sack.rb +1 -3
- data/app/models/spree/classification.rb +1 -1
- data/app/models/spree/configuration.rb +1 -1
- data/app/models/spree/country.rb +1 -1
- data/app/models/spree/credit_card.rb +8 -12
- data/app/models/spree/gateway.rb +0 -3
- data/app/models/spree/gateway/bogus.rb +2 -3
- data/app/models/spree/image.rb +3 -1
- data/app/models/spree/inventory_unit.rb +5 -6
- data/app/models/spree/item_adjustments.rb +3 -4
- data/app/models/spree/legacy_user.rb +1 -1
- data/app/models/spree/line_item.rb +6 -13
- data/app/models/spree/log_entry.rb +1 -1
- data/app/models/spree/option_type.rb +1 -1
- data/app/models/spree/option_value.rb +1 -3
- data/app/models/spree/order.rb +52 -70
- data/app/models/spree/order/checkout.rb +17 -10
- data/app/models/spree/order/currency_updater.rb +1 -1
- data/app/models/spree/order_contents.rb +7 -4
- data/app/models/spree/order_populator.rb +1 -1
- data/app/models/spree/order_updater.rb +8 -21
- data/app/models/spree/payment.rb +26 -12
- data/app/models/spree/payment/processing.rb +5 -16
- data/app/models/spree/payment_capture_event.rb +1 -1
- data/app/models/spree/payment_method.rb +2 -2
- data/app/models/spree/payment_method/check.rb +0 -2
- data/app/models/spree/preference.rb +1 -31
- data/app/models/spree/preferences/configuration.rb +2 -6
- data/app/models/spree/preferences/preferable.rb +46 -74
- data/app/models/spree/preferences/preferable_class_methods.rb +11 -46
- data/app/models/spree/preferences/scoped_store.rb +33 -0
- data/app/models/spree/preferences/store.rb +8 -7
- data/app/models/spree/price.rb +1 -3
- data/app/models/spree/product.rb +59 -87
- data/app/models/spree/product/scopes.rb +22 -13
- data/app/models/spree/product_option_type.rb +1 -1
- data/app/models/spree/product_property.rb +1 -3
- data/app/models/spree/product_scope/scopes.rb +1 -1
- data/app/models/spree/promotion.rb +4 -5
- data/app/models/spree/promotion/actions/create_adjustment.rb +11 -2
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +19 -2
- data/app/models/spree/promotion/actions/create_line_items.rb +2 -12
- data/app/models/spree/promotion/rules/user.rb +5 -1
- data/app/models/spree/promotion_action.rb +1 -1
- data/app/models/spree/promotion_action_line_item.rb +1 -1
- data/app/models/spree/promotion_handler/cart.rb +2 -14
- data/app/models/spree/promotion_handler/coupon.rb +3 -13
- data/app/models/spree/promotion_rule.rb +1 -1
- data/app/models/spree/property.rb +1 -3
- data/app/models/spree/prototype.rb +1 -1
- data/app/models/spree/return_authorization.rb +4 -10
- data/app/models/spree/role.rb +1 -1
- data/app/models/spree/shipment.rb +1 -9
- data/app/models/spree/shipping_category.rb +3 -3
- data/app/models/spree/shipping_method.rb +1 -1
- data/app/models/spree/shipping_method_category.rb +2 -2
- data/app/models/spree/shipping_rate.rb +3 -3
- data/app/models/spree/state.rb +1 -1
- data/app/models/spree/state_change.rb +1 -1
- data/app/models/spree/stock/availability_validator.rb +7 -3
- data/app/models/spree/stock/package.rb +0 -23
- data/app/models/spree/stock/splitter/backordered.rb +1 -1
- data/app/models/spree/stock/splitter/shipping_category.rb +1 -1
- data/app/models/spree/stock/splitter/weight.rb +1 -1
- data/app/models/spree/stock_item.rb +7 -10
- data/app/models/spree/stock_location.rb +2 -6
- data/app/models/spree/stock_movement.rb +1 -3
- data/app/models/spree/stock_transfer.rb +1 -3
- data/app/models/spree/store.rb +33 -0
- data/app/models/spree/tax_category.rb +2 -2
- data/app/models/spree/tax_rate.rb +21 -52
- data/app/models/spree/taxon.rb +9 -8
- data/app/models/spree/taxonomy.rb +1 -1
- data/app/models/spree/tracker.rb +1 -1
- data/app/models/spree/variant.rb +13 -15
- data/app/models/spree/variant/scopes.rb +1 -1
- data/app/models/spree/zone.rb +22 -22
- data/app/models/spree/zone_member.rb +2 -2
- data/config/initializers/user_class_extensions.rb +0 -8
- data/config/locales/en.yml +7 -42
- data/db/default/spree/countries.rb +2 -3
- data/db/default/spree/stores.rb +9 -0
- data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
- data/db/migrate/20130807024301_upgrade_adjustments.rb +4 -5
- data/db/migrate/20130807024302_rename_adjustment_fields.rb +5 -2
- data/db/migrate/20131118183431_add_line_item_id_to_spree_inventory_units.rb +1 -1
- data/db/migrate/20140106065820_remove_value_type_from_spree_preferences.rb +8 -0
- data/db/migrate/20140227112348_add_preference_store_to_everything.rb +8 -0
- data/db/migrate/20140309023735_migrate_old_preferences.rb +23 -0
- data/db/migrate/20140309024355_create_spree_stores.rb +25 -0
- data/db/migrate/20140309033438_create_store_from_preferences.rb +30 -0
- data/db/migrate/20140315053743_add_timestamps_to_spree_assets.rb +6 -0
- data/db/migrate/20140331100557_add_additional_store_fields.rb +8 -0
- data/db/migrate/20140410141842_add_many_missing_indexes.rb +18 -0
- data/db/migrate/20140410150358_correct_some_polymorphic_index_and_add_more_missing.rb +66 -0
- data/db/migrate/20140508151342_change_spree_price_amount_precision.rb +1 -1
- data/db/migrate/20140518174634_add_token_to_spree_orders.rb +5 -0
- data/db/migrate/20140530024945_move_order_token_from_tokenized_permission.rb +29 -0
- data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +5 -3
- data/db/migrate/20140604135309_drop_credit_card_first_name_and_last_name.rb +6 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +1 -0
- data/lib/generators/spree/dummy/templates/initializers/devise.rb +3 -0
- data/lib/generators/spree/dummy/templates/rails/routes.rb +0 -1
- data/lib/generators/spree/install/install_generator.rb +8 -17
- data/lib/generators/spree/install/templates/config/initializers/spree.rb +2 -2
- data/lib/spree/core.rb +13 -9
- data/lib/spree/core/calculated_adjustments.rb +1 -1
- data/lib/spree/core/controller_helpers/auth.rb +27 -18
- data/lib/spree/core/controller_helpers/common.rb +2 -2
- data/lib/spree/core/controller_helpers/order.rb +15 -24
- data/lib/spree/core/controller_helpers/store.rb +19 -0
- data/lib/spree/core/delegate_belongs_to.rb +2 -2
- data/lib/spree/core/engine.rb +0 -10
- data/lib/spree/core/importer.rb +1 -0
- data/lib/spree/core/importer/order.rb +16 -44
- data/lib/spree/core/importer/product.rb +62 -0
- data/lib/spree/core/product_filters.rb +0 -4
- data/lib/spree/core/routes.rb +4 -6
- data/lib/spree/core/validators/email.rb +23 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/money.rb +1 -169
- data/lib/spree/permitted_attributes.rb +6 -4
- data/lib/spree/testing_support/authorization_helpers.rb +23 -21
- data/lib/spree/testing_support/capybara_ext.rb +11 -21
- data/lib/spree/testing_support/common_rake.rb +3 -1
- data/lib/spree/testing_support/controller_requests.rb +0 -2
- data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
- data/lib/spree/testing_support/factories/line_item_factory.rb +4 -1
- data/lib/spree/testing_support/factories/order_factory.rb +5 -4
- data/lib/spree/testing_support/factories/product_factory.rb +0 -4
- data/lib/spree/testing_support/factories/promotion_factory.rb +5 -7
- data/lib/spree/testing_support/factories/shipment_factory.rb +0 -1
- data/lib/spree/testing_support/factories/stock_factory.rb +2 -2
- data/lib/spree/testing_support/factories/store_factory.rb +8 -0
- data/lib/spree/testing_support/preferences.rb +3 -3
- data/lib/tasks/core.rake +2 -2
- metadata +48 -39
- data/app/models/spree/stock/order_counter.rb +0 -55
- data/app/models/spree/tokenized_permission.rb +0 -6
- data/app/views/spree/shared/_routes.html.erb +0 -13
- data/db/migrate/20140804185157_add_default_to_shipment_cost.rb +0 -10
- data/db/migrate/20141021194502_add_state_lock_version_to_order.rb +0 -5
- data/lib/spree/core/adjustment_source.rb +0 -26
- data/lib/spree/core/mail_interceptor.rb +0 -22
- data/lib/spree/core/mail_method.rb +0 -27
- data/lib/spree/core/mail_settings.rb +0 -55
- data/lib/spree/core/ransackable_attributes.rb +0 -15
- data/lib/spree/core/token_resource.rb +0 -27
@@ -0,0 +1,19 @@
|
|
1
|
+
module Spree
|
2
|
+
module Core
|
3
|
+
module ControllerHelpers
|
4
|
+
module Store
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
|
9
|
+
def current_store
|
10
|
+
@current_store ||= Spree::Store.current(request.env['SERVER_NAME'])
|
11
|
+
end
|
12
|
+
helper_method :current_store
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,8 +4,8 @@
|
|
4
4
|
#
|
5
5
|
# Todo - integrate with ActiveRecord::Dirty to make sure changes to delegate object are noticed
|
6
6
|
# Should do
|
7
|
-
# class User <
|
8
|
-
# class Contact <
|
7
|
+
# class User < Spree::Base; delegate_belongs_to :contact, :firstname; end
|
8
|
+
# class Contact < Spree::Base; end
|
9
9
|
# u = User.first
|
10
10
|
# u.changed? # => false
|
11
11
|
# u.firstname = 'Bobby'
|
data/lib/spree/core/engine.rb
CHANGED
@@ -9,10 +9,6 @@ module Spree
|
|
9
9
|
Spree::Config = app.config.spree.preferences #legacy access
|
10
10
|
end
|
11
11
|
|
12
|
-
initializer "spree.load_preferences", :before => "spree.environment" do
|
13
|
-
::ActiveRecord::Base.send :include, Spree::Preferences::Preferable
|
14
|
-
end
|
15
|
-
|
16
12
|
initializer "spree.register.calculators" do |app|
|
17
13
|
app.config.spree.calculators.shipping_methods = [
|
18
14
|
Spree::Calculator::Shipping::FlatPercentItemTotal,
|
@@ -39,12 +35,6 @@ module Spree
|
|
39
35
|
Spree::PaymentMethod::Check ]
|
40
36
|
end
|
41
37
|
|
42
|
-
initializer "spree.mail.settings" do |app|
|
43
|
-
ActionMailer::Base.add_delivery_method :spree, Spree::Core::MailMethod
|
44
|
-
Spree::Core::MailSettings.init
|
45
|
-
Mail.register_interceptor(Spree::Core::MailInterceptor)
|
46
|
-
end
|
47
|
-
|
48
38
|
# We need to define promotions rules here so extensions and existing apps
|
49
39
|
# can add their custom classes on their initializer files
|
50
40
|
initializer 'spree.promo.environment' do |app|
|
data/lib/spree/core/importer.rb
CHANGED
@@ -13,34 +13,20 @@ module Spree
|
|
13
13
|
order = Spree::Order.create!
|
14
14
|
order.associate_user!(user)
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
create_shipments_from_params(shipments_attrs, order)
|
16
|
+
create_shipments_from_params(params.delete(:shipments_attributes), order)
|
19
17
|
create_line_items_from_params(params.delete(:line_items_attributes),order)
|
20
18
|
create_adjustments_from_params(params.delete(:adjustments_attributes), order)
|
21
19
|
create_payments_from_params(params.delete(:payments_attributes), order)
|
22
20
|
|
23
|
-
if
|
21
|
+
if(completed_at = params.delete(:completed_at))
|
24
22
|
order.completed_at = completed_at
|
25
23
|
order.state = 'complete'
|
26
24
|
end
|
27
25
|
|
28
|
-
user_id = params.delete(:user_id)
|
29
|
-
if user.has_spree_role? "admin"
|
30
|
-
order.user_id = user_id
|
31
|
-
end
|
32
|
-
|
33
26
|
order.update_attributes!(params)
|
34
|
-
|
35
|
-
order.
|
36
|
-
|
37
|
-
# Really ensure that the order totals & states are correct
|
38
|
-
order.updater.update
|
39
|
-
if shipments_attrs.present?
|
40
|
-
order.shipments.each_with_index do |shipment, index|
|
41
|
-
shipment.update_columns(cost: shipments_attrs[index][:cost].to_f) if shipments_attrs[index][:cost].present?
|
42
|
-
end
|
43
|
-
end
|
27
|
+
# Really ensure that the order totals are correct
|
28
|
+
order.update_totals
|
29
|
+
order.persist_totals
|
44
30
|
order.reload
|
45
31
|
rescue Exception => e
|
46
32
|
order.destroy if order && order.persisted?
|
@@ -53,13 +39,8 @@ module Spree
|
|
53
39
|
shipments_hash.each do |s|
|
54
40
|
begin
|
55
41
|
shipment = order.shipments.build
|
56
|
-
shipment.tracking
|
57
|
-
shipment.stock_location = Spree::StockLocation.
|
58
|
-
|
59
|
-
if s[:shipped_at].present?
|
60
|
-
shipment.shipped_at = s[:shipped_at]
|
61
|
-
shipment.state = 'shipped'
|
62
|
-
end
|
42
|
+
shipment.tracking = s[:tracking]
|
43
|
+
shipment.stock_location = Spree::StockLocation.find_by_name!(s[:stock_location])
|
63
44
|
|
64
45
|
inventory_units = s[:inventory_units] || []
|
65
46
|
inventory_units.each do |iu|
|
@@ -76,7 +57,6 @@ module Spree
|
|
76
57
|
rate = shipment.shipping_rates.create!(:shipping_method => shipping_method,
|
77
58
|
:cost => s[:cost])
|
78
59
|
shipment.selected_shipping_rate_id = rate.id
|
79
|
-
shipment.update_amounts
|
80
60
|
|
81
61
|
rescue Exception => e
|
82
62
|
raise "Order import shipments: #{e.message} #{s}"
|
@@ -88,15 +68,12 @@ module Spree
|
|
88
68
|
return {} unless line_items_hash
|
89
69
|
line_items_hash.each_key do |k|
|
90
70
|
begin
|
91
|
-
|
92
|
-
|
71
|
+
line_item = line_items_hash[k]
|
72
|
+
ensure_variant_id_from_params(line_item)
|
73
|
+
|
74
|
+
extra_params = line_item.except(:variant_id, :quantity)
|
93
75
|
line_item = order.contents.add(Spree::Variant.find(line_item[:variant_id]), line_item[:quantity])
|
94
|
-
|
95
|
-
if extra_params.present?
|
96
|
-
line_item.update_attributes!(extra_params)
|
97
|
-
else
|
98
|
-
line_item.save!
|
99
|
-
end
|
76
|
+
line_item.update_attributes(extra_params) unless extra_params.empty?
|
100
77
|
rescue Exception => e
|
101
78
|
raise "Order import line items: #{e.message} #{line_item}"
|
102
79
|
end
|
@@ -121,11 +98,9 @@ module Spree
|
|
121
98
|
return [] unless payments_hash
|
122
99
|
payments_hash.each do |p|
|
123
100
|
begin
|
124
|
-
payment = order.payments.build
|
101
|
+
payment = order.payments.build
|
125
102
|
payment.amount = p[:amount].to_f
|
126
|
-
|
127
|
-
# spree_wombat serializes payment state as status so imported orders should fall back to status field.
|
128
|
-
payment.state = p[:state] || p[:status] || 'completed'
|
103
|
+
payment.state = p.fetch(:state, 'completed')
|
129
104
|
payment.payment_method = Spree::PaymentMethod.find_by_name!(p[:payment_method])
|
130
105
|
payment.save!
|
131
106
|
rescue Exception => e
|
@@ -136,13 +111,10 @@ module Spree
|
|
136
111
|
|
137
112
|
def self.ensure_variant_id_from_params(hash)
|
138
113
|
begin
|
139
|
-
sku = hash.delete(:sku)
|
140
114
|
unless hash[:variant_id].present?
|
141
|
-
hash[:variant_id] = Spree::Variant.active.find_by_sku!(sku).id
|
115
|
+
hash[:variant_id] = Spree::Variant.active.find_by_sku!(hash[:sku]).id
|
116
|
+
hash.delete(:sku)
|
142
117
|
end
|
143
|
-
hash
|
144
|
-
rescue ActiveRecord::RecordNotFound => e
|
145
|
-
raise "Ensure order import variant: Variant w/SKU #{sku} not found."
|
146
118
|
rescue Exception => e
|
147
119
|
raise "Ensure order import variant: #{e.message} #{hash}"
|
148
120
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Spree
|
2
|
+
module Core
|
3
|
+
module Importer
|
4
|
+
class Product
|
5
|
+
attr_reader :product, :product_attrs, :variants_attrs, :options_attrs
|
6
|
+
|
7
|
+
def initialize(product, product_params, options = {})
|
8
|
+
@product = product || Spree::Product.new(product_params)
|
9
|
+
|
10
|
+
@product_attrs = product_params
|
11
|
+
@variants_attrs = options[:variants_attrs] || []
|
12
|
+
@options_attrs = options[:options_attrs] || []
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
if product.save
|
17
|
+
variants_attrs.each do |variant_attribute|
|
18
|
+
# make sure the product is assigned before the options=
|
19
|
+
product.variants.create({ product: product }.merge(variant_attribute))
|
20
|
+
end
|
21
|
+
|
22
|
+
set_up_options
|
23
|
+
end
|
24
|
+
|
25
|
+
product
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
if product.update_attributes(product_attrs)
|
30
|
+
variants_attrs.each do |variant_attribute|
|
31
|
+
# update the variant if the id is present in the payload
|
32
|
+
if variant_attribute['id'].present?
|
33
|
+
product.variants.find(variant_attribute['id'].to_i).update_attributes(variant_attribute)
|
34
|
+
else
|
35
|
+
# make sure the product is assigned before the options=
|
36
|
+
product.variants.create({ product: product }.merge(variant_attribute))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
set_up_options
|
41
|
+
end
|
42
|
+
|
43
|
+
product
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def set_up_options
|
48
|
+
options_attrs.each do |name|
|
49
|
+
option_type = Spree::OptionType.where(name: name).first_or_initialize do |option_type|
|
50
|
+
option_type.presentation = name
|
51
|
+
option_type.save!
|
52
|
+
end
|
53
|
+
|
54
|
+
unless product.option_types.include?(option_type)
|
55
|
+
product.option_types << option_type
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -42,10 +42,6 @@ module Spree
|
|
42
42
|
# or taxons), eg see the taxon model/controller.
|
43
43
|
|
44
44
|
# See specific filters below for concrete examples.
|
45
|
-
|
46
|
-
# This module is included by Taxon. In development mode that inclusion does not
|
47
|
-
# happen until Taxon class is loaded. Ensure that Taxon class is loaded before
|
48
|
-
# you try something like Product.price_range_any
|
49
45
|
module ProductFilters
|
50
46
|
# Example: filtering by price
|
51
47
|
# The named scope just maps incoming labels onto their conditions, and builds the conjunction
|
data/lib/spree/core/routes.rb
CHANGED
@@ -38,11 +38,9 @@ module Spree
|
|
38
38
|
@append_routes = []
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Spree::Core::Engine.routes.eval_block(block)
|
45
|
-
end
|
41
|
+
def eval_block(&block)
|
42
|
+
Spree::Core::Engine.routes.eval_block(block)
|
43
|
+
end
|
46
44
|
end
|
47
45
|
end
|
48
|
-
end
|
46
|
+
end
|
@@ -1,7 +1,29 @@
|
|
1
|
+
# Borrowed from http://my.rails-royce.org/2010/07/21/email-validation-in-ruby-on-rails-without-regexp/
|
2
|
+
# Mentioned in tweet here: https://twitter.com/_sohara/status/177120126083141633
|
3
|
+
require 'mail'
|
1
4
|
class EmailValidator < ActiveModel::EachValidator
|
2
5
|
def validate_each(record,attribute,value)
|
3
|
-
unless value
|
6
|
+
unless valid?(value)
|
4
7
|
record.errors.add(attribute, :invalid, {:value => value}.merge!(options))
|
5
8
|
end
|
6
9
|
end
|
10
|
+
|
11
|
+
def valid?(email)
|
12
|
+
begin
|
13
|
+
m = Mail::Address.new(email)
|
14
|
+
# We must check that value contains a domain and that value is an email address
|
15
|
+
r = m.domain && m.address == email
|
16
|
+
t = m.__send__(:tree)
|
17
|
+
# We need to dig into treetop
|
18
|
+
# A valid domain must have dot_atom_text elements size > 1
|
19
|
+
# user@localhost is excluded
|
20
|
+
# treetop must respond to domain
|
21
|
+
# We exclude valid email values like <user@localhost.com>
|
22
|
+
# Hence we use m.__send__(tree).domain
|
23
|
+
r &&= (t.domain.dot_atom_text.elements.size > 1)
|
24
|
+
rescue Exception => e
|
25
|
+
r = false
|
26
|
+
end
|
27
|
+
r
|
28
|
+
end
|
7
29
|
end
|
data/lib/spree/core/version.rb
CHANGED
data/lib/spree/money.rb
CHANGED
@@ -9,7 +9,7 @@ module Spree
|
|
9
9
|
delegate :cents, :to => :money
|
10
10
|
|
11
11
|
def initialize(amount, options={})
|
12
|
-
@money =
|
12
|
+
@money = Monetize.parse([amount, (options[:currency] || Spree::Config[:currency])].join)
|
13
13
|
@options = {}
|
14
14
|
@options[:with_currency] = Spree::Config[:display_currency]
|
15
15
|
@options[:symbol_position] = Spree::Config[:currency_symbol_position].to_sym
|
@@ -22,174 +22,6 @@ module Spree
|
|
22
22
|
@options[:symbol_position] = @options[:symbol_position].to_sym
|
23
23
|
end
|
24
24
|
|
25
|
-
# This method is being deprecated in Money 6.1.0, so now lives here.
|
26
|
-
def self.parse(input, currency = nil)
|
27
|
-
i = input.to_s.strip
|
28
|
-
|
29
|
-
# raise Money::Currency.table.collect{|c| c[1][:symbol]}.inspect
|
30
|
-
|
31
|
-
# Check the first character for a currency symbol, alternatively get it
|
32
|
-
# from the stated currency string
|
33
|
-
c = if ::Monetize.assume_from_symbol && i =~ /^(\$|€|£)/
|
34
|
-
case i
|
35
|
-
when /^\$/ then "USD"
|
36
|
-
when /^€/ then "EUR"
|
37
|
-
when /^£/ then "GBP"
|
38
|
-
end
|
39
|
-
else
|
40
|
-
i[/[A-Z]{2,3}/]
|
41
|
-
end
|
42
|
-
|
43
|
-
# check that currency passed and embedded currency are the same,
|
44
|
-
# and negotiate the final currency
|
45
|
-
if currency.nil? and c.nil?
|
46
|
-
currency = ::Money.default_currency
|
47
|
-
elsif currency.nil?
|
48
|
-
currency = c
|
49
|
-
elsif c.nil?
|
50
|
-
currency = currency
|
51
|
-
elsif currency != c
|
52
|
-
# TODO: ParseError
|
53
|
-
raise ArgumentError, "Mismatching Currencies"
|
54
|
-
end
|
55
|
-
currency = ::Money::Currency.wrap(currency)
|
56
|
-
|
57
|
-
fractional = extract_cents(i, currency)
|
58
|
-
::Money.new(fractional, currency)
|
59
|
-
end
|
60
|
-
|
61
|
-
# This method is being deprecated in Money 6.1.0, so now lives here.
|
62
|
-
def self.extract_cents(input, currency = Money.default_currency)
|
63
|
-
# remove anything that's not a number, potential thousands_separator, or minus sign
|
64
|
-
num = input.gsub(/[^\d.,'-]/, '')
|
65
|
-
|
66
|
-
# set a boolean flag for if the number is negative or not
|
67
|
-
negative = num =~ /^-|-$/ ? true : false
|
68
|
-
|
69
|
-
# decimal mark character
|
70
|
-
decimal_char = currency.decimal_mark
|
71
|
-
|
72
|
-
# if negative, remove the minus sign from the number
|
73
|
-
# if it's not negative, the hyphen makes the value invalid
|
74
|
-
if negative
|
75
|
-
num = num.sub(/^-|-$/, '')
|
76
|
-
end
|
77
|
-
|
78
|
-
raise ArgumentError, "Invalid currency amount (hyphen)" if num.include?('-')
|
79
|
-
|
80
|
-
#if the number ends with punctuation, just throw it out. If it means decimal,
|
81
|
-
#it won't hurt anything. If it means a literal period or comma, this will
|
82
|
-
#save it from being mis-interpreted as a decimal.
|
83
|
-
num.chop! if num.match(/[\.|,]$/)
|
84
|
-
|
85
|
-
# gather all decimal_marks within the result number
|
86
|
-
used_delimiters = num.scan(/[^\d]/)
|
87
|
-
|
88
|
-
# determine the number of unique decimal_marks within the number
|
89
|
-
#
|
90
|
-
# e.g.
|
91
|
-
# $1,234,567.89 would return 2 (, and .)
|
92
|
-
# $125,00 would return 1
|
93
|
-
# $199 would return 0
|
94
|
-
# $1 234,567.89 would raise an error (decimal_marks are space, comma, and period)
|
95
|
-
case used_delimiters.uniq.length
|
96
|
-
# no decimal_mark or thousands_separator; major (dollars) is the number, and minor (cents) is 0
|
97
|
-
when 0 then major, minor = num, 0
|
98
|
-
|
99
|
-
# two decimal_marks, so we know the last item in this array is the
|
100
|
-
# major/minor thousands_separator and the rest are decimal_marks
|
101
|
-
when 2
|
102
|
-
thousands_separator, decimal_mark = used_delimiters.uniq
|
103
|
-
|
104
|
-
# remove all thousands_separator, split on the decimal_mark
|
105
|
-
major, minor = num.gsub(thousands_separator, '').split(decimal_mark)
|
106
|
-
min = 0 unless min
|
107
|
-
when 1
|
108
|
-
# we can't determine if the comma or period is supposed to be a decimal_mark or a thousands_separator
|
109
|
-
# e.g.
|
110
|
-
# 1,00 - comma is a thousands_separator
|
111
|
-
# 1.000 - period is a thousands_separator
|
112
|
-
# 1,000 - comma is a decimal_mark
|
113
|
-
# 1,000,000 - comma is a decimal_mark
|
114
|
-
# 10000,00 - comma is a thousands_separator
|
115
|
-
# 1000,000 - comma is a thousands_separator
|
116
|
-
|
117
|
-
# assign first decimal_mark for reusability
|
118
|
-
decimal_mark = used_delimiters.first
|
119
|
-
|
120
|
-
# When we have identified the decimal mark character
|
121
|
-
if decimal_char == decimal_mark
|
122
|
-
major, minor = num.split(decimal_char)
|
123
|
-
|
124
|
-
else
|
125
|
-
# decimal_mark is used as a decimal_mark when there are multiple instances, always
|
126
|
-
if num.scan(decimal_mark).length > 1 # multiple matches; treat as decimal_mark
|
127
|
-
major, minor = num.gsub(decimal_mark, ''), 0
|
128
|
-
else
|
129
|
-
# ex: 1,000 - 1.0000 - 10001.000
|
130
|
-
# split number into possible major (dollars) and minor (cents) values
|
131
|
-
possible_major, possible_minor = num.split(decimal_mark)
|
132
|
-
possible_major ||= "0"
|
133
|
-
possible_minor ||= "00"
|
134
|
-
|
135
|
-
# if the minor (cents) length isn't 3, assign major/minor from the possibles
|
136
|
-
# e.g.
|
137
|
-
# 1,00 => 1.00
|
138
|
-
# 1.0000 => 1.00
|
139
|
-
# 1.2 => 1.20
|
140
|
-
if possible_minor.length != 3 # thousands_separator
|
141
|
-
major, minor = possible_major, possible_minor
|
142
|
-
else
|
143
|
-
# minor length is three
|
144
|
-
# let's try to figure out intent of the thousands_separator
|
145
|
-
|
146
|
-
# the major length is greater than three, which means
|
147
|
-
# the comma or period is used as a thousands_separator
|
148
|
-
# e.g.
|
149
|
-
# 1000,000
|
150
|
-
# 100000,000
|
151
|
-
if possible_major.length > 3
|
152
|
-
major, minor = possible_major, possible_minor
|
153
|
-
else
|
154
|
-
# number is in format ###{sep}### or ##{sep}### or #{sep}###
|
155
|
-
# handle as , is sep, . is thousands_separator
|
156
|
-
if decimal_mark == '.'
|
157
|
-
major, minor = possible_major, possible_minor
|
158
|
-
else
|
159
|
-
major, minor = "#{possible_major}#{possible_minor}", 0
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
else
|
166
|
-
# TODO: ParseError
|
167
|
-
raise ArgumentError, "Invalid currency amount"
|
168
|
-
end
|
169
|
-
|
170
|
-
# build the string based on major/minor since decimal_mark/thousands_separator have been removed
|
171
|
-
# avoiding floating point arithmetic here to ensure accuracy
|
172
|
-
cents = (major.to_i * currency.subunit_to_unit)
|
173
|
-
# Because of an bug in JRuby, we can't just call #floor
|
174
|
-
minor = minor.to_s
|
175
|
-
minor = if minor.size < currency.decimal_places
|
176
|
-
(minor + ("0" * currency.decimal_places))[0,currency.decimal_places].to_i
|
177
|
-
elsif minor.size > currency.decimal_places
|
178
|
-
if minor[currency.decimal_places,1].to_i >= 5
|
179
|
-
minor[0,currency.decimal_places].to_i+1
|
180
|
-
else
|
181
|
-
minor[0,currency.decimal_places].to_i
|
182
|
-
end
|
183
|
-
else
|
184
|
-
minor.to_i
|
185
|
-
end
|
186
|
-
|
187
|
-
cents += minor
|
188
|
-
|
189
|
-
# if negative, multiply by -1; otherwise, return positive cents
|
190
|
-
negative ? cents * -1 : cents
|
191
|
-
end
|
192
|
-
|
193
25
|
def to_s
|
194
26
|
@money.format(@options)
|
195
27
|
end
|