spree_core 2.0.13 → 2.1.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/controllers/spree/base_controller.rb +3 -0
- data/app/helpers/spree/base_helper.rb +6 -16
- data/app/helpers/spree/products_helper.rb +3 -8
- data/app/helpers/spree/taxons_helper.rb +1 -1
- data/app/mailers/spree/base_mailer.rb +0 -5
- data/app/models/spree/ability.rb +10 -7
- data/app/models/spree/address.rb +7 -17
- data/app/models/spree/adjustment.rb +15 -11
- data/app/models/spree/app_configuration.rb +0 -5
- data/app/models/spree/billing_integration.rb +0 -1
- data/app/models/spree/calculator/flat_percent_item_total.rb +1 -3
- data/app/models/spree/calculator/flat_rate.rb +2 -4
- data/app/models/spree/calculator/flexi_rate.rb +6 -9
- data/app/models/spree/calculator/per_item.rb +2 -4
- data/app/models/spree/calculator/percent_per_item.rb +1 -3
- data/app/models/spree/calculator/price_sack.rb +4 -9
- data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +1 -2
- data/app/models/spree/calculator/shipping/flat_rate.rb +2 -4
- data/app/models/spree/calculator/shipping/flexi_rate.rb +4 -9
- data/app/models/spree/calculator/shipping/per_item.rb +2 -3
- data/app/models/spree/calculator/shipping/price_sack.rb +4 -9
- data/app/models/spree/classification.rb +0 -3
- data/app/models/spree/country.rb +1 -3
- data/app/models/spree/credit_card.rb +37 -38
- data/app/models/spree/gateway/bogus_simple.rb +0 -8
- data/app/models/spree/gateway.rb +1 -3
- data/app/models/spree/image.rb +1 -3
- data/app/models/spree/inventory_unit.rb +5 -8
- data/app/models/spree/legacy_user.rb +0 -4
- data/app/models/spree/line_item.rb +2 -15
- data/app/models/spree/option_type.rb +2 -5
- data/app/models/spree/option_value.rb +1 -3
- data/app/models/spree/order/checkout.rb +4 -13
- data/app/models/spree/order.rb +47 -99
- data/app/models/spree/order_contents.rb +4 -7
- data/app/models/spree/order_inventory.rb +4 -8
- data/app/models/spree/order_updater.rb +13 -12
- data/app/models/spree/payment/processing.rb +12 -19
- data/app/models/spree/payment.rb +17 -30
- data/app/models/spree/payment_method.rb +2 -3
- data/app/models/spree/preference.rb +1 -1
- data/app/models/spree/preferences/configuration.rb +1 -1
- data/app/models/spree/preferences/preferable.rb +1 -1
- data/app/models/spree/preferences/store.rb +1 -1
- data/app/models/spree/price.rb +0 -7
- data/app/models/spree/product/scopes.rb +16 -17
- data/app/models/spree/product.rb +27 -62
- data/app/models/spree/product_property.rb +3 -5
- data/app/models/spree/promotion/actions/create_adjustment.rb +9 -8
- data/app/models/spree/promotion/actions/create_line_items.rb +1 -2
- data/app/models/spree/promotion/rules/first_order.rb +1 -1
- data/app/models/spree/promotion/rules/item_total.rb +2 -4
- data/app/models/spree/promotion/rules/product.rb +2 -2
- data/app/models/spree/promotion/rules/user.rb +1 -3
- data/app/models/spree/promotion.rb +23 -24
- data/app/models/spree/promotion_action.rb +0 -2
- data/app/models/spree/promotion_action_line_item.rb +1 -3
- data/app/models/spree/promotion_rule.rb +0 -2
- data/app/models/spree/property.rb +2 -4
- data/app/models/spree/prototype.rb +0 -2
- data/app/models/spree/return_authorization.rb +6 -9
- data/app/models/spree/role.rb +0 -2
- data/app/models/spree/shipment.rb +19 -25
- data/app/models/spree/shipping_calculator.rb +0 -2
- data/app/models/spree/shipping_category.rb +0 -2
- data/app/models/spree/shipping_method.rb +6 -20
- data/app/models/spree/shipping_rate.rb +12 -10
- data/app/models/spree/state.rb +2 -4
- data/app/models/spree/stock/availability_validator.rb +2 -2
- data/app/models/spree/stock/estimator.rb +6 -20
- data/app/models/spree/stock/packer.rb +1 -1
- data/app/models/spree/stock/quantifier.rb +2 -3
- data/app/models/spree/stock/splitter/base.rb +1 -1
- data/app/models/spree/stock_item.rb +8 -18
- data/app/models/spree/stock_location.rb +2 -11
- data/app/models/spree/stock_movement.rb +2 -5
- data/app/models/spree/stock_transfer.rb +0 -2
- data/app/models/spree/tax_category.rb +0 -2
- data/app/models/spree/tax_rate.rb +12 -12
- data/app/models/spree/taxon.rb +1 -13
- data/app/models/spree/taxonomy.rb +3 -6
- data/app/models/spree/tracker.rb +0 -2
- data/app/models/spree/variant/scopes.rb +2 -2
- data/app/models/spree/variant.rb +13 -31
- data/app/models/spree/zone.rb +2 -7
- data/app/models/spree/zone_member.rb +0 -2
- data/app/views/spree/payments/_payment.html.erb +1 -3
- data/config/locales/en.yml +11 -26
- data/db/default/spree/countries.rb +230 -229
- data/db/default/spree/states.rb +57 -56
- data/db/default/spree/zones.rb +5 -5
- data/db/migrate/20130213191427_create_default_stock.rb +4 -7
- data/db/migrate/20130417120035_update_adjustment_states.rb +2 -2
- data/db/migrate/20130417123427_add_shipping_rates_to_shipments.rb +1 -1
- data/db/migrate/20130509115210_add_number_to_stock_transfer.rb +1 -1
- data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
- data/db/migrate/20130611185927_add_user_id_index_to_spree_orders.rb +5 -0
- data/db/migrate/20130618041418_add_updated_at_to_spree_countries.rb +9 -0
- data/db/migrate/20130619012236_add_updated_at_to_spree_states.rb +9 -0
- data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +4 -5
- data/db/migrate/20130806145853_set_default_stock_location_on_shipments.rb +1 -1
- data/lib/generators/spree/dummy/dummy_generator.rb +3 -14
- data/lib/generators/spree/dummy/templates/rails/database.yml +0 -10
- data/lib/generators/spree/dummy/templates/rails/test.rb +2 -7
- data/lib/generators/spree/install/install_generator.rb +11 -8
- data/lib/spree/core/calculated_adjustments.rb +9 -8
- data/lib/spree/core/controller_helpers/auth.rb +2 -3
- data/lib/spree/core/controller_helpers/order.rb +8 -13
- data/lib/spree/core/controller_helpers/ssl.rb +13 -22
- data/lib/spree/core/controller_helpers/strong_parameters.rb +36 -0
- data/lib/spree/core/delegate_belongs_to.rb +0 -2
- data/lib/spree/core/engine.rb +1 -5
- data/lib/spree/core/ext/active_record.rb +2 -9
- data/lib/spree/core/permalinks.rb +1 -5
- data/lib/spree/core/product_duplicator.rb +2 -16
- data/lib/spree/core/product_filters.rb +37 -33
- data/lib/spree/core/search/base.rb +1 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +3 -31
- data/lib/spree/i18n.rb +0 -1
- data/lib/spree/money.rb +2 -177
- data/lib/spree/permitted_attributes.rb +95 -0
- data/lib/spree/promo/coupon_applicator.rb +4 -12
- data/lib/spree/testing_support/capybara_ext.rb +13 -17
- data/lib/spree/testing_support/common_rake.rb +1 -1
- data/lib/spree/testing_support/controller_requests.rb +3 -3
- data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
- data/lib/spree/testing_support/factories/product_factory.rb +0 -4
- data/lib/spree/testing_support/factories/shipping_method_factory.rb +1 -3
- data/lib/spree/testing_support/factories/user_factory.rb +1 -1
- data/lib/spree/testing_support/factories/variant_factory.rb +0 -15
- data/lib/spree/testing_support/factories.rb +1 -1
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- data/lib/tasks/core.rake +2 -2
- data/vendor/assets/javascripts/jquery.payment.js +497 -0
- metadata +166 -172
- data/app/views/spree/admin/shared/_report_order_criteria.html.erb +0 -17
- data/db/migrate/20130417120034_add_index_to_source_columns_on_adjustments.rb +0 -5
- data/db/migrate/20130830001033_add_shipping_category_to_shipping_methods_and_products.rb +0 -15
- data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +0 -19
- data/db/migrate/20130909115621_change_states_required_for_countries.rb +0 -9
- data/db/migrate/20131001013410_remove_unused_credit_card_fields.rb +0 -12
- data/db/migrate/20131026154747_add_track_inventory_to_variant.rb +0 -5
- data/db/migrate/20131113035136_add_channel_to_spree_orders.rb +0 -5
- data/db/migrate/20140120160805_add_index_to_variant_id_and_currency_on_prices.rb +0 -5
- data/db/migrate/20140205181631_default_variant_weight_to_zero.rb +0 -11
- data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +0 -5
- data/lib/spree/core/preference_rescue.rb +0 -25
|
@@ -37,8 +37,6 @@ module DelegateBelongsTo
|
|
|
37
37
|
attrs = get_association_column_names(association) if attrs.empty?
|
|
38
38
|
attrs.concat get_association_column_names(association) if attrs.delete :defaults
|
|
39
39
|
attrs.each do |attr|
|
|
40
|
-
next if attribute_method?(attr)
|
|
41
|
-
|
|
42
40
|
class_def attr do |*args|
|
|
43
41
|
if args.empty?
|
|
44
42
|
send(:delegator_for, association).send(attr)
|
data/lib/spree/core/engine.rb
CHANGED
|
@@ -86,11 +86,7 @@ module Spree
|
|
|
86
86
|
]
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
# Spree.user_class would be nil and users might experience errors related
|
|
91
|
-
# to malformed model associations (Spree.user_class is only defined on
|
|
92
|
-
# the app initializer)
|
|
93
|
-
config.after_initialize do
|
|
89
|
+
initializer 'spree.promo.register.promotion.calculators' do
|
|
94
90
|
Rails.application.config.spree.promotions.rules.concat [
|
|
95
91
|
Spree::Promotion::Rules::ItemTotal,
|
|
96
92
|
Spree::Promotion::Rules::Product,
|
|
@@ -2,14 +2,7 @@ module ActiveRecord::Persistence
|
|
|
2
2
|
|
|
3
3
|
# Update attributes of a record in the database without callbacks, validations etc.
|
|
4
4
|
def update_attributes_without_callbacks(attributes)
|
|
5
|
-
self.assign_attributes(attributes
|
|
6
|
-
self.class.
|
|
5
|
+
self.assign_attributes(attributes)
|
|
6
|
+
self.class.where(:id => id).update_all(attributes)
|
|
7
7
|
end
|
|
8
|
-
|
|
9
|
-
# Update a single attribute in the database
|
|
10
|
-
def update_attribute_without_callbacks(name, value)
|
|
11
|
-
send("#{name}=", value)
|
|
12
|
-
update_attributes_without_callbacks(name => value)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
8
|
end
|
|
@@ -37,10 +37,6 @@ module Spree
|
|
|
37
37
|
permalink_options[:prefix] || ""
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def permalink_length
|
|
41
|
-
permalink_options[:length] || 9
|
|
42
|
-
end
|
|
43
|
-
|
|
44
40
|
def permalink_order
|
|
45
41
|
order = permalink_options[:order]
|
|
46
42
|
"#{order} ASC," if order
|
|
@@ -48,7 +44,7 @@ module Spree
|
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
def generate_permalink
|
|
51
|
-
"#{self.class.permalink_prefix}#{Array.new(
|
|
47
|
+
"#{self.class.permalink_prefix}#{Array.new(9){rand(9)}.join}"
|
|
52
48
|
end
|
|
53
49
|
|
|
54
50
|
def save_permalink(permalink_value=self.to_param)
|
|
@@ -2,12 +2,8 @@ module Spree
|
|
|
2
2
|
class ProductDuplicator
|
|
3
3
|
attr_accessor :product
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
mattr_accessor :clone_images_default
|
|
7
|
-
|
|
8
|
-
def initialize(product, include_images = @@clone_images_default)
|
|
5
|
+
def initialize(product)
|
|
9
6
|
@product = product
|
|
10
|
-
@include_images = include_images
|
|
11
7
|
end
|
|
12
8
|
|
|
13
9
|
def duplicate
|
|
@@ -27,14 +23,12 @@ module Spree
|
|
|
27
23
|
def duplicate_product
|
|
28
24
|
product.dup.tap do |new_product|
|
|
29
25
|
new_product.name = "COPY OF #{product.name}"
|
|
30
|
-
new_product.permalink = "copy-of-#{product.permalink}"
|
|
31
26
|
new_product.taxons = product.taxons
|
|
32
27
|
new_product.created_at = nil
|
|
33
28
|
new_product.deleted_at = nil
|
|
34
29
|
new_product.updated_at = nil
|
|
35
30
|
new_product.product_properties = reset_properties
|
|
36
31
|
new_product.master = duplicate_master
|
|
37
|
-
new_product.variants = product.variants.map { |variant| duplicate_variant variant }
|
|
38
32
|
end
|
|
39
33
|
end
|
|
40
34
|
|
|
@@ -43,20 +37,12 @@ module Spree
|
|
|
43
37
|
master.dup.tap do |new_master|
|
|
44
38
|
new_master.sku = "COPY OF #{master.sku}"
|
|
45
39
|
new_master.deleted_at = nil
|
|
46
|
-
new_master.images = master.images.map { |image| duplicate_image image }
|
|
40
|
+
new_master.images = master.images.map { |image| duplicate_image image }
|
|
47
41
|
new_master.price = master.price
|
|
48
42
|
new_master.currency = master.currency
|
|
49
43
|
end
|
|
50
44
|
end
|
|
51
45
|
|
|
52
|
-
def duplicate_variant(variant)
|
|
53
|
-
new_variant = variant.dup
|
|
54
|
-
new_variant.sku = "COPY OF #{new_variant.sku}"
|
|
55
|
-
new_variant.deleted_at = nil
|
|
56
|
-
new_variant.option_values = variant.option_values.map { |option_value| option_value}
|
|
57
|
-
new_variant
|
|
58
|
-
end
|
|
59
|
-
|
|
60
46
|
def duplicate_image(image)
|
|
61
47
|
new_image = image.dup
|
|
62
48
|
new_image.assign_attributes(:attachment => image.attachment.clone)
|
|
@@ -57,12 +57,12 @@ module Spree
|
|
|
57
57
|
# below scope would be something like ["$10 - $15", "$15 - $18", "$18 - $20"]
|
|
58
58
|
#
|
|
59
59
|
Spree::Product.add_search_scope :price_range_any do |*opts|
|
|
60
|
-
conds = opts.map {|o| Spree::Core::ProductFilters.price_filter[:conds][o]}.reject {|c| c.nil?}
|
|
60
|
+
conds = opts.map {|o| Spree::Core::ProductFilters.price_filter[:conds][o]}.reject { |c| c.nil? }
|
|
61
61
|
scope = conds.shift
|
|
62
62
|
conds.each do |new_scope|
|
|
63
63
|
scope = scope.or(new_scope)
|
|
64
64
|
end
|
|
65
|
-
Spree::Product.joins(:
|
|
65
|
+
Spree::Product.joins(master: :default_price).where(scope)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def ProductFilters.format_price(amount)
|
|
@@ -71,15 +71,16 @@ module Spree
|
|
|
71
71
|
|
|
72
72
|
def ProductFilters.price_filter
|
|
73
73
|
v = Spree::Price.arel_table
|
|
74
|
-
conds = [ [ Spree.t(:under_price, :
|
|
74
|
+
conds = [ [ Spree.t(:under_price, price: format_price(10)) , v[:amount].lteq(10)],
|
|
75
75
|
[ "#{format_price(10)} - #{format_price(15)}" , v[:amount].in(10..15)],
|
|
76
76
|
[ "#{format_price(15)} - #{format_price(18)}" , v[:amount].in(15..18)],
|
|
77
77
|
[ "#{format_price(18)} - #{format_price(20)}" , v[:amount].in(18..20)],
|
|
78
|
-
[ Spree.t(:or_over_price, :
|
|
79
|
-
{
|
|
80
|
-
:
|
|
81
|
-
:
|
|
82
|
-
:
|
|
78
|
+
[ Spree.t(:or_over_price, price: format_price(20)) , v[:amount].gteq(20)]]
|
|
79
|
+
{
|
|
80
|
+
name: Spree.t(:price_range),
|
|
81
|
+
scope: :price_range_any,
|
|
82
|
+
conds: Hash[*conds.flatten],
|
|
83
|
+
labels: conds.map { |k,v| [k, k] }
|
|
83
84
|
}
|
|
84
85
|
end
|
|
85
86
|
|
|
@@ -98,23 +99,24 @@ module Spree
|
|
|
98
99
|
# rather than an inner join.
|
|
99
100
|
if Spree::Property.table_exists?
|
|
100
101
|
Spree::Product.add_search_scope :brand_any do |*opts|
|
|
101
|
-
conds = opts.map {|o| ProductFilters.brand_filter[:conds][o]}.reject {|c| c.nil?}
|
|
102
|
+
conds = opts.map {|o| ProductFilters.brand_filter[:conds][o]}.reject { |c| c.nil? }
|
|
102
103
|
scope = conds.shift
|
|
103
104
|
conds.each do |new_scope|
|
|
104
105
|
scope = scope.or(new_scope)
|
|
105
106
|
end
|
|
106
|
-
Spree::Product.with_property(
|
|
107
|
+
Spree::Product.with_property('brand').where(scope)
|
|
107
108
|
end
|
|
108
109
|
|
|
109
110
|
def ProductFilters.brand_filter
|
|
110
|
-
brand_property = Spree::Property.
|
|
111
|
-
brands = Spree::ProductProperty.where(:
|
|
111
|
+
brand_property = Spree::Property.find_by(name: 'brand')
|
|
112
|
+
brands = brand_property ? Spree::ProductProperty.where(property_id: brand_property.id).pluck(:value).uniq.map(&:to_s) : []
|
|
112
113
|
pp = Spree::ProductProperty.arel_table
|
|
113
114
|
conds = Hash[*brands.map { |b| [b, pp[:value].eq(b)] }.flatten]
|
|
114
|
-
{
|
|
115
|
-
:
|
|
116
|
-
:
|
|
117
|
-
:
|
|
115
|
+
{
|
|
116
|
+
name: 'Brands',
|
|
117
|
+
scope: :brand_any,
|
|
118
|
+
conds: conds,
|
|
119
|
+
labels: (brands.sort).map { |k| [k, k] }
|
|
118
120
|
}
|
|
119
121
|
end
|
|
120
122
|
end
|
|
@@ -145,16 +147,16 @@ module Spree
|
|
|
145
147
|
|
|
146
148
|
def ProductFilters.selective_brand_filter(taxon = nil)
|
|
147
149
|
taxon ||= Spree::Taxonomy.first.root
|
|
148
|
-
brand_property = Spree::Property.
|
|
149
|
-
scope = Spree::ProductProperty.where(:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
brand_property = Spree::Property.find_by(name: 'brand')
|
|
151
|
+
scope = Spree::ProductProperty.where(property: brand_property).
|
|
152
|
+
joins(product: :taxons).
|
|
153
|
+
where("#{Spree::Taxon.table_name}.id" => [taxon] + taxon.descendants).
|
|
154
|
+
scoped
|
|
153
155
|
brands = scope.pluck(:value).uniq
|
|
154
156
|
{
|
|
155
|
-
:
|
|
156
|
-
:
|
|
157
|
-
:
|
|
157
|
+
name: 'Applicable Brands',
|
|
158
|
+
scope: :selective_brand_any,
|
|
159
|
+
labels: brands.sort.map { |k| [k, k] }
|
|
158
160
|
}
|
|
159
161
|
end
|
|
160
162
|
end
|
|
@@ -173,10 +175,11 @@ module Spree
|
|
|
173
175
|
#
|
|
174
176
|
def ProductFilters.taxons_below(taxon)
|
|
175
177
|
return Spree::Core::ProductFilters.all_taxons if taxon.nil?
|
|
176
|
-
{
|
|
177
|
-
:
|
|
178
|
-
:
|
|
179
|
-
:
|
|
178
|
+
{
|
|
179
|
+
name: 'Taxons under ' + taxon.name,
|
|
180
|
+
scope: :taxons_id_in_tree_any,
|
|
181
|
+
labels: taxon.children.sort_by(&:position).map { |t| [t.name, t.id] },
|
|
182
|
+
conds: nil
|
|
180
183
|
}
|
|
181
184
|
end
|
|
182
185
|
|
|
@@ -187,11 +190,12 @@ module Spree
|
|
|
187
190
|
#
|
|
188
191
|
# idea: expand the format to allow nesting of labels?
|
|
189
192
|
def ProductFilters.all_taxons
|
|
190
|
-
taxons = Spree::Taxonomy.all.map {|t| [t.root] + t.root.descendants }.flatten
|
|
191
|
-
{
|
|
192
|
-
:
|
|
193
|
-
:
|
|
194
|
-
:
|
|
193
|
+
taxons = Spree::Taxonomy.all.map { |t| [t.root] + t.root.descendants }.flatten
|
|
194
|
+
{
|
|
195
|
+
name: 'All taxons',
|
|
196
|
+
scope: :taxons_id_equals_any,
|
|
197
|
+
labels: taxons.sort_by(&:name).map { |t| [t.name, t.id] },
|
|
198
|
+
conds: nil # not needed
|
|
195
199
|
}
|
|
196
200
|
end
|
|
197
201
|
end
|
|
@@ -46,7 +46,7 @@ module Spree
|
|
|
46
46
|
if base_scope.respond_to?(:search_scopes) && base_scope.search_scopes.include?(scope_name.to_sym)
|
|
47
47
|
base_scope = base_scope.send(scope_name, *scope_attribute)
|
|
48
48
|
else
|
|
49
|
-
base_scope = base_scope.merge(Spree::Product.
|
|
49
|
+
base_scope = base_scope.merge(Spree::Product.ransack({scope_name => scope_attribute}).result)
|
|
50
50
|
end
|
|
51
51
|
end if search
|
|
52
52
|
base_scope
|
data/lib/spree/core/version.rb
CHANGED
data/lib/spree/core.rb
CHANGED
|
@@ -5,7 +5,6 @@ require 'awesome_nested_set'
|
|
|
5
5
|
require 'cancan'
|
|
6
6
|
require 'kaminari'
|
|
7
7
|
require 'mail'
|
|
8
|
-
require 'monetize'
|
|
9
8
|
require 'paperclip'
|
|
10
9
|
require 'paranoia'
|
|
11
10
|
require 'ransack'
|
|
@@ -17,9 +16,9 @@ module Spree
|
|
|
17
16
|
|
|
18
17
|
def self.user_class
|
|
19
18
|
if @@user_class.is_a?(Class)
|
|
20
|
-
raise "Spree.user_class MUST be a String
|
|
21
|
-
elsif @@user_class.is_a?(String)
|
|
22
|
-
@@user_class.
|
|
19
|
+
raise "Spree.user_class MUST be a String object, not a Class object."
|
|
20
|
+
elsif @@user_class.is_a?(String)
|
|
21
|
+
@@user_class.constantize
|
|
23
22
|
end
|
|
24
23
|
end
|
|
25
24
|
|
|
@@ -55,30 +54,3 @@ require 'spree/core/product_duplicator'
|
|
|
55
54
|
ActiveRecord::Base.class_eval do
|
|
56
55
|
include CollectiveIdea::Acts::NestedSet
|
|
57
56
|
end
|
|
58
|
-
|
|
59
|
-
# Monkey patch to give us miliseconds precision in timestamps
|
|
60
|
-
module ActiveSupport
|
|
61
|
-
class TimeWithZone
|
|
62
|
-
# Coerces time to a string for JSON encoding. The default format is ISO 8601. You can get
|
|
63
|
-
# %Y/%m/%d %H:%M:%S +offset style by setting <tt>ActiveSupport::JSON::Encoding.use_standard_json_time_format</tt>
|
|
64
|
-
# to false.
|
|
65
|
-
#
|
|
66
|
-
# ==== Examples
|
|
67
|
-
#
|
|
68
|
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
|
|
69
|
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
|
|
70
|
-
# # => "2005-02-01T15:15:10.001Z"
|
|
71
|
-
#
|
|
72
|
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
|
|
73
|
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
|
|
74
|
-
# # => "2005/02/01 15:15:10 +0000"
|
|
75
|
-
#
|
|
76
|
-
def as_json(options = nil)
|
|
77
|
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
|
|
78
|
-
xmlschema(3)
|
|
79
|
-
else
|
|
80
|
-
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
data/lib/spree/i18n.rb
CHANGED
data/lib/spree/money.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
1
|
require 'money'
|
|
4
2
|
|
|
5
3
|
module Spree
|
|
@@ -9,187 +7,18 @@ module Spree
|
|
|
9
7
|
delegate :cents, :to => :money
|
|
10
8
|
|
|
11
9
|
def initialize(amount, options={})
|
|
12
|
-
@money =
|
|
10
|
+
@money = ::Money.parse([amount, (options[:currency] || Spree::Config[:currency])].join)
|
|
13
11
|
@options = {}
|
|
14
12
|
@options[:with_currency] = Spree::Config[:display_currency]
|
|
15
13
|
@options[:symbol_position] = Spree::Config[:currency_symbol_position].to_sym
|
|
16
14
|
@options[:no_cents] = Spree::Config[:hide_cents]
|
|
17
15
|
@options[:decimal_mark] = Spree::Config[:currency_decimal_mark]
|
|
18
16
|
@options[:thousands_separator] = Spree::Config[:currency_thousands_separator]
|
|
19
|
-
@options[:sign_before_symbol] = Spree::Config[:currency_sign_before_symbol]
|
|
20
17
|
@options.merge!(options)
|
|
21
18
|
# Must be a symbol because the Money gem doesn't do the conversion
|
|
22
19
|
@options[:symbol_position] = @options[:symbol_position].to_sym
|
|
23
20
|
end
|
|
24
21
|
|
|
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
22
|
def to_s
|
|
194
23
|
@money.format(@options)
|
|
195
24
|
end
|
|
@@ -199,15 +28,11 @@ module Spree
|
|
|
199
28
|
if options[:html]
|
|
200
29
|
# 1) prevent blank, breaking spaces
|
|
201
30
|
# 2) prevent escaping of HTML character entities
|
|
202
|
-
output = output.
|
|
31
|
+
output = output.gsub(" ", " ").html_safe
|
|
203
32
|
end
|
|
204
33
|
output
|
|
205
34
|
end
|
|
206
35
|
|
|
207
|
-
def as_json(*)
|
|
208
|
-
to_s
|
|
209
|
-
end
|
|
210
|
-
|
|
211
36
|
def ==(obj)
|
|
212
37
|
@money == obj.money
|
|
213
38
|
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PermittedAttributes
|
|
3
|
+
ATTRIBUTES = [
|
|
4
|
+
:address_attributes,
|
|
5
|
+
:checkout_attributes,
|
|
6
|
+
:image_attributes,
|
|
7
|
+
:inventory_unit_attributes,
|
|
8
|
+
:line_item_attributes,
|
|
9
|
+
:option_type_attributes,
|
|
10
|
+
:option_value_attributes,
|
|
11
|
+
:payment_attributes,
|
|
12
|
+
:product_attributes,
|
|
13
|
+
:product_properties_attributes,
|
|
14
|
+
:property_attributes,
|
|
15
|
+
:return_authorization_attributes,
|
|
16
|
+
:shipment_attributes,
|
|
17
|
+
:source_attributes,
|
|
18
|
+
:stock_item_attributes,
|
|
19
|
+
:stock_location_attributes,
|
|
20
|
+
:stock_movement_attributes,
|
|
21
|
+
:taxon_attributes,
|
|
22
|
+
:taxonomy_attributes,
|
|
23
|
+
:user_attributes,
|
|
24
|
+
:variant_attributes
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
mattr_reader *ATTRIBUTES
|
|
28
|
+
|
|
29
|
+
@@address_attributes = [
|
|
30
|
+
:firstname, :lastname, :address1, :address2,
|
|
31
|
+
:city, :country_id, :state_id, :zipcode, :phone,
|
|
32
|
+
:state_name, :alternative_phone, :company]
|
|
33
|
+
|
|
34
|
+
@@checkout_attributes = [:email, :use_billing, :shipping_method_id, :coupon_code]
|
|
35
|
+
|
|
36
|
+
@@image_attributes = [:alt, :attachment, :position, :viewable_type, :viewable_id]
|
|
37
|
+
|
|
38
|
+
@@inventory_unit_attributes = [:shipment, :variant_id]
|
|
39
|
+
|
|
40
|
+
@@line_item_attributes = [:id, :variant_id, :quantity]
|
|
41
|
+
|
|
42
|
+
@@option_type_attributes = [:name, :presentation, :option_values_attributes]
|
|
43
|
+
|
|
44
|
+
@@option_value_attributes = [:name, :presentation]
|
|
45
|
+
|
|
46
|
+
@@payment_attributes = [:amount, :payment_method_id]
|
|
47
|
+
|
|
48
|
+
@@product_properties_attributes = [:property_name, :value, :position]
|
|
49
|
+
|
|
50
|
+
@@product_attributes = [
|
|
51
|
+
:name, :description, :available_on, :permalink, :meta_description,
|
|
52
|
+
:meta_keywords, :price, :sku, :deleted_at, :prototype_id,
|
|
53
|
+
:option_values_hash, :weight, :height, :width, :depth,
|
|
54
|
+
:shipping_category_id, :tax_category_id, :product_properties_attributes,
|
|
55
|
+
:variants_attributes, :taxon_ids, :option_type_ids, :cost_currency, :cost_price]
|
|
56
|
+
|
|
57
|
+
@@property_attributes = [:name, :presentation]
|
|
58
|
+
|
|
59
|
+
@@return_authorization_attributes = [:amount, :reason, :stock_location_id]
|
|
60
|
+
|
|
61
|
+
@@shipment_attributes = [
|
|
62
|
+
:order, :special_instructions, :stock_location_id, :id,
|
|
63
|
+
:tracking, :address, :inventory_units, :selected_shipping_rate_id]
|
|
64
|
+
|
|
65
|
+
# month / year may be provided by some sources, or others may elect to use one field
|
|
66
|
+
@@source_attributes = [
|
|
67
|
+
:number, :month, :year, :expiry, :verification_value,
|
|
68
|
+
:first_name, :last_name, :cc_type]
|
|
69
|
+
|
|
70
|
+
@@stock_item_attributes = [:variant, :stock_location, :backorderable, :variant_id]
|
|
71
|
+
|
|
72
|
+
@@stock_location_attributes = [
|
|
73
|
+
:name, :active, :address1, :address2, :city, :zipcode,
|
|
74
|
+
:backorderable_default, :state_name, :state_id, :country_id, :phone,
|
|
75
|
+
:propagate_all_variants]
|
|
76
|
+
|
|
77
|
+
@@stock_movement_attributes = [
|
|
78
|
+
:quantity, :stock_item, :stock_item_id, :originator, :action]
|
|
79
|
+
|
|
80
|
+
@@taxonomy_attributes = [:name]
|
|
81
|
+
|
|
82
|
+
@@taxon_attributes = [
|
|
83
|
+
:name, :parent_id, :position, :icon, :description, :permalink, :taxonomy_id,
|
|
84
|
+
:meta_description, :meta_keywords, :meta_title]
|
|
85
|
+
|
|
86
|
+
# TODO Should probably use something like Spree.user_class.attributes
|
|
87
|
+
@@user_attributes = [:email, :password, :password_confirmation]
|
|
88
|
+
|
|
89
|
+
@@variant_attributes = [
|
|
90
|
+
:name, :presentation, :cost_price, :lock_version,
|
|
91
|
+
:position, :option_value_ids,
|
|
92
|
+
:product_id, :option_values_attributes, :price,
|
|
93
|
+
:weight, :height, :width, :depth, :sku, :cost_currency]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -13,7 +13,7 @@ module Spree
|
|
|
13
13
|
if @order.adjustments.promotion.eligible.detect { |p| p.originator.promotion.code == @order.coupon_code }.present?
|
|
14
14
|
return { :coupon_applied? => true, :notice => Spree.t(:coupon_code_already_applied) }
|
|
15
15
|
else
|
|
16
|
-
promotion = Spree::Promotion.
|
|
16
|
+
promotion = Spree::Promotion.find_by(code: @order.coupon_code)
|
|
17
17
|
if promotion.present?
|
|
18
18
|
handle_present_promotion(promotion)
|
|
19
19
|
else
|
|
@@ -32,17 +32,9 @@ module Spree
|
|
|
32
32
|
return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded?
|
|
33
33
|
|
|
34
34
|
event_name = "spree.checkout.coupon_code_added"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
determine_promotion_application_result(promo)
|
|
39
|
-
else
|
|
40
|
-
# if action is create line items
|
|
41
|
-
return { :coupon_applied? => true, :success => Spree.t(:coupon_code_applied) }
|
|
42
|
-
end
|
|
43
|
-
else
|
|
44
|
-
return { :coupon_applied? => false, :error => Spree.t(:coupon_code_not_eligible) }
|
|
45
|
-
end
|
|
35
|
+
promotion.activate(:coupon_code => @order.coupon_code, :order => @order)
|
|
36
|
+
promo = @order.adjustments.includes(:originator).promotion.detect { |p| p.originator.promotion.code == @order.coupon_code }
|
|
37
|
+
determine_promotion_application_result(promo)
|
|
46
38
|
end
|
|
47
39
|
|
|
48
40
|
def promotion_expired
|