spree_core 3.0.1 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
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]