spree_core 2.2.1 → 2.2.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/products_helper.rb +3 -3
  3. data/app/models/concerns/spree/user_reporting.rb +2 -2
  4. data/app/models/spree/address.rb +7 -7
  5. data/app/models/spree/credit_card.rb +11 -5
  6. data/app/models/spree/gateway.rb +4 -1
  7. data/app/models/spree/item_adjustments.rb +17 -7
  8. data/app/models/spree/line_item.rb +2 -2
  9. data/app/models/spree/order.rb +35 -19
  10. data/app/models/spree/order/checkout.rb +3 -1
  11. data/app/models/spree/order_updater.rb +8 -2
  12. data/app/models/spree/payment.rb +2 -2
  13. data/app/models/spree/payment/processing.rb +9 -0
  14. data/app/models/spree/payment_method.rb +6 -0
  15. data/app/models/spree/price.rb +2 -2
  16. data/app/models/spree/product.rb +7 -2
  17. data/app/models/spree/promotion/actions/create_item_adjustments.rb +1 -0
  18. data/app/models/spree/promotion/rules/product.rb +4 -2
  19. data/app/models/spree/promotion/rules/user.rb +2 -2
  20. data/app/models/spree/promotion_handler/coupon.rb +6 -1
  21. data/app/models/spree/shipment.rb +0 -1
  22. data/app/models/spree/shipping_rate.rb +11 -9
  23. data/app/models/spree/stock/availability_validator.rb +1 -1
  24. data/app/models/spree/stock/quantifier.rb +1 -9
  25. data/app/models/spree/stock_item.rb +4 -0
  26. data/app/models/spree/tax_rate.rb +9 -8
  27. data/app/models/spree/taxon.rb +3 -0
  28. data/app/models/spree/variant.rb +1 -5
  29. data/config/locales/en.yml +1 -0
  30. data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +1 -1
  31. data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +5 -0
  32. data/lib/generators/spree/dummy/dummy_generator.rb +7 -2
  33. data/lib/spree/core.rb +2 -0
  34. data/lib/spree/core/engine.rb +1 -1
  35. data/lib/spree/core/importer/order.rb +13 -2
  36. data/lib/spree/core/version.rb +1 -1
  37. data/lib/spree/migrations.rb +10 -1
  38. data/lib/spree/money.rb +1 -1
  39. data/lib/spree/testing_support/capybara_ext.rb +1 -1
  40. data/lib/spree/testing_support/factories/address_factory.rb +1 -1
  41. metadata +34 -11
  42. data/vendor/assets/fonts/FontAwesome.otf +0 -0
  43. data/vendor/assets/fonts/fontawesome-webfont.eot +0 -0
  44. data/vendor/assets/fonts/fontawesome-webfont.svg +0 -399
  45. data/vendor/assets/fonts/fontawesome-webfont.ttf +0 -0
  46. data/vendor/assets/fonts/fontawesome-webfont.woff +0 -0
  47. data/vendor/assets/stylesheets/font-awesome.scss +0 -1475
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6877ed94eb40aa392d3e75311ae1c46f07d16c4
4
- data.tar.gz: 86f962251bf0d966fce2dc5562a4e537b05db77c
3
+ metadata.gz: 2a5120849e1aaa18b3747ba821d39be5e0e22c64
4
+ data.tar.gz: 472557e5032544f1c53694c452989910466e7fa9
5
5
  SHA512:
6
- metadata.gz: b9f99321e59660959d319148d1c7deed17c10caedb97e6d29d95dd15c195b2dac43bcfb35d5117c915bc456a1d8c0fb8f2d319971a7bd681f842f77a91ba25b6
7
- data.tar.gz: 154ee0e0f7d1bd5fa2ce9bce96008951200122a3c687dfe88b6e75284987bef2c12d464c6bba83a2a97747ccd2e9dae7537b2fddc3a4fb9e9cc924c5c11c8491
6
+ metadata.gz: 8dec314e53c791de3fa15d96772d7b6b46771873321ff7219fe5a7a8316d61f6dfa82fc50d6b176d1186a3c1944eeb9962488570dea5f14a8fb7a40ac6fba4fe
7
+ data.tar.gz: 3423ee941bbce9284bad16a6137a991efe553553a87abca67585cb5bf8dc9e10a4cf4b364b456e8404f37b0649050b154ecd266fa78965a5ec9758866b53896b
@@ -9,7 +9,6 @@ module Spree
9
9
  end
10
10
  end
11
11
 
12
-
13
12
  # returns the formatted price for the specified variant as a difference from product price
14
13
  def variant_price_diff(variant)
15
14
  diff = variant.amount_in(current_currency) - variant.product.amount_in(current_currency)
@@ -54,8 +53,9 @@ module Spree
54
53
  end
55
54
 
56
55
  def cache_key_for_products
57
- max_updated_at = @products.maximum(:updated_at).to_s(:number)
58
- "#{current_currency}/spree/products/all-#{params[:page]}-#{max_updated_at}"
56
+ count = @products.count
57
+ max_updated_at = (@products.maximum(:updated_at) || Date.today).to_s(:number)
58
+ "#{current_currency}/spree/products/all-#{params[:page]}-#{max_updated_at}-#{count}"
59
59
  end
60
60
  end
61
61
  end
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  module UserReporting
3
3
  def lifetime_value
4
- orders.complete.pluck(:total).sum
4
+ spree_orders.complete.pluck(:total).sum
5
5
  end
6
6
 
7
7
  def display_lifetime_value
@@ -9,7 +9,7 @@ module Spree
9
9
  end
10
10
 
11
11
  def order_count
12
- BigDecimal(orders.complete.count)
12
+ BigDecimal(spree_orders.complete.count)
13
13
  end
14
14
 
15
15
  def average_order_value
@@ -82,15 +82,15 @@ module Spree
82
82
  }
83
83
  end
84
84
 
85
- private
86
- def require_phone?
87
- true
88
- end
85
+ def require_phone?
86
+ true
87
+ end
89
88
 
90
- def require_zipcode?
91
- true
92
- end
89
+ def require_zipcode?
90
+ true
91
+ end
93
92
 
93
+ private
94
94
  def state_validate
95
95
  # Skip state validation without country (also required)
96
96
  # or when disabled by preference
@@ -29,12 +29,18 @@ module Spree
29
29
  }
30
30
 
31
31
  def expiry=(expiry)
32
- if expiry.present?
33
- self[:month], self[:year] = expiry.delete(' ').split('/')
34
- self[:year] = "20" + self[:year] if self[:year].length == 2
35
- self[:year] = self[:year].to_i
36
- self[:month] = self[:month].to_i
32
+ return unless expiry.present?
33
+
34
+ self[:month], self[:year] =
35
+ if expiry.match(/\d\s?\/\s?\d/) # will match mm/yy and mm / yyyy
36
+ expiry.delete(' ').split('/')
37
+ elsif match = expiry.match(/(\d{2})(\d{2,4})/) # will match mmyy and mmyyyy
38
+ [match[1], match[2]]
37
39
  end
40
+
41
+ self[:year] = "20" + self[:year] if self[:year].length == 2
42
+ self[:year] = self[:year].to_i
43
+ self[:month] = self[:month].to_i
38
44
  end
39
45
 
40
46
  def number=(num)
@@ -64,7 +64,7 @@ module Spree
64
64
  payment_source_class.where(id: source_ids).with_payment_profile
65
65
  end
66
66
 
67
- def sources_with_profile(order)
67
+ def reusable_sources(order)
68
68
  if order.completed?
69
69
  sources_by_order order
70
70
  else
@@ -75,5 +75,8 @@ module Spree
75
75
  end
76
76
  end
77
77
  end
78
+
79
+ # for backwards compatibility
80
+ alias_method :source_with_profiles, :reusable_sources
78
81
  end
79
82
  end
@@ -1,6 +1,8 @@
1
1
  module Spree
2
2
  # Manage (recalculate) item (LineItem or Shipment) adjustments
3
3
  class ItemAdjustments
4
+ include ActiveSupport::Callbacks
5
+ define_callbacks :promo_adjustments, :tax_adjustments
4
6
  attr_reader :item
5
7
 
6
8
  delegate :adjustments, :order, to: :item
@@ -32,14 +34,22 @@ module Spree
32
34
  # Included tax adjustments are those which are included in the price.
33
35
  # These ones should not effect the eventual total price.
34
36
  #
35
- # Additional tax adjustments are the opposite; effecting the final total.
36
- promotion_total = adjustments.promotion.reload.map(&:update!).compact.sum
37
- unless promotion_total == 0
38
- choose_best_promotion_adjustment
37
+ # Additional tax adjustments are the opposite; effecting the final total.
38
+ promo_total = 0
39
+ run_callbacks :promo_adjustments do
40
+ promotion_total = adjustments.promotion.reload.map(&:update!).compact.sum
41
+ unless promotion_total == 0
42
+ choose_best_promotion_adjustment
43
+ end
44
+ promo_total = best_promotion_adjustment.try(:amount).to_f
45
+ end
46
+
47
+ included_tax_total = 0
48
+ additional_tax_total = 0
49
+ run_callbacks :tax_adjustments do
50
+ included_tax_total = adjustments.tax.included.reload.map(&:update!).compact.sum
51
+ additional_tax_total = adjustments.tax.additional.reload.map(&:update!).compact.sum
39
52
  end
40
- promo_total = best_promotion_adjustment.try(:amount).to_f
41
- included_tax_total = adjustments.tax.included.reload.map(&:update!).compact.sum
42
- additional_tax_total = adjustments.tax.additional.reload.map(&:update!).compact.sum
43
53
 
44
54
  item.update_columns(
45
55
  :promo_total => promo_total,
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class LineItem < ActiveRecord::Base
3
3
  before_validation :adjust_quantity
4
- belongs_to :order, class_name: "Spree::Order", inverse_of: :line_items
4
+ belongs_to :order, class_name: "Spree::Order", inverse_of: :line_items, touch: true
5
5
  belongs_to :variant, class_name: "Spree::Variant", inverse_of: :line_items
6
6
  belongs_to :tax_category, class_name: "Spree::TaxCategory"
7
7
 
@@ -78,7 +78,7 @@ module Spree
78
78
  end
79
79
 
80
80
  def sufficient_stock?
81
- Stock::Quantifier.new(variant_id).can_supply? quantity
81
+ Stock::Quantifier.new(variant).can_supply? quantity
82
82
  end
83
83
 
84
84
  def insufficient_stock?
@@ -48,6 +48,8 @@ module Spree
48
48
  has_many :line_item_adjustments, through: :line_items, source: :adjustments
49
49
  has_many :shipment_adjustments, through: :shipments, source: :adjustments
50
50
  has_many :inventory_units, inverse_of: :order
51
+ has_many :products, through: :variants
52
+ has_many :variants, through: :line_items
51
53
 
52
54
  has_and_belongs_to_many :promotions, join_table: 'spree_orders_promotions'
53
55
 
@@ -87,8 +89,12 @@ module Spree
87
89
  where(number: number)
88
90
  end
89
91
 
92
+ scope :created_between, ->(start_date, end_date) { where(created_at: start_date..end_date) }
93
+ scope :completed_between, ->(start_date, end_date) { where(completed_at: start_date..end_date) }
94
+
90
95
  def self.between(start_date, end_date)
91
- where(created_at: start_date..end_date)
96
+ ActiveSupport::Deprecation.warn("Order#between will be deprecated in Spree 2.3, please use either Order#created_between or Order#completed_between instead.")
97
+ self.created_between(start_date, end_date)
92
98
  end
93
99
 
94
100
  def self.by_customer(customer)
@@ -114,7 +120,8 @@ module Spree
114
120
  end
115
121
 
116
122
  def all_adjustments
117
- Adjustment.where("order_id = :order_id OR adjustable_id = :order_id", :order_id => self.id)
123
+ Adjustment.where("order_id = :order_id OR (adjustable_id = :order_id AND adjustable_type = 'Spree::Order')",
124
+ :order_id => self.id)
118
125
  end
119
126
 
120
127
  # For compatiblity with Calculator::PriceSack
@@ -146,6 +153,10 @@ module Spree
146
153
  Spree::Money.new(additional_tax_total, { currency: currency })
147
154
  end
148
155
 
156
+ def display_tax_total
157
+ Spree::Money.new(included_tax_total + additional_tax_total, { currency: currency })
158
+ end
159
+
149
160
  def display_shipment_total
150
161
  Spree::Money.new(shipment_total, { currency: currency })
151
162
  end
@@ -197,7 +208,7 @@ module Spree
197
208
  # Returns the relevant zone (if any) to be used for taxation purposes.
198
209
  # Uses default tax zone unless there is a specific match
199
210
  def tax_zone
200
- Zone.match(tax_address) || Zone.default_tax
211
+ @tax_zone ||= Zone.match(tax_address) || Zone.default_tax
201
212
  end
202
213
 
203
214
  # Indicates whether tax should be backed out of the price calcualtions in
@@ -205,7 +216,7 @@ module Spree
205
216
  # taxes in that case.
206
217
  def exclude_tax?
207
218
  return false unless Spree::Config[:prices_inc_tax]
208
- return tax_zone != Zone.default_tax
219
+ tax_zone != Zone.default_tax
209
220
  end
210
221
 
211
222
  # Returns the address for taxation based on configuration
@@ -244,14 +255,16 @@ module Spree
244
255
  end
245
256
 
246
257
  # Associates the specified user with the order.
247
- def associate_user!(user)
258
+ def associate_user!(user, override_email = true)
248
259
  self.user = user
249
- self.email = user.email
250
- self.created_by = user if self.created_by.blank?
260
+ attrs_to_set = { user_id: user.id }
261
+ attrs_to_set[:email] = user.email if override_email
262
+ attrs_to_set[:created_by_id] = user.id if self.created_by.blank?
263
+ assign_attributes(attrs_to_set)
251
264
 
252
265
  if persisted?
253
266
  # immediately persist the changes we just made, but don't use save since we might have an invalid address associated
254
- self.class.unscoped.where(id: id).update_all(email: user.email, user_id: user.id, created_by_id: self.created_by_id)
267
+ self.class.unscoped.where(id: id).update_all(attrs_to_set)
255
268
  end
256
269
  end
257
270
 
@@ -395,16 +408,8 @@ module Spree
395
408
  bill_address.try(:lastname)
396
409
  end
397
410
 
398
- def products
399
- line_items.map(&:product)
400
- end
401
-
402
- def variants
403
- line_items.map(&:variant)
404
- end
405
-
406
411
  def insufficient_stock_lines
407
- @insufficient_stock_lines ||= line_items.select(&:insufficient_stock?)
412
+ line_items.select(&:insufficient_stock?)
408
413
  end
409
414
 
410
415
  def merge!(order, user = nil)
@@ -422,6 +427,10 @@ module Spree
422
427
 
423
428
  self.associate_user!(user) if !self.user && !user.blank?
424
429
 
430
+ updater.update_item_count
431
+ updater.update_item_total
432
+ updater.persist_totals
433
+
425
434
  # So that the destroy doesn't take out line items which may have been re-assigned
426
435
  order.line_items.reload
427
436
  order.destroy
@@ -429,6 +438,8 @@ module Spree
429
438
 
430
439
  def empty!
431
440
  line_items.destroy_all
441
+ updater.update_item_count
442
+
432
443
  adjustments.destroy_all
433
444
  update_totals
434
445
  persist_totals
@@ -575,6 +586,11 @@ module Spree
575
586
  self.ensure_updated_shipments
576
587
  end
577
588
 
589
+ def reload
590
+ remove_instance_variable(:@tax_zone) if defined?(@tax_zone)
591
+ super
592
+ end
593
+
578
594
  private
579
595
 
580
596
  def link_by_email
@@ -583,7 +599,7 @@ module Spree
583
599
 
584
600
  # Determine if email is required (we don't want validation errors before we hit the checkout)
585
601
  def require_email
586
- return true unless new_record? or ['cart', 'address'].include?(state)
602
+ true unless new_record? or ['cart', 'address'].include?(state)
587
603
  end
588
604
 
589
605
  def ensure_line_items_present
@@ -607,7 +623,7 @@ module Spree
607
623
 
608
624
  def after_cancel
609
625
  shipments.each { |shipment| shipment.cancel! }
610
- payments.completed.each { |payment| payment.credit! }
626
+ payments.completed.each { |payment| payment.cancel! }
611
627
 
612
628
  send_cancel_email
613
629
  self.update_column(:payment_state, 'credit_owed') unless shipped?
@@ -38,7 +38,7 @@ module Spree
38
38
  # To avoid multiple occurrences of the same transition being defined
39
39
  # On first definition, state_machines will not be defined
40
40
  state_machines.clear if respond_to?(:state_machines)
41
- state_machine :state, :initial => :cart do
41
+ state_machine :state, :initial => :cart, :use_transactions => false, :action => :save_state do
42
42
  klass.next_event_transitions.each { |t| transition(t.merge(:on => :next)) }
43
43
 
44
44
  # Persist the state on the order
@@ -101,6 +101,8 @@ module Spree
101
101
  order.persist_totals
102
102
  end
103
103
  end
104
+
105
+ alias_method :save_state, :save
104
106
  end
105
107
 
106
108
  def self.go_to_state(name, options={})
@@ -149,9 +149,15 @@ module Spree
149
149
  elsif payments.last.state == 'checkout'
150
150
  order.payment_state = 'pending'
151
151
  elsif payments.last.state == 'completed'
152
- order.payment_state = 'credit_owed'
153
- else
152
+ if line_items.empty?
153
+ order.payment_state = 'credit_owed'
154
+ else
155
+ order.payment_state = 'balance_due'
156
+ end
157
+ elsif payments.last.state == 'pending'
154
158
  order.payment_state = 'balance_due'
159
+ else
160
+ order.payment_state = 'credit_owed'
155
161
  end
156
162
  else
157
163
  order.payment_state = 'balance_due'
@@ -113,8 +113,8 @@ module Spree
113
113
 
114
114
  # see https://github.com/spree/spree/issues/981
115
115
  def build_source
116
- return if source_attributes.nil?
117
- if payment_method and payment_method.payment_source_class
116
+ return unless new_record?
117
+ if source_attributes.present? && source.blank? && payment_method.try(:payment_source_class)
118
118
  self.source = payment_method.payment_source_class.new(source_attributes)
119
119
  self.source.payment_method_id = payment_method.id
120
120
  self.source.user_id = self.order.user_id if self.order
@@ -111,6 +111,14 @@ module Spree
111
111
  end
112
112
  end
113
113
 
114
+ def cancel!
115
+ if payment_method.respond_to?(:cancel)
116
+ payment_method.cancel(response_code)
117
+ else
118
+ credit!
119
+ end
120
+ end
121
+
114
122
  def partial_credit(amount)
115
123
  return if amount > credit_allowed
116
124
  started_processing!
@@ -118,6 +126,7 @@ module Spree
118
126
  end
119
127
 
120
128
  def gateway_options
129
+ order.reload
121
130
  options = { :email => order.email,
122
131
  :customer => order.email,
123
132
  :customer_id => order.user_id,
@@ -54,6 +54,12 @@ module Spree
54
54
  true
55
55
  end
56
56
 
57
+ # Custom gateways should redefine this method. See Gateway implementation
58
+ # as an example
59
+ def reusable_sources(order)
60
+ []
61
+ end
62
+
57
63
  def auto_capture?
58
64
  self.auto_capture.nil? ? Spree::Config[:auto_capture] : self.auto_capture
59
65
  end
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class Price < ActiveRecord::Base
3
3
  acts_as_paranoid
4
- belongs_to :variant, class_name: 'Spree::Variant', inverse_of: :prices
4
+ belongs_to :variant, class_name: 'Spree::Variant', inverse_of: :prices, touch: true
5
5
 
6
6
  validate :check_price
7
7
  validates :amount, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
@@ -29,6 +29,7 @@ module Spree
29
29
  end
30
30
 
31
31
  private
32
+
32
33
  def check_price
33
34
  raise "Price must belong to a variant" if variant.nil?
34
35
 
@@ -51,4 +52,3 @@ module Spree
51
52
 
52
53
  end
53
54
  end
54
-
@@ -201,7 +201,7 @@ module Spree
201
201
  if self.variants_including_master.any? { |v| !v.should_track_inventory? }
202
202
  Float::INFINITY
203
203
  else
204
- self.stock_items.sum(&:count_on_hand)
204
+ self.stock_items.to_a.sum(&:count_on_hand)
205
205
  end
206
206
  end
207
207
 
@@ -257,8 +257,13 @@ module Spree
257
257
  self.master ||= Variant.new
258
258
  end
259
259
 
260
+ # Iterate through this products taxons and taxonomies and touch their timestamps in a batch
260
261
  def touch_taxons
261
- self.taxons.each(&:touch)
262
+ taxons_to_touch = taxons.map(&:self_and_ancestors).flatten.uniq
263
+ Spree::Taxon.where(id: taxons_to_touch.map(&:id)).update_all(updated_at: Time.current)
264
+
265
+ taxonomy_ids_to_touch = taxons_to_touch.map(&:taxonomy_id).flatten.uniq
266
+ Spree::Taxonomy.where(id: taxonomy_ids_to_touch).update_all(updated_at: Time.current)
262
267
  end
263
268
  end
264
269
  end