spree_core 2.1.3 → 2.1.4

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/admin/images_helper.rb +1 -1
  3. data/app/helpers/spree/base_helper.rb +5 -2
  4. data/app/models/spree/address.rb +9 -1
  5. data/app/models/spree/adjustment.rb +2 -2
  6. data/app/models/spree/calculator/default_tax.rb +5 -1
  7. data/app/models/spree/credit_card.rb +2 -0
  8. data/app/models/spree/gateway.rb +1 -1
  9. data/app/models/spree/inventory_unit.rb +5 -4
  10. data/app/models/spree/legacy_user.rb +2 -11
  11. data/app/models/spree/line_item.rb +2 -3
  12. data/app/models/spree/log_entry.rb +4 -0
  13. data/app/models/spree/option_type.rb +6 -0
  14. data/app/models/spree/option_value.rb +12 -1
  15. data/app/models/spree/order.rb +34 -16
  16. data/app/models/spree/order/checkout.rb +4 -0
  17. data/app/models/spree/order_inventory.rb +1 -1
  18. data/app/models/spree/payment.rb +10 -2
  19. data/app/models/spree/payment/processing.rb +5 -4
  20. data/app/models/spree/payment_method.rb +2 -0
  21. data/app/models/spree/price.rb +5 -0
  22. data/app/models/spree/product.rb +6 -5
  23. data/app/models/spree/product/scopes.rb +12 -6
  24. data/app/models/spree/product_property.rb +1 -1
  25. data/app/models/spree/promotion.rb +1 -8
  26. data/app/models/spree/promotion/rules/user_logged_in.rb +1 -3
  27. data/app/models/spree/property.rb +8 -0
  28. data/app/models/spree/shipment.rb +9 -14
  29. data/app/models/spree/shipping_method.rb +3 -2
  30. data/app/models/spree/shipping_rate.rb +7 -9
  31. data/app/models/spree/stock/estimator.rb +21 -14
  32. data/app/models/spree/stock/package.rb +1 -1
  33. data/app/models/spree/stock/packer.rb +1 -1
  34. data/app/models/spree/stock/quantifier.rb +11 -2
  35. data/app/models/spree/stock_item.rb +2 -2
  36. data/app/models/spree/stock_location.rb +8 -0
  37. data/app/models/spree/stock_movement.rb +3 -1
  38. data/app/models/spree/taxon.rb +2 -2
  39. data/app/models/spree/variant.rb +19 -4
  40. data/app/models/spree/zone.rb +1 -1
  41. data/app/views/spree/shared/_routes.html.erb +1 -1
  42. data/config/locales/en.yml +15 -1
  43. data/db/default/spree/countries.rb +7 -7
  44. data/db/migrate/20130417120034_add_index_to_source_columns_on_adjustments.rb +5 -0
  45. data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +5 -2
  46. data/db/migrate/20131026154747_add_track_inventory_to_variant.rb +5 -0
  47. data/db/migrate/20131120234456_add_updated_at_to_variants.rb +5 -0
  48. data/db/migrate/20131211192741_unique_shipping_method_categories.rb +24 -0
  49. data/db/migrate/20140120160805_add_index_to_variant_id_and_currency_on_prices.rb +5 -0
  50. data/lib/generators/spree/dummy/dummy_generator.rb +14 -3
  51. data/lib/generators/spree/dummy/templates/rails/database.yml +10 -0
  52. data/lib/spree/core.rb +3 -0
  53. data/lib/spree/core/controller_helpers/order.rb +4 -1
  54. data/lib/spree/core/controller_helpers/ssl.rb +5 -7
  55. data/lib/spree/core/controller_helpers/strong_parameters.rb +6 -0
  56. data/lib/spree/core/delegate_belongs_to.rb +16 -10
  57. data/lib/spree/core/engine.rb +11 -2
  58. data/lib/spree/core/mail_method.rb +27 -0
  59. data/lib/spree/core/mail_settings.rb +33 -38
  60. data/lib/spree/core/permalinks.rb +5 -1
  61. data/lib/spree/core/s3_support.rb +1 -1
  62. data/lib/spree/core/user_address.rb +30 -0
  63. data/lib/spree/core/validators/email.rb +9 -3
  64. data/lib/spree/core/version.rb +1 -1
  65. data/lib/spree/i18n.rb +1 -0
  66. data/lib/spree/migrations.rb +55 -0
  67. data/lib/spree/money.rb +171 -1
  68. data/lib/spree/permitted_attributes.rb +6 -6
  69. data/lib/spree/testing_support/capybara_ext.rb +6 -5
  70. data/lib/spree/testing_support/controller_requests.rb +20 -4
  71. data/lib/spree/testing_support/factories/product_factory.rb +4 -0
  72. data/lib/spree/testing_support/factories/variant_factory.rb +15 -0
  73. metadata +158 -164
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b8194bdbcc2f3d26d2662205e0deed23ed0bf800
4
- data.tar.gz: 232ddcdb7e3b3b8cb25b1029f77015331b2b7043
3
+ metadata.gz: a4097b8c35f0d4c860dbaeff13518f90e790880e
4
+ data.tar.gz: 67e8f16ef1ebf41b7fd672a31d17ad3736439856
5
5
  SHA512:
6
- metadata.gz: da652bb0512643d231a1826e0af01ea36fffc76b2b9f91bffe1ada65133b6ec8d2a6c365f38d88235209db209565816dc44fbfc5868f79dd1b7a111a5c9cc2de
7
- data.tar.gz: 9db7c280e5907596695bdb35b9098611ff778b27cbb5fc8f10ca789b3cf86bc7e0aeeda0e6a62b01dc01da7d0fc88ba23ca74a193c301bec105f5f4bfe7805cd
6
+ metadata.gz: 83ed87a684e91dac0ce56b9d1836d0d4e1cee96cb6dd388e1d68dba07fa9d9140cabf3f77f7c50298e42ae026e1827a668aa8355256965044c0bc097157a69db
7
+ data.tar.gz: a6bbe1a58e61240068e92db9d3f8a2b577b939e363ff286c7ba95f0738884bc64c1f54339b4163aa51053bec6e25323f349c64fb42a08b93c852d9a19ca99901
@@ -6,7 +6,7 @@ module Spree
6
6
  if image.viewable.is_master?
7
7
  Spree.t(:all)
8
8
  else
9
- image.viewable.options_text
9
+ image.viewable.sku_and_options_text
10
10
  end
11
11
  else
12
12
  Spree.t(:all)
@@ -34,7 +34,7 @@ module Spree
34
34
  v.options_text
35
35
  end
36
36
 
37
- def meta_data_tags
37
+ def meta_data
38
38
  object = instance_variable_get('@'+controller_name.singularize)
39
39
  meta = {}
40
40
 
@@ -51,8 +51,11 @@ module Spree
51
51
  keywords: Spree::Config[:default_meta_keywords],
52
52
  description: Spree::Config[:default_meta_description]
53
53
  })
54
+ meta
55
+ end
54
56
 
55
- meta.map do |name, content|
57
+ def meta_data_tags
58
+ meta_data.map do |name, content|
56
59
  tag('meta', name: name, content: content)
57
60
  end.join("\n")
58
61
  end
@@ -14,7 +14,7 @@ module Spree
14
14
  alias_attribute :first_name, :firstname
15
15
  alias_attribute :last_name, :lastname
16
16
 
17
- def self.default
17
+ def self.build_default
18
18
  country = Spree::Country.find(Spree::Config[:default_country_id]) rescue Spree::Country.first
19
19
  new(country: country)
20
20
  end
@@ -24,6 +24,14 @@ module Spree
24
24
  # new_record? || (shipments.empty? && checkouts.empty?)
25
25
  # end
26
26
 
27
+ def self.default(user = nil, kind = "bill")
28
+ if user
29
+ user.send(:"#{kind}_address") || build_default
30
+ else
31
+ build_default
32
+ end
33
+ end
34
+
27
35
  def full_name
28
36
  "#{firstname} #{lastname}".strip
29
37
  end
@@ -54,8 +54,8 @@ module Spree
54
54
  scope :shipping, -> { where(originator_type: 'Spree::ShippingMethod') }
55
55
  scope :optional, -> { where(mandatory: false) }
56
56
  scope :eligible, -> { where(eligible: true) }
57
- scope :charge, -> { where('amount >= 0') }
58
- scope :credit, -> { where('amount < 0') }
57
+ scope :charge, -> { where("#{quoted_table_name}.amount >= 0") }
58
+ scope :credit, -> { where("#{quoted_table_name}.amount < 0") }
59
59
  scope :promotion, -> { where(originator_type: 'Spree::PromotionAction') }
60
60
  scope :manual, -> { where(originator_type: nil) }
61
61
  scope :return_authorization, -> { where(source_type: "Spree::ReturnAuthorization") }
@@ -28,7 +28,11 @@ module Spree
28
28
  end
29
29
 
30
30
  line_items_total = matched_line_items.sum(&:total)
31
- round_to_two_places(line_items_total * rate.amount)
31
+ if rate.included_in_price
32
+ deduced_total_by_rate(line_items_total, rate)
33
+ else
34
+ round_to_two_places(line_items_total * rate.amount)
35
+ end
32
36
  end
33
37
 
34
38
  def compute_line_item(line_item)
@@ -29,6 +29,8 @@ module Spree
29
29
  if expiry.present?
30
30
  self[:month], self[:year] = expiry.delete(' ').split('/')
31
31
  self[:year] = "20" + self[:year] if self[:year].length == 2
32
+ self[:year] = self[:year].to_i
33
+ self[:month] = self[:month].to_i
32
34
  end
33
35
  end
34
36
 
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class Gateway < PaymentMethod
3
- delegate_belongs_to :provider, :authorize, :purchase, :capture, :void, :credit
3
+ delegate :authorize, :purchase, :capture, :void, :credit, to: :provider
4
4
 
5
5
  validates :name, :type, presence: true
6
6
 
@@ -2,16 +2,17 @@ module Spree
2
2
  class InventoryUnit < ActiveRecord::Base
3
3
  belongs_to :variant, class_name: "Spree::Variant"
4
4
  belongs_to :order, class_name: "Spree::Order"
5
- belongs_to :shipment, class_name: "Spree::Shipment"
5
+ belongs_to :shipment, class_name: "Spree::Shipment", touch: true
6
6
  belongs_to :return_authorization, class_name: "Spree::ReturnAuthorization"
7
7
 
8
8
  scope :backordered, -> { where state: 'backordered' }
9
9
  scope :shipped, -> { where state: 'shipped' }
10
10
  scope :backordered_per_variant, ->(stock_item) do
11
- includes(:shipment)
11
+ includes(:shipment, :order)
12
12
  .where("spree_shipments.state != 'canceled'").references(:shipment)
13
13
  .where(variant_id: stock_item.variant_id)
14
- .backordered.order("#{self.table_name}.created_at ASC")
14
+ .where('spree_orders.completed_at is not null')
15
+ .backordered.order("spree_orders.completed_at ASC")
15
16
  end
16
17
 
17
18
  # state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
@@ -34,7 +35,7 @@ module Spree
34
35
  # lead to issues once users tried to modify the objects returned. That's due
35
36
  # to ActiveRecord `joins(shipment: :stock_location)` only return readonly
36
37
  # objects
37
- #
38
+ #
38
39
  # Returns an array of backordered inventory units as per a given stock item
39
40
  def self.backordered_for_stock_item(stock_item)
40
41
  backordered_per_variant(stock_item).select do |unit|
@@ -1,24 +1,15 @@
1
1
  # Default implementation of User. This class is intended to be modified by extensions (ex. spree_auth_devise)
2
2
  module Spree
3
3
  class LegacyUser < ActiveRecord::Base
4
+ include Core::UserAddress
5
+
4
6
  self.table_name = 'spree_users'
5
7
  has_many :orders, foreign_key: :user_id
6
- belongs_to :ship_address, class_name: 'Spree::Address'
7
- belongs_to :bill_address, class_name: 'Spree::Address'
8
8
 
9
9
  before_destroy :check_completed_orders
10
10
 
11
11
  class DestroyWithOrdersError < StandardError; end
12
12
 
13
- def anonymous?
14
- false
15
- end
16
-
17
- # Creates an anonymous user
18
- def self.anonymous!
19
- create
20
- end
21
-
22
13
  def has_spree_role?(role)
23
14
  true
24
15
  end
@@ -20,12 +20,11 @@ module Spree
20
20
  validates :price, numericality: true
21
21
  validates_with Stock::AvailabilityValidator
22
22
 
23
- before_save :update_inventory
24
-
23
+ after_save :update_inventory
25
24
  after_save :update_order
26
25
  after_destroy :update_order
27
26
 
28
- delegate :name, :description, to: :variant
27
+ delegate :name, :description, :should_track_inventory?, to: :variant
29
28
 
30
29
  attr_accessor :target_shipment
31
30
 
@@ -12,5 +12,9 @@ module Spree
12
12
  log.details = details
13
13
  log.save!
14
14
  end
15
+
16
+ def parsed_details
17
+ @details ||= YAML.load(details)
18
+ end
15
19
  end
16
20
  end
@@ -9,5 +9,11 @@ module Spree
9
9
  default_scope -> { order("#{self.table_name}.position") }
10
10
 
11
11
  accepts_nested_attributes_for :option_values, reject_if: lambda { |ov| ov[:name].blank? || ov[:presentation].blank? }, allow_destroy: true
12
+
13
+ after_touch :touch_all_products
14
+
15
+ def touch_all_products
16
+ products.find_each(&:touch)
17
+ end
12
18
  end
13
19
  end
@@ -1,9 +1,20 @@
1
1
  module Spree
2
2
  class OptionValue < ActiveRecord::Base
3
- belongs_to :option_type
3
+ belongs_to :option_type, :class_name => 'Spree::OptionType', :touch => true
4
4
  acts_as_list scope: :option_type
5
5
  has_and_belongs_to_many :variants, join_table: 'spree_option_values_variants', class_name: "Spree::Variant"
6
6
 
7
7
  validates :name, :presentation, presence: true
8
+
9
+ after_touch :touch_all_variants
10
+
11
+ def touch_all_variants
12
+ # This can cause a cascade of products to be updated
13
+ # To disable it in Rails 4.1, we can do this:
14
+ # https://github.com/rails/rails/pull/12772
15
+ # Spree::Product.no_touching do
16
+ variants.find_each(&:touch)
17
+ # end
18
+ end
8
19
  end
9
20
  end
@@ -92,7 +92,7 @@ module Spree
92
92
  end
93
93
 
94
94
  def self.complete
95
- where('completed_at IS NOT NULL')
95
+ where.not(completed_at: nil)
96
96
  end
97
97
 
98
98
  def self.incomplete
@@ -161,11 +161,12 @@ module Spree
161
161
 
162
162
  # If true, causes the confirmation step to happen during the checkout process
163
163
  def confirmation_required?
164
- if payments.empty? and Spree::Config[:always_include_confirm_step]
165
- true
166
- else
167
- payments.map(&:payment_method).compact.any?(&:payment_profiles_supported?)
168
- end
164
+ Spree::Config[:always_include_confirm_step] ||
165
+ payments.valid.map(&:payment_method).compact.any?(&:payment_profiles_supported?) ||
166
+ # Little hacky fix for #4117
167
+ # If this wasn't here, order would transition to address state on confirm failure
168
+ # because there would be no valid payments any more.
169
+ state == 'confirm'
169
170
  end
170
171
 
171
172
  # Indicates the number of items in the order
@@ -350,7 +351,7 @@ module Spree
350
351
  end
351
352
 
352
353
  def available_payment_methods
353
- @available_payment_methods ||= PaymentMethod.available(:front_end)
354
+ @available_payment_methods ||= (PaymentMethod.available(:front_end) + PaymentMethod.available(:both)).uniq
354
355
  end
355
356
 
356
357
  def pending_payments
@@ -447,12 +448,15 @@ module Spree
447
448
  state = "#{name}_state"
448
449
  if persisted?
449
450
  old_state = self.send("#{state}_was")
450
- self.state_changes.create(
451
- previous_state: old_state,
452
- next_state: self.send(state),
453
- name: name,
454
- user_id: self.user_id
455
- )
451
+ new_state = self.send(state)
452
+ unless old_state == new_state
453
+ self.state_changes.create(
454
+ previous_state: old_state,
455
+ next_state: new_state,
456
+ name: name,
457
+ user_id: self.user_id
458
+ )
459
+ end
456
460
  end
457
461
  end
458
462
 
@@ -506,10 +510,14 @@ module Spree
506
510
  def ensure_updated_shipments
507
511
  if shipments.any?
508
512
  self.shipments.destroy_all
509
- self.update_column(:state, "address")
513
+ restart_checkout_flow
510
514
  end
511
515
  end
512
516
 
517
+ def restart_checkout_flow
518
+ self.update_column(:state, checkout_steps.first)
519
+ end
520
+
513
521
  def refresh_shipment_rates
514
522
  shipments.map &:refresh_rates
515
523
  end
@@ -518,6 +526,15 @@ module Spree
518
526
  (bill_address.empty? && ship_address.empty?) || bill_address.same_as?(ship_address)
519
527
  end
520
528
 
529
+ def is_risky?
530
+ self.payments.where(%{
531
+ (avs_response IS NOT NULL and avs_response != 'D') or
532
+ (cvv_response_code IS NOT NULL and cvv_response_code != 'M') or
533
+ cvv_response_message IS NOT NULL or
534
+ state = 'failed'
535
+ }.squish!).uniq.count > 0
536
+ end
537
+
521
538
  private
522
539
 
523
540
  def link_by_email
@@ -526,7 +543,7 @@ module Spree
526
543
 
527
544
  # Determine if email is required (we don't want validation errors before we hit the checkout)
528
545
  def require_email
529
- return true unless new_record? or state == 'cart'
546
+ return true unless new_record? or ['cart', 'address'].include?(state)
530
547
  end
531
548
 
532
549
  def ensure_line_items_present
@@ -555,9 +572,10 @@ module Spree
555
572
 
556
573
  def after_cancel
557
574
  shipments.each { |shipment| shipment.cancel! }
575
+ payments.completed.each { |payment| payment.credit! }
558
576
 
559
577
  send_cancel_email
560
- self.payment_state = 'credit_owed' unless shipped?
578
+ self.update_column(:payment_state, 'credit_owed') unless shipped?
561
579
  end
562
580
 
563
581
  def send_cancel_email
@@ -151,6 +151,10 @@ module Spree
151
151
  @checkout_steps ||= {}
152
152
  end
153
153
 
154
+ def self.checkout_step_names
155
+ self.checkout_steps.keys
156
+ end
157
+
154
158
  def self.add_transition(options)
155
159
  self.next_event_transitions << { options.delete(:from) => options.delete(:to) }.merge(options)
156
160
  end
@@ -65,7 +65,7 @@ module Spree
65
65
  end
66
66
 
67
67
  def add_to_shipment(shipment, variant, quantity)
68
- if Config.track_inventory_levels
68
+ if variant.should_track_inventory?
69
69
  on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
70
70
 
71
71
  on_hand.times { shipment.set_up_inventory('on_hand', variant, order) }
@@ -4,7 +4,7 @@ module Spree
4
4
 
5
5
  IDENTIFIER_CHARS = (('A'..'Z').to_a + ('0'..'9').to_a - %w(0 1 I O)).freeze
6
6
 
7
- belongs_to :order, class_name: 'Spree::Order'
7
+ belongs_to :order, class_name: 'Spree::Order', touch: true
8
8
  belongs_to :source, polymorphic: true
9
9
  belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
10
10
 
@@ -30,7 +30,7 @@ module Spree
30
30
  scope :completed, -> { with_state('completed') }
31
31
  scope :pending, -> { with_state('pending') }
32
32
  scope :failed, -> { with_state('failed') }
33
- scope :valid, -> { where('state NOT IN (?)', %w(failed invalid)) }
33
+ scope :valid, -> { where.not(state: %w(failed invalid)) }
34
34
 
35
35
  after_rollback :persist_invalid
36
36
 
@@ -118,6 +118,14 @@ module Spree
118
118
  res || payment_method
119
119
  end
120
120
 
121
+ def is_avs_risky?
122
+ !(avs_response == "D" || avs_response.nil?)
123
+ end
124
+
125
+ def is_cvv_risky?
126
+ !(cvv_response_code == "M" || cvv_response_code.nil?)
127
+ end
128
+
121
129
  private
122
130
 
123
131
  def validate_source
@@ -115,14 +115,15 @@ module Spree
115
115
  end
116
116
 
117
117
  def gateway_options
118
- options = { :email => order.email,
119
- :customer => order.email,
120
- :ip => order.last_ip_address,
118
+ options = { :email => order.email,
119
+ :customer => order.email,
120
+ :customer_id => order.user_id,
121
+ :ip => order.last_ip_address,
121
122
  # Need to pass in a unique identifier here to make some
122
123
  # payment gateways happy.
123
124
  #
124
125
  # For more information, please see Spree::Payment#set_unique_identifier
125
- :order_id => gateway_order_id }
126
+ :order_id => gateway_order_id }
126
127
 
127
128
  options.merge!({ :shipping => order.ship_total * 100,
128
129
  :tax => order.tax_total * 100,
@@ -8,6 +8,8 @@ module Spree
8
8
 
9
9
  validates :name, presence: true
10
10
 
11
+ has_many :payments, class_name: "Spree::Payment"
12
+
11
13
  def self.providers
12
14
  Rails.application.config.spree.payment_methods
13
15
  end
@@ -22,6 +22,11 @@ module Spree
22
22
  self[:amount] = parse_price(price)
23
23
  end
24
24
 
25
+ # Remove variant default_scope `deleted_at: nil`
26
+ def variant
27
+ Spree::Variant.unscoped { super }
28
+ end
29
+
25
30
  private
26
31
  def check_price
27
32
  raise "Price must belong to a variant" if variant.nil?
@@ -35,15 +35,18 @@ module Spree
35
35
 
36
36
  has_one :master,
37
37
  -> { where is_master: true },
38
+ inverse_of: :product,
38
39
  class_name: 'Spree::Variant',
39
40
  dependent: :destroy
40
41
 
41
42
  has_many :variants,
42
43
  -> { where(is_master: false).order("#{::Spree::Variant.quoted_table_name}.position ASC") },
44
+ inverse_of: :product,
43
45
  class_name: 'Spree::Variant'
44
46
 
45
47
  has_many :variants_including_master,
46
48
  -> { order("#{::Spree::Variant.quoted_table_name}.position ASC") },
49
+ inverse_of: :product,
47
50
  class_name: 'Spree::Variant',
48
51
  dependent: :destroy
49
52
 
@@ -65,8 +68,6 @@ module Spree
65
68
 
66
69
  has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
67
70
 
68
- accepts_nested_attributes_for :variants, allow_destroy: true
69
-
70
71
  validates :name, presence: true
71
72
  validates :permalink, presence: true
72
73
  validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
@@ -191,10 +192,10 @@ module Spree
191
192
  end
192
193
 
193
194
  def total_on_hand
194
- if Spree::Config.track_inventory_levels
195
- self.stock_items.sum(&:count_on_hand)
196
- else
195
+ if self.variants_including_master.any? { |v| !v.should_track_inventory? }
197
196
  Float::INFINITY
197
+ else
198
+ self.stock_items.sum(&:count_on_hand)
198
199
  end
199
200
  end
200
201