spree_core 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/friendly_id/slug_decorator.rb +3 -0
  3. data/app/models/spree/adjustment.rb +1 -1
  4. data/app/models/spree/calculator.rb +5 -0
  5. data/app/models/spree/customer_return.rb +22 -16
  6. data/app/models/spree/gateway/bogus.rb +4 -0
  7. data/app/models/spree/line_item.rb +1 -1
  8. data/app/models/spree/order.rb +22 -48
  9. data/app/models/spree/order/checkout.rb +270 -255
  10. data/app/models/spree/order_contents.rb +64 -61
  11. data/app/models/spree/order_merger.rb +65 -0
  12. data/app/models/spree/payment.rb +5 -0
  13. data/app/models/spree/payment/processing.rb +2 -1
  14. data/app/models/spree/payment_method/check.rb +12 -2
  15. data/app/models/spree/product.rb +5 -0
  16. data/app/models/spree/promotion_handler/cart.rb +18 -14
  17. data/app/models/spree/shipment.rb +1 -1
  18. data/app/models/spree/stock/availability_validator.rb +10 -9
  19. data/app/models/spree/stock/content_item.rb +8 -0
  20. data/app/models/spree/stock/package.rb +8 -0
  21. data/app/models/spree/stock_item.rb +5 -1
  22. data/app/models/spree/stock_movement.rb +6 -1
  23. data/app/models/spree/variant.rb +18 -15
  24. data/app/models/spree/zone.rb +39 -29
  25. data/app/views/spree/order_mailer/cancel_email.html.erb +1 -1
  26. data/app/views/spree/order_mailer/cancel_email.text.erb +1 -1
  27. data/app/views/spree/order_mailer/confirm_email.html.erb +1 -1
  28. data/app/views/spree/order_mailer/confirm_email.text.erb +1 -1
  29. data/config/initializers/user_class_extensions.rb +4 -0
  30. data/config/locales/en.yml +4 -3
  31. data/db/default/spree/default_reimbursement_type.rb +1 -0
  32. data/db/migrate/20150515211137_fix_adjustment_order_id.rb +70 -0
  33. data/db/migrate/20150522181728_add_deleted_at_to_friendly_id_slugs.rb +6 -0
  34. data/db/migrate/20150609093816_increase_scale_on_pre_tax_amounts.rb +16 -0
  35. data/db/migrate/20150707204155_enable_acts_as_paranoid_on_calculators.rb +6 -0
  36. data/lib/spree/core/controller_helpers/auth.rb +1 -1
  37. data/lib/spree/core/engine.rb +7 -0
  38. data/lib/spree/core/validators/email.rb +7 -3
  39. data/lib/spree/core/version.rb +1 -1
  40. data/lib/spree/permitted_attributes.rb +2 -2
  41. data/lib/spree/testing_support/common_rake.rb +3 -8
  42. data/lib/spree/testing_support/factories.rb +1 -1
  43. data/lib/spree/testing_support/factories/order_factory.rb +11 -0
  44. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  45. metadata +11 -4
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class Variant < Spree::Base
3
3
  acts_as_paranoid
4
- acts_as_list
4
+ acts_as_list scope: :product
5
5
 
6
6
  include Spree::DefaultPrice
7
7
 
@@ -37,13 +37,20 @@ module Spree
37
37
  validates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) }
38
38
 
39
39
  after_create :create_stock_items
40
- after_create :set_position
41
40
  after_create :set_master_out_of_stock, unless: :is_master?
42
41
 
43
42
  after_touch :clear_in_stock_cache
44
43
 
45
44
  scope :in_stock, -> { joins(:stock_items).where('count_on_hand > ? OR track_inventory = ?', 0, false) }
46
45
 
46
+ LOCALIZED_NUMBERS = %w(cost_price weight depth width height)
47
+
48
+ LOCALIZED_NUMBERS.each do |m|
49
+ define_method("#{m}=") do |argument|
50
+ self[m] = Spree::LocalizedNumber.parse(argument) if argument.present?
51
+ end
52
+ end
53
+
47
54
  def self.active(currency = nil)
48
55
  joins(:prices).where(deleted_at: nil).where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
49
56
  end
@@ -60,14 +67,6 @@ module Spree
60
67
  end
61
68
  end
62
69
 
63
- def cost_price=(price)
64
- self[:cost_price] = Spree::LocalizedNumber.parse(price) if price.present?
65
- end
66
-
67
- def weight=(weight)
68
- self[:weight] = Spree::LocalizedNumber.parse(weight) if weight.present?
69
- end
70
-
71
70
  # returns number of units currently on backorder for this variant.
72
71
  def on_backorder
73
72
  inventory_units.with_state('backordered').size
@@ -153,7 +152,7 @@ module Spree
153
152
  end
154
153
 
155
154
  def price_in(currency)
156
- prices.select{ |price| price.currency == currency }.first || Spree::Price.new(variant_id: self.id, currency: currency)
155
+ prices.detect { |price| price.currency == currency } || Spree::Price.new(variant_id: id, currency: currency)
157
156
  end
158
157
 
159
158
  def amount_in(currency)
@@ -214,6 +213,14 @@ module Spree
214
213
  self.track_inventory? && Spree::Config.track_inventory_levels
215
214
  end
216
215
 
216
+ def volume
217
+ (width || 0) * (height || 0) * (depth || 0)
218
+ end
219
+
220
+ def dimension
221
+ (width || 0) + (height || 0) + (depth || 0)
222
+ end
223
+
217
224
  private
218
225
 
219
226
  def set_master_out_of_stock
@@ -245,10 +252,6 @@ module Spree
245
252
  end
246
253
  end
247
254
 
248
- def set_position
249
- self.update_column(:position, product.variants.maximum(:position).to_i + 1)
250
- end
251
-
252
255
  def in_stock_cache_key
253
256
  "variant-#{id}-in_stock"
254
257
  end
@@ -2,12 +2,10 @@ module Spree
2
2
  class Zone < Spree::Base
3
3
  has_many :zone_members, dependent: :destroy, class_name: "Spree::ZoneMember", inverse_of: :zone
4
4
  has_many :tax_rates, dependent: :destroy, inverse_of: :zone
5
- has_many :countries, through: :zone_members, source: :zoneable,
6
- source_type: "Spree::Country"
7
- has_many :states, through: :zone_members, source: :zoneable,
8
- source_type: "Spree::State"
5
+ has_many :countries, through: :zone_members, source: :zoneable, source_type: "Spree::Country"
6
+ has_many :states, through: :zone_members, source: :zoneable, source_type: "Spree::State"
9
7
 
10
- has_and_belongs_to_many :shipping_methods, :join_table => 'spree_shipping_methods_zones'
8
+ has_and_belongs_to_many :shipping_methods, join_table: 'spree_shipping_methods_zones'
11
9
 
12
10
  validates :name, presence: true, uniqueness: { allow_blank: true }
13
11
 
@@ -47,10 +45,14 @@ module Spree
47
45
  # Returns the matching zone with the highest priority zone type (State, Country, Zone.)
48
46
  # Returns nil in the case of no matches.
49
47
  def self.match(address)
50
- return unless address and matches = self.includes(:zone_members).
51
- order('spree_zones.zone_members_count', 'spree_zones.created_at').
52
- where("(spree_zone_members.zoneable_type = 'Spree::Country' AND spree_zone_members.zoneable_id = ?) OR (spree_zone_members.zoneable_type = 'Spree::State' AND spree_zone_members.zoneable_id = ?)", address.country_id, address.state_id).
53
- references(:zones)
48
+ return unless address &&
49
+ matches = includes(:zone_members).
50
+ order('spree_zones.zone_members_count', 'spree_zones.created_at').
51
+ where("(spree_zone_members.zoneable_type = 'Spree::Country' AND " +
52
+ "spree_zone_members.zoneable_id = ?) OR " +
53
+ "(spree_zone_members.zoneable_type = 'Spree::State' AND " +
54
+ "spree_zone_members.zoneable_id = ?)", address.country_id, address.state_id).
55
+ references(:zones)
54
56
 
55
57
  ['state', 'country'].each do |zone_kind|
56
58
  if match = matches.detect { |zone| zone_kind == zone.kind }
@@ -96,9 +98,12 @@ module Spree
96
98
  # convenience method for returning the countries contained within a zone
97
99
  def country_list
98
100
  @countries ||= case kind
99
- when 'country' then zoneables
100
- when 'state' then zoneables.collect(&:country)
101
- else []
101
+ when 'country' then
102
+ zoneables
103
+ when 'state' then
104
+ zoneables.collect(&:country)
105
+ else
106
+ []
102
107
  end.flatten.compact.uniq
103
108
  end
104
109
 
@@ -139,11 +144,15 @@ module Spree
139
144
  # Indicates whether the specified zone falls entirely within the zone performing
140
145
  # the check.
141
146
  def contains?(target)
142
- return false if kind == 'state' && target.kind == 'country'
147
+ return false if state? && target.country?
143
148
  return false if zone_members.empty? || target.zone_members.empty?
144
149
 
145
150
  if kind == target.kind
146
- return false if (target.countries.pluck(:id) - countries.pluck(:id)).present?
151
+ if state?
152
+ return false if (target.states.pluck(:id) - states.pluck(:id)).present?
153
+ elsif country?
154
+ return false if (target.countries.pluck(:id) - countries.pluck(:id)).present?
155
+ end
147
156
  else
148
157
  return false if (target.states.pluck(:country_id) - countries.pluck(:id)).present?
149
158
  end
@@ -151,24 +160,25 @@ module Spree
151
160
  end
152
161
 
153
162
  private
154
- def remove_defunct_members
155
- if zone_members.any?
156
- zone_members.where('zoneable_id IS NULL OR zoneable_type != ?', "Spree::#{kind.classify}").destroy_all
157
- end
158
- end
159
163
 
160
- def remove_previous_default
161
- Spree::Zone.where('id != ?', self.id).update_all(default_tax: false) if default_tax
164
+ def remove_defunct_members
165
+ if zone_members.any?
166
+ zone_members.where('zoneable_id IS NULL OR zoneable_type != ?', "Spree::#{kind.classify}").destroy_all
162
167
  end
168
+ end
163
169
 
164
- def set_zone_members(ids, type)
165
- zone_members.destroy_all
166
- ids.reject{ |id| id.blank? }.map do |id|
167
- member = ZoneMember.new
168
- member.zoneable_type = type
169
- member.zoneable_id = id
170
- members << member
171
- end
170
+ def remove_previous_default
171
+ Spree::Zone.where('id != ?', id).update_all(default_tax: false) if default_tax
172
+ end
173
+
174
+ def set_zone_members(ids, type)
175
+ zone_members.destroy_all
176
+ ids.reject(&:blank?).map do |id|
177
+ member = ZoneMember.new
178
+ member.zoneable_type = type
179
+ member.zoneable_id = id
180
+ members << member
172
181
  end
182
+ end
173
183
  end
174
184
  end
@@ -18,7 +18,7 @@
18
18
  <%= raw(item.variant.product.name) %>
19
19
  <%= raw(item.variant.options_text) -%>
20
20
  </td>
21
- <td>(<%=item.quantity%>) @ <%= item.single_money %> = <%= item.display_amount %></td>
21
+ <td>(<%=item.quantity%>) <%= Spree.t('at_symbol') %> <%= item.single_money %> = <%= item.display_amount %></td>
22
22
  </tr>
23
23
  <% end %>
24
24
  <tr>
@@ -6,7 +6,7 @@
6
6
  <%= Spree.t('order_mailer.cancel_email.order_summary_canceled') %>
7
7
  ============================================================
8
8
  <% @order.line_items.each do |item| %>
9
- <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= item.single_money %> = <%= item.display_amount %>
9
+ <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) <%= Spree.t('at_symbol') %> <%= item.single_money %> = <%= item.display_amount %>
10
10
  <% end %>
11
11
  ============================================================
12
12
  <%= Spree.t('order_mailer.cancel_email.subtotal') %> <%= @order.display_item_total %>
@@ -18,7 +18,7 @@
18
18
  <%= raw(item.variant.product.name) %>
19
19
  <%= raw(item.variant.options_text) -%>
20
20
  </td>
21
- <td>(<%=item.quantity%>) @ <%= item.single_money %> = <%= item.display_amount %></td>
21
+ <td>(<%=item.quantity%>) <%= Spree.t('at_symbol') %> <%= item.single_money %> = <%= item.display_amount %></td>
22
22
  </tr>
23
23
  <% end %>
24
24
  <tr>
@@ -6,7 +6,7 @@
6
6
  <%= Spree.t('order_mailer.confirm_email.order_summary') %>
7
7
  ============================================================
8
8
  <% @order.line_items.each do |item| %>
9
- <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= item.single_money %> = <%= item.display_amount %>
9
+ <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) <%= Spree.t('at_symbol') %> <%= item.single_money %> = <%= item.display_amount %>
10
10
  <% end %>
11
11
  ============================================================
12
12
  <%= Spree.t('order_mailer.confirm_email.subtotal') %> <%= @order.display_item_total %>
@@ -24,6 +24,10 @@ Spree::Core::Engine.config.to_prepare do
24
24
  def last_incomplete_spree_order
25
25
  orders.incomplete.order('created_at DESC').first
26
26
  end
27
+
28
+ def analytics_id
29
+ id
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -463,6 +463,7 @@ en:
463
463
  are_you_sure: Are you sure?
464
464
  are_you_sure_delete: Are you sure you want to delete this record?
465
465
  associated_adjustment_closed: The associated adjustment is closed, and will not be recalculated. Do you want to open it?
466
+ at_symbol: '@'
466
467
  authorization_failure: Authorization Failure
467
468
  authorized: Authorized
468
469
  auto_capture: Auto Capture
@@ -767,6 +768,7 @@ en:
767
768
  line_item_adjustments: "Line item adjustments"
768
769
  list: List
769
770
  loading: Loading
771
+ loading_tree: Loading tree. Please wait…
770
772
  locale_changed: Locale Changed
771
773
  location: Location
772
774
  lock: Lock
@@ -798,6 +800,7 @@ en:
798
800
  meta_title: Meta Title
799
801
  metadata: Metadata
800
802
  minimal_amount: Minimal Amount
803
+ missing_return_authorization: ! 'Missing Return Authorization for %{item_name}.'
801
804
  month: Month
802
805
  more: More
803
806
  move_stock_between_locations: Move Stock Between Locations
@@ -805,7 +808,7 @@ en:
805
808
  my_orders: My Orders
806
809
  name: Name
807
810
  name_on_card: Name on card
808
- name_or_sku: Name or SKU (enter at least first 4 characters of product name)
811
+ name_or_sku: Name or SKU (enter at least first 3 characters of product name)
809
812
  new: New
810
813
  new_adjustment: New Adjustment
811
814
  new_customer: New Customer
@@ -928,7 +931,6 @@ en:
928
931
  order_total: Order Total
929
932
  order_updated: Order Updated
930
933
  orders: Orders
931
- other_items_in_other: Other Items in Order
932
934
  out_of_stock: Out of Stock
933
935
  overview: Overview
934
936
  package_from: package from
@@ -1126,7 +1128,6 @@ en:
1126
1128
  return_number: Return Number
1127
1129
  return_quantity: Return Quantity
1128
1130
  returned: Returned
1129
- returns: Returns
1130
1131
  review: Review
1131
1132
  risk: Risk
1132
1133
  risk_analysis: Risk Analysis
@@ -0,0 +1 @@
1
+ Spree::RefundReason.find_or_create_by(name: "Return processing", mutable: false)
@@ -0,0 +1,70 @@
1
+ class FixAdjustmentOrderId < ActiveRecord::Migration
2
+ def change
3
+ say 'Populate order_id from adjustable_id where appropriate'
4
+ execute(<<-SQL.squish)
5
+ UPDATE
6
+ spree_adjustments
7
+ SET
8
+ order_id = adjustable_id
9
+ WHERE
10
+ adjustable_type = 'Spree::Order'
11
+ ;
12
+ SQL
13
+
14
+ # Submitter of change does not care about MySQL, as it is not officially supported.
15
+ # Still spree officials decided to provide a working code path for MySQL users, hence
16
+ # submitter made a AR code path he could validate on PostgreSQL.
17
+ #
18
+ # Whoever runs a big enough MySQL installation where the AR solution hurts:
19
+ # Will have to write a better MySQL specific equivalent.
20
+ if Spree::Order.connection.adapter_name.eql?('MySQL')
21
+ Spree::Adjustment.where(adjustable_type: 'Spree::LineItem').find_each do |adjustment|
22
+ adjustment.update_columns(order_id: Spree::LineItem.find(adjustment.adjustable_id).order_id)
23
+ end
24
+ else
25
+ execute(<<-SQL.squish)
26
+ UPDATE
27
+ spree_adjustments
28
+ SET
29
+ order_id =
30
+ (SELECT order_id FROM spree_line_items WHERE spree_line_items.id = spree_adjustments.adjustable_id)
31
+ WHERE
32
+ adjustable_type = 'Spree::LineItem'
33
+ SQL
34
+ end
35
+
36
+ say 'Fix schema for spree_adjustments order_id column'
37
+ change_table :spree_adjustments do |t|
38
+ t.change :order_id, :integer, null: false
39
+ end
40
+
41
+ # Improved schema for postgresql, uncomment if you like it:
42
+ #
43
+ # # Negated Logical implication.
44
+ # #
45
+ # # When adjustable_type is 'Spree::Order' (p) the adjustable_id must be order_id (q).
46
+ # #
47
+ # # When adjustable_type is NOT 'Spree::Order' the adjustable id allowed to be any value (including of order_id in
48
+ # # case foreign keys match). XOR does not work here.
49
+ # #
50
+ # # Postgresql does not have an operator for logical implication. So we need to build the following truth table
51
+ # # via AND with OR:
52
+ # #
53
+ # # p q | CHECK = !(p -> q)
54
+ # # -----------
55
+ # # t t | t
56
+ # # t f | f
57
+ # # f t | t
58
+ # # f f | t
59
+ # #
60
+ # # According to de-morgans law the logical implication q -> p is equivalent to !p || q
61
+ # #
62
+ # execute(<<-SQL.squish)
63
+ # ALTER TABLE ONLY spree_adjustments
64
+ # ADD CONSTRAINT fk_spree_adjustments FOREIGN KEY (order_id)
65
+ # REFERENCES spree_orders(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
66
+ # ADD CONSTRAINT check_spree_adjustments_order_id CHECK
67
+ # (adjustable_type <> 'Spree::Order' OR order_id = adjustable_id);
68
+ # SQL
69
+ end
70
+ end
@@ -0,0 +1,6 @@
1
+ class AddDeletedAtToFriendlyIdSlugs < ActiveRecord::Migration
2
+ def change
3
+ add_column :friendly_id_slugs, :deleted_at, :datetime
4
+ add_index :friendly_id_slugs, :deleted_at
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ class IncreaseScaleOnPreTaxAmounts < ActiveRecord::Migration
2
+ def change
3
+ # set pre_tax_amount on shipments to discounted_amount - included_tax_total
4
+ # so that the null: false option on the shipment pre_tax_amount doesn't generate
5
+ # errors.
6
+ #
7
+ execute(<<-SQL)
8
+ UPDATE spree_shipments
9
+ SET pre_tax_amount = (cost + promo_total) - included_tax_total
10
+ WHERE pre_tax_amount IS NULL;
11
+ SQL
12
+
13
+ change_column :spree_line_items, :pre_tax_amount, :decimal, precision: 12, scale: 4, default: 0.0, null: false
14
+ change_column :spree_shipments, :pre_tax_amount, :decimal, precision: 12, scale: 4, default: 0.0, null: false
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ class EnableActsAsParanoidOnCalculators < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_calculators, :deleted_at, :datetime
4
+ add_index :spree_calculators, :deleted_at
5
+ end
6
+ end
@@ -19,7 +19,7 @@ module Spree
19
19
  end
20
20
 
21
21
  def redirect_back_or_default(default)
22
- redirect_to(session["spree_user_return_to"] || default)
22
+ redirect_to(session["spree_user_return_to"] || request.env["HTTP_REFERER"] || default)
23
23
  session["spree_user_return_to"] = nil
24
24
  end
25
25
 
@@ -102,6 +102,13 @@ module Spree
102
102
  initializer "spree.core.checking_migrations" do |app|
103
103
  Migrations.new(config, engine_name).check
104
104
  end
105
+
106
+ config.to_prepare do
107
+ # Load application's model / class decorators
108
+ Dir.glob(File.join(File.dirname(__FILE__), '../../../app/**/*_decorator*.rb')) do |c|
109
+ Rails.configuration.cache_classes ? require(c) : load(c)
110
+ end
111
+ end
105
112
  end
106
113
  end
107
114
  end
@@ -1,7 +1,11 @@
1
1
  class EmailValidator < ActiveModel::EachValidator
2
- def validate_each(record,attribute,value)
3
- unless value =~ /\A([^@\.]|[^@\.]([^@\s]*)[^@\.])@([^@\s]+\.)+[^@\s]+\z/
4
- record.errors.add(attribute, :invalid, {:value => value}.merge!(options))
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ %r{\A(([A-Za-z0-9]+_+)|
4
+ ([A-Za-z0-9]+\-+)|
5
+ ([A-Za-z0-9]+\.+)|
6
+ ([A-Za-z0-9]+\++))*[A-Za-z0-9_]+@((\w+\-+)|
7
+ (\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}\z}xi
8
+ record.errors.add(attribute, :invalid, { value: value }.merge!(options))
5
9
  end
6
10
  end
7
11
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
2
  def self.version
3
- "3.0.1"
3
+ '3.0.2'
4
4
  end
5
5
  end
@@ -61,8 +61,8 @@ module Spree
61
61
  :meta_keywords, :price, :sku, :deleted_at, :prototype_id,
62
62
  :option_values_hash, :weight, :height, :width, :depth,
63
63
  :shipping_category_id, :tax_category_id,
64
- :taxon_ids, :cost_currency, :cost_price,
65
- option_type_ids: []
64
+ :cost_currency, :cost_price,
65
+ option_type_ids: [], taxon_ids: []
66
66
  ]
67
67
 
68
68
  @@property_attributes = [:name, :presentation]