spree_core 2.1.3 → 2.1.4

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