spree_core 2.2.2 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +2 -2
  3. data/app/helpers/spree/products_helper.rb +1 -1
  4. data/app/helpers/spree/store_helper.rb +1 -1
  5. data/app/models/spree/adjustment.rb +10 -9
  6. data/app/models/spree/app_configuration.rb +1 -0
  7. data/app/models/spree/credit_card.rb +6 -5
  8. data/app/models/spree/line_item.rb +3 -3
  9. data/app/models/spree/order.rb +4 -8
  10. data/app/models/spree/order/checkout.rb +2 -0
  11. data/app/models/spree/order_contents.rb +3 -0
  12. data/app/models/spree/order_updater.rb +2 -2
  13. data/app/models/spree/payment.rb +10 -9
  14. data/app/models/spree/payment/processing.rb +1 -1
  15. data/app/models/spree/product.rb +18 -1
  16. data/app/models/spree/promotion/actions/create_item_adjustments.rb +5 -3
  17. data/app/models/spree/promotion_action.rb +2 -0
  18. data/app/models/spree/promotion_handler/coupon.rb +13 -4
  19. data/app/models/spree/return_authorization.rb +9 -8
  20. data/app/models/spree/stock/estimator.rb +1 -1
  21. data/app/models/spree/zone.rb +5 -5
  22. data/app/views/spree/order_mailer/confirm_email.text.erb +20 -2
  23. data/app/views/spree/shared/_routes.html.erb +5 -5
  24. data/config/locales/en.yml +1 -0
  25. data/db/migrate/20131118183431_add_line_item_id_to_spree_inventory_units.rb +2 -2
  26. data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +12 -0
  27. data/db/migrate/20140609201656_add_deleted_at_to_spree_promotion_actions.rb +6 -0
  28. data/db/migrate/20140616202624_remove_uncaptured_amount_from_spree_payments.rb +5 -0
  29. data/lib/spree/core/product_duplicator.rb +6 -2
  30. data/lib/spree/core/version.rb +1 -1
  31. data/lib/spree/testing_support/factories/promotion_factory.rb +31 -1
  32. metadata +14 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a5120849e1aaa18b3747ba821d39be5e0e22c64
4
- data.tar.gz: 472557e5032544f1c53694c452989910466e7fa9
3
+ metadata.gz: f612c6f42ba3ade084e50bdca945ccf34f1ce76f
4
+ data.tar.gz: f84f159adfe58ab3b210da9b0d596a54d3d758c2
5
5
  SHA512:
6
- metadata.gz: 8dec314e53c791de3fa15d96772d7b6b46771873321ff7219fe5a7a8316d61f6dfa82fc50d6b176d1186a3c1944eeb9962488570dea5f14a8fb7a40ac6fba4fe
7
- data.tar.gz: 3423ee941bbce9284bad16a6137a991efe553553a87abca67585cb5bf8dc9e10a4cf4b364b456e8404f37b0649050b154ecd266fa78965a5ec9758866b53896b
6
+ metadata.gz: 39a5a14c31bfeb6eaff2d0f2b99eacd506e01f882fa5c20f6f03efc1d8e74d7f965a37e0d1f5f857ef25e0735056905708c8960b89310a776d2fc506d702b9d3
7
+ data.tar.gz: 7c92311fcbbd4cc550c32c3473352f719e94d40367fde91c05779a7274eff4a34c6d3ed096c69fc9eacbb921ab4f20ad3c3ba722d8060646e4ace2813e2d9dcb
@@ -20,11 +20,11 @@ module Spree
20
20
  text = "#{text}: (#{Spree.t('empty')})"
21
21
  css_class = 'empty'
22
22
  else
23
- text = "#{text}: (#{simple_current_order.item_count}) <span class='amount'>#{simple_current_order.display_total.to_html}</span>".html_safe
23
+ text = "#{text}: (#{simple_current_order.item_count}) <span class='amount'>#{simple_current_order.display_total.to_html}</span>"
24
24
  css_class = 'full'
25
25
  end
26
26
 
27
- link_to text, spree.cart_path, :class => "cart-info #{css_class}"
27
+ link_to text.html_safe, spree.cart_path, :class => "cart-info #{css_class}"
28
28
  end
29
29
 
30
30
  # human readable list of variant options
@@ -55,7 +55,7 @@ module Spree
55
55
  def cache_key_for_products
56
56
  count = @products.count
57
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}"
58
+ "#{I18n.locale}/#{current_currency}/spree/products/all-#{params[:page]}-#{max_updated_at}-#{count}"
59
59
  end
60
60
  end
61
61
  end
@@ -10,7 +10,7 @@ module Spree
10
10
  def cache_key_for_taxons
11
11
  max_updated_at = @taxons.maximum(:updated_at).to_i
12
12
  parts = [@taxon.try(:id), max_updated_at].compact.join("-")
13
- "taxons/#{parts}"
13
+ "#{I18n.locale}/taxons/#{parts}"
14
14
  end
15
15
  end
16
16
  end
@@ -24,7 +24,7 @@ module Spree
24
24
  class Adjustment < ActiveRecord::Base
25
25
  belongs_to :adjustable, polymorphic: true
26
26
  belongs_to :source, polymorphic: true
27
- belongs_to :order, :class_name => "Spree::Order"
27
+ belongs_to :order, class_name: "Spree::Order"
28
28
 
29
29
  validates :label, presence: true
30
30
  validates :amount, numericality: true
@@ -59,6 +59,14 @@ module Spree
59
59
  state == "closed"
60
60
  end
61
61
 
62
+ def currency
63
+ adjustable ? adjustable.currency : Spree::Config[:currency]
64
+ end
65
+
66
+ def display_amount
67
+ Spree::Money.new(amount, { currency: currency })
68
+ end
69
+
62
70
  def promotion?
63
71
  source.class < Spree::PromotionAction
64
72
  end
@@ -88,19 +96,12 @@ module Spree
88
96
  amount
89
97
  end
90
98
 
91
- def currency
92
- adjustable ? adjustable.currency : Spree::Config[:currency]
93
- end
94
-
95
- def display_amount
96
- Spree::Money.new(amount, { currency: currency })
97
- end
98
-
99
99
  private
100
100
 
101
101
  def update_adjustable_adjustment_total
102
102
  # Cause adjustable's total to be recalculated
103
103
  Spree::ItemAdjustments.new(adjustable).update if adjustable
104
104
  end
105
+
105
106
  end
106
107
  end
@@ -57,6 +57,7 @@ module Spree
57
57
  preference :max_level_in_taxons_menu, :integer, default: 1 # maximum nesting level in taxons menu
58
58
  preference :orders_per_page, :integer, default: 15
59
59
  preference :prices_inc_tax, :boolean, default: false
60
+ preference :properties_per_page, :integer, default: 15
60
61
  preference :products_per_page, :integer, default: 12
61
62
  preference :promotions_per_page, :integer, default: 15
62
63
  preference :redirect_https_to_http, :boolean, :default => false
@@ -37,9 +37,10 @@ module Spree
37
37
  elsif match = expiry.match(/(\d{2})(\d{2,4})/) # will match mmyy and mmyyyy
38
38
  [match[1], match[2]]
39
39
  end
40
-
41
- self[:year] = "20" + self[:year] if self[:year].length == 2
42
- self[:year] = self[:year].to_i
40
+ if self[:year]
41
+ self[:year] = "20" + self[:year] if self[:year].length == 2
42
+ self[:year] = self[:year].to_i
43
+ end
43
44
  self[:month] = self[:month].to_i
44
45
  end
45
46
 
@@ -111,8 +112,8 @@ module Spree
111
112
  :month => month,
112
113
  :year => year,
113
114
  :verification_value => verification_value,
114
- :first_name => first_name,
115
- :last_name => last_name
115
+ :first_name => first_name || name.to_s.split(/[[:space:]]/, 2)[0],
116
+ :last_name => last_name || name.to_s.split(/[[:space:]]/, 2)[1]
116
117
  )
117
118
  end
118
119
 
@@ -28,7 +28,7 @@ module Spree
28
28
  after_save :update_inventory
29
29
  after_save :update_adjustments
30
30
 
31
- after_create :create_tax_charge
31
+ after_create :update_tax_charge
32
32
 
33
33
  delegate :name, :description, :should_track_inventory?, to: :variant
34
34
 
@@ -108,6 +108,7 @@ module Spree
108
108
 
109
109
  def update_adjustments
110
110
  if quantity_changed?
111
+ update_tax_charge # Called to ensure pre_tax_amount is updated.
111
112
  recalculate_adjustments
112
113
  end
113
114
  end
@@ -116,7 +117,7 @@ module Spree
116
117
  Spree::ItemAdjustments.new(self).update
117
118
  end
118
119
 
119
- def create_tax_charge
120
+ def update_tax_charge
120
121
  Spree::TaxRate.adjust(order, [self])
121
122
  end
122
123
 
@@ -127,4 +128,3 @@ module Spree
127
128
  end
128
129
  end
129
130
  end
130
-
@@ -121,7 +121,7 @@ module Spree
121
121
 
122
122
  def all_adjustments
123
123
  Adjustment.where("order_id = :order_id OR (adjustable_id = :order_id AND adjustable_type = 'Spree::Order')",
124
- :order_id => self.id)
124
+ order_id: self.id)
125
125
  end
126
126
 
127
127
  # For compatiblity with Calculator::PriceSack
@@ -439,8 +439,9 @@ module Spree
439
439
  def empty!
440
440
  line_items.destroy_all
441
441
  updater.update_item_count
442
-
443
442
  adjustments.destroy_all
443
+ shipments.destroy_all
444
+
444
445
  update_totals
445
446
  persist_totals
446
447
  end
@@ -532,12 +533,7 @@ module Spree
532
533
  end
533
534
 
534
535
  def is_risky?
535
- self.payments.where(%{
536
- (avs_response IS NOT NULL and avs_response != '' and avs_response != 'D' and avs_response != 'M') or
537
- (cvv_response_code IS NOT NULL and cvv_response_code != 'M') or
538
- cvv_response_message IS NOT NULL and cvv_response_message != '' or
539
- state = 'failed'
540
- }.squish!).uniq.count > 0
536
+ self.payments.risky.count > 0
541
537
  end
542
538
 
543
539
  def approved_by(user)
@@ -222,6 +222,8 @@ module Spree
222
222
  raise Core::GatewayError.new Spree.t(:invalid_credit_card)
223
223
  end
224
224
 
225
+ credit_card.verification_value = params[:cvc_confirm] if params[:cvc_confirm].present?
226
+
225
227
  attributes[:payments_attributes].first[:source] = credit_card
226
228
  attributes[:payments_attributes].first[:payment_method_id] = credit_card.payment_method_id
227
229
  attributes[:payments_attributes].first.delete :source_attributes
@@ -49,7 +49,10 @@ module Spree
49
49
  order_updater.update_item_count
50
50
  order_updater.update_item_total
51
51
  order_updater.update_adjustment_total
52
+
53
+ order_updater.update_payment_state if order.completed?
52
54
  order_updater.persist_totals
55
+
53
56
  order.reload
54
57
  end
55
58
 
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class OrderUpdater
3
3
  attr_reader :order
4
- delegate :payments, :line_items, :adjustments, :shipments, :update_hooks, to: :order
4
+ delegate :payments, :line_items, :adjustments, :all_adjustments, :shipments, :update_hooks, to: :order
5
5
 
6
6
  def initialize(order)
7
7
  @order = order
@@ -30,7 +30,7 @@ module Spree
30
30
  end
31
31
 
32
32
  def recalculate_adjustments
33
- adjustments.includes(:source).each { |adjustment| adjustment.update! order }
33
+ all_adjustments.includes(:adjustable).map(&:adjustable).uniq.each { |adjustable| Spree::ItemAdjustments.new(adjustable).update }
34
34
  end
35
35
 
36
36
  # Updates the following Order total values:
@@ -2,7 +2,9 @@ module Spree
2
2
  class Payment < ActiveRecord::Base
3
3
  include Spree::Payment::Processing
4
4
 
5
- IDENTIFIER_CHARS = (('A'..'Z').to_a + ('0'..'9').to_a - %w(0 1 I O)).freeze
5
+ IDENTIFIER_CHARS = (('A'..'Z').to_a + ('0'..'9').to_a - %w(0 1 I O)).freeze
6
+ NON_RISKY_AVS_CODES = ['B', 'D', 'H', 'J', 'M', 'Q', 'T', 'V', 'X', 'Y'].freeze
7
+ RISKY_AVS_CODES = ['A', 'C', 'E', 'F', 'G', 'I', 'K', 'L', 'N', 'O', 'P', 'R', 'S', 'U', 'W', 'Z'].freeze
6
8
 
7
9
  belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :payments
8
10
  belongs_to :source, polymorphic: true
@@ -16,7 +18,6 @@ module Spree
16
18
 
17
19
  before_validation :validate_source
18
20
  before_create :set_unique_identifier
19
- before_save :update_uncaptured_amount
20
21
 
21
22
  after_save :create_payment_profile, if: :profiles_supported?
22
23
 
@@ -33,6 +34,7 @@ module Spree
33
34
  scope :completed, -> { with_state('completed') }
34
35
  scope :pending, -> { with_state('pending') }
35
36
  scope :failed, -> { with_state('failed') }
37
+ scope :risky, -> { where("avs_response IN (?) OR (cvv_response_code IS NOT NULL and cvv_response_code != 'M') OR state = 'failed'", RISKY_AVS_CODES) }
36
38
  scope :valid, -> { where.not(state: %w(failed invalid)) }
37
39
 
38
40
  after_rollback :persist_invalid
@@ -94,7 +96,7 @@ module Spree
94
96
  case amount
95
97
  when String
96
98
  separator = I18n.t('number.currency.format.separator')
97
- number = amount.delete("^0-9-#{separator}").tr(separator, '.')
99
+ number = amount.delete("^0-9-#{separator}\.").tr(separator, '.')
98
100
  number.to_d if number.present?
99
101
  end || amount
100
102
  end
@@ -132,8 +134,7 @@ module Spree
132
134
  end
133
135
 
134
136
  def is_avs_risky?
135
- return false if avs_response == "D"
136
- return false if avs_response.blank?
137
+ return false if avs_response.blank? || NON_RISKY_AVS_CODES.include?(avs_response)
137
138
  return true
138
139
  end
139
140
 
@@ -144,6 +145,10 @@ module Spree
144
145
  return true
145
146
  end
146
147
 
148
+ def uncaptured_amount
149
+ amount - capture_events.sum(:amount)
150
+ end
151
+
147
152
  private
148
153
 
149
154
  def validate_source
@@ -193,9 +198,5 @@ module Spree
193
198
  def generate_identifier
194
199
  Array.new(8){ IDENTIFIER_CHARS.sample }.join
195
200
  end
196
-
197
- def update_uncaptured_amount
198
- self.uncaptured_amount = amount - capture_events.sum(:amount)
199
- end
200
201
  end
201
202
  end
@@ -50,7 +50,7 @@ module Spree
50
50
  gateway_options
51
51
  )
52
52
 
53
- money = ::Money.new(amount, Spree::Config[:currency])
53
+ money = ::Money.new(amount, currency)
54
54
  capture_events.create!(amount: money.to_f)
55
55
  handle_response(response, :complete, :failure)
56
56
  end
@@ -21,7 +21,7 @@
21
21
  module Spree
22
22
  class Product < ActiveRecord::Base
23
23
  extend FriendlyId
24
- friendly_id :name, use: :slugged
24
+ friendly_id :slug_candidates, use: :slugged
25
25
 
26
26
  acts_as_paranoid
27
27
  has_many :product_option_types, dependent: :destroy, inverse_of: :product
@@ -77,9 +77,12 @@ module Spree
77
77
  validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
78
78
  validates :shipping_category_id, presence: true
79
79
  validates :slug, length: { minimum: 3 }
80
+ validates :slug, uniqueness: true
80
81
 
81
82
  before_validation :normalize_slug, on: :update
82
83
 
84
+ after_destroy :punch_slug
85
+
83
86
  attr_accessor :option_values_hash
84
87
 
85
88
  accepts_nested_attributes_for :product_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? }
@@ -213,6 +216,7 @@ module Spree
213
216
  end
214
217
 
215
218
  private
219
+
216
220
  def normalize_slug
217
221
  self.slug = normalize_friendly_id(slug)
218
222
  end
@@ -265,6 +269,19 @@ module Spree
265
269
  taxonomy_ids_to_touch = taxons_to_touch.map(&:taxonomy_id).flatten.uniq
266
270
  Spree::Taxonomy.where(id: taxonomy_ids_to_touch).update_all(updated_at: Time.current)
267
271
  end
272
+
273
+ # Try building a slug based on the following fields in increasing order of specificity.
274
+ def slug_candidates
275
+ [
276
+ :name,
277
+ [:name, :sku]
278
+ ]
279
+ end
280
+
281
+ def punch_slug
282
+ update(slug: "#{Time.now.to_i}_#{slug}") # punch slug with date prefix to allow reuse of original
283
+ end
284
+
268
285
  end
269
286
  end
270
287
 
@@ -41,8 +41,9 @@ module Spree
41
41
  # Ensure a negative amount which does not exceed the sum of the order's
42
42
  # item_total and ship_total
43
43
  def compute_amount(adjustable)
44
- amount = self.calculator.compute(adjustable).to_f.abs
45
- [adjustable.total, amount].min * -1
44
+ promotion_amount = self.calculator.compute(adjustable).to_f.abs
45
+
46
+ [adjustable.amount, promotion_amount].min * -1
46
47
  end
47
48
 
48
49
  private
@@ -62,7 +63,8 @@ module Spree
62
63
  end
63
64
 
64
65
  def deals_with_adjustments
65
- adjustment_scope = Adjustment.includes(:order).references(:spree_orders)
66
+ adjustment_scope = self.adjustments.includes(:order).references(:spree_orders)
67
+
66
68
  # For incomplete orders, remove the adjustment completely.
67
69
  adjustment_scope.where("spree_orders.completed_at IS NULL").each do |adjustment|
68
70
  adjustment.destroy
@@ -2,6 +2,8 @@
2
2
  # PromotionActions perform the necessary tasks when a promotion is activated by an event and determined to be eligible.
3
3
  module Spree
4
4
  class PromotionAction < ActiveRecord::Base
5
+ acts_as_paranoid
6
+
5
7
  belongs_to :promotion, class_name: 'Spree::Promotion'
6
8
 
7
9
  scope :of_type, ->(t) { where(type: t) }
@@ -36,15 +36,16 @@ module Spree
36
36
 
37
37
  def handle_present_promotion(promotion)
38
38
  return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded?(order)
39
+ return promotion_applied if promotion_exists_on_order?(order, promotion)
39
40
  return ineligible_for_this_order unless promotion.eligible?(order)
40
41
 
41
42
  # If any of the actions for the promotion return `true`,
42
43
  # then result here will also be `true`.
43
44
  result = promotion.activate(:order => order)
44
45
  if result
45
- determine_promotion_application_result(result)
46
+ determine_promotion_application_result
46
47
  else
47
- self.error = Spree.t(:coupon_code_already_applied)
48
+ self.error = Spree.t(:coupon_code_unknown_error)
48
49
  end
49
50
  end
50
51
 
@@ -56,7 +57,15 @@ module Spree
56
57
  self.error = Spree.t(:coupon_code_not_eligible)
57
58
  end
58
59
 
59
- def determine_promotion_application_result(result)
60
+ def promotion_applied
61
+ self.error = Spree.t(:coupon_code_already_applied)
62
+ end
63
+
64
+ def promotion_exists_on_order?(order, promotion)
65
+ order.promotions.include? promotion
66
+ end
67
+
68
+ def determine_promotion_application_result
60
69
  detector = lambda { |p|
61
70
  if p.source.promotion.code
62
71
  p.source.promotion.code.downcase == order.coupon_code.downcase
@@ -67,7 +76,7 @@ module Spree
67
76
  discount ||= order.shipment_adjustments.promotion.detect(&detector)
68
77
  discount ||= order.adjustments.promotion.detect(&detector)
69
78
 
70
- if result and discount.eligible
79
+ if discount.eligible
71
80
  order.update_totals
72
81
  order.persist_totals
73
82
  self.success = Spree.t(:coupon_code_applied)
@@ -3,7 +3,7 @@ module Spree
3
3
  belongs_to :order, class_name: 'Spree::Order'
4
4
 
5
5
  has_many :inventory_units
6
- has_one :stock_location
6
+ belongs_to :stock_location
7
7
  before_create :generate_number
8
8
  before_save :force_positive_amount
9
9
 
@@ -81,16 +81,17 @@ module Spree
81
81
  end
82
82
 
83
83
  def process_return
84
- inventory_units.each do |iu|
84
+ inventory_units(include: :variant).each do |iu|
85
85
  iu.return!
86
- stock_item = Spree::StockItem.where(variant_id: iu.variant.id, stock_location_id: stock_location_id).first
87
- Spree::StockMovement.create!(stock_item_id: stock_item.id, quantity: 1)
86
+
87
+ if iu.variant.should_track_inventory?
88
+ if stock_item = Spree::StockItem.find_by(variant_id: iu.variant_id, stock_location_id: stock_location_id)
89
+ Spree::StockMovement.create!(stock_item_id: stock_item.id, quantity: 1)
90
+ end
91
+ end
88
92
  end
89
93
 
90
- credit = Adjustment.new(amount: compute_amount, label: Spree.t(:rma_credit))
91
- credit.source = self
92
- credit.adjustable = order
93
- credit.save
94
+ Adjustment.create(adjustable: order, amount: compute_amount, label: Spree.t(:rma_credit), source: self)
94
95
  order.update!
95
96
 
96
97
  order.return if inventory_units.all?(&:returned?)
@@ -52,8 +52,8 @@ module Spree
52
52
  package.shipping_methods.select do |ship_method|
53
53
  calculator = ship_method.calculator
54
54
  begin
55
- calculator.available?(package) &&
56
55
  ship_method.include?(order.ship_address) &&
56
+ calculator.available?(package) &&
57
57
  (calculator.preferences[:currency].nil? ||
58
58
  calculator.preferences[:currency] == currency)
59
59
  rescue Exception => exception
@@ -71,12 +71,12 @@ module Spree
71
71
  # All zoneables belonging to the zone members. Will be a collection of either
72
72
  # countries or states depending on the zone type.
73
73
  def zoneables
74
- members.collect(&:zoneable)
74
+ members.includes(:zoneable).collect(&:zoneable)
75
75
  end
76
76
 
77
77
  def country_ids
78
78
  if kind == 'country'
79
- members.collect(&:zoneable_id)
79
+ members.pluck(:zoneable_id)
80
80
  else
81
81
  []
82
82
  end
@@ -84,7 +84,7 @@ module Spree
84
84
 
85
85
  def state_ids
86
86
  if kind == 'state'
87
- members.collect(&:zoneable_id)
87
+ members.pluck(:zoneable_id)
88
88
  else
89
89
  []
90
90
  end
@@ -117,9 +117,9 @@ module Spree
117
117
  return false if zone_members.empty? || target.zone_members.empty?
118
118
 
119
119
  if kind == target.kind
120
- return false if target.zoneables.any? { |target_zoneable| zoneables.exclude?(target_zoneable) }
120
+ return false if (target.zoneables.collect(&:id) - zoneables.collect(&:id)).present?
121
121
  else
122
- return false if target.zoneables.any? { |target_state| zoneables.exclude?(target_state.country) }
122
+ return false if (target.zoneables.collect(&:country).collect(&:id) - zoneables.collect(&:id)).present?
123
123
  end
124
124
  true
125
125
  end
@@ -10,11 +10,29 @@
10
10
  <% end %>
11
11
  ============================================================
12
12
  <%= Spree.t('order_mailer.confirm_email.subtotal', :subtotal => @order.display_item_total) %>
13
+ <% if @order.line_item_adjustments.exists? %>
14
+ <% if @order.all_adjustments.promotion.eligible.exists? %>
15
+ <% @order.all_adjustments.promotion.eligible.group_by(&:label).each do |label, adjustments| %>
16
+ <%= Spree.t(:promotion) %>: <%= label %> <%= Spree::Money.new(adjustments.sum(&:amount), currency: @order.currency) %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
13
20
 
14
- <% @order.adjustments.eligible.each do |adjustment| %>
15
- <%= raw(adjustment.label) %> <%= adjustment.display_amount %>
21
+ <% @order.shipments.group_by { |s| s.selected_shipping_rate.try(:name) }.each do |name, shipments| %>
22
+ <%= Spree.t(:shipping) %>: <%= name %> <%= Spree::Money.new(shipments.sum(&:discounted_cost), currency: @order.currency) %>
23
+ <% end %>
24
+
25
+ <% if @order.all_adjustments.eligible.tax.exists? %>
26
+ <% @order.all_adjustments.eligible.tax.group_by(&:label).each do |label, adjustments| %>
27
+ <%= Spree.t(:tax) %>: <%= label %> <%= Spree::Money.new(adjustments.sum(&:amount), currency: @order.currency) %>
28
+ <% end %>
16
29
  <% end %>
17
30
 
31
+ <% @order.adjustments.eligible.each do |adjustment| %>
32
+ <% next if (adjustment.source_type == 'Spree::TaxRate') and (adjustment.amount == 0) %>
33
+ <%= adjustment.label %> <%= adjustment.display_amount %>
34
+ <% end %>
35
+ ============================================================
18
36
  <%= Spree.t('order_mailer.confirm_email.total', :total => @order.display_total) %>
19
37
 
20
38
  <%= Spree.t('order_mailer.confirm_email.thanks') %>
@@ -1,13 +1,13 @@
1
1
  <script>
2
2
  if (Spree === undefined) {
3
- var Spree = {}
3
+ var Spree = {};
4
4
  }
5
5
  if (Spree.routes == undefined) {
6
- Spree.routes = {}
6
+ Spree.routes = {};
7
7
  }
8
- Spree.routes.states_search = "<%= spree.api_states_path(:format => 'json') %>"
8
+ Spree.routes.states_search = "<%= spree.api_states_path(:format => 'json') %>";
9
9
  Spree.routes.apply_coupon_code = function(order_id) {
10
- return "<%= spree.api_orders_path %>/" + order_id + "/apply_coupon_code"
10
+ return "<%= spree.api_orders_path %>/" + order_id + "/apply_coupon_code";
11
11
  }
12
- Spree.routes.root = "<%= spree.root_url if spree.respond_to? :root_url %>"
12
+ Spree.routes.root = "<%= spree.root_url if spree.respond_to? :root_url %>";
13
13
  </script>
@@ -485,6 +485,7 @@ en:
485
485
  coupon_code_max_usage: Coupon code usage limit exceeded
486
486
  coupon_code_not_eligible: This coupon code is not eligible for this order
487
487
  coupon_code_not_found: The coupon code you entered doesn't exist. Please try again.
488
+ coupon_code_unknown_error: This coupon code could not be applied to the cart at this time.
488
489
  create: Create
489
490
  create_a_new_account: Create a new account
490
491
  created_at: Created At
@@ -8,9 +8,9 @@ class AddLineItemIdToSpreeInventoryUnits < ActiveRecord::Migration
8
8
  shipments = Spree::Shipment.includes(:inventory_units, :order)
9
9
 
10
10
  shipments.find_each do |shipment|
11
- shipment.inventory_units.group_by(&:variant).each do |variant, units|
11
+ shipment.inventory_units.group_by(&:variant_id).each do |variant, units|
12
12
 
13
- line_item = shipment.order.find_line_item_by_variant(variant)
13
+ line_item = shipment.order.line_items.find_by(variant_id: variant_id)
14
14
  next unless line_item
15
15
 
16
16
  Spree::InventoryUnit.where(id: units.map(&:id)).update_all(line_item_id: line_item.id)
@@ -0,0 +1,12 @@
1
+ class SetShipmentTotalForUsersUpgrading < ActiveRecord::Migration
2
+ def up
3
+ # NOTE You might not need this at all unless you're upgrading from Spree 2.1.x
4
+ # or below. For those upgrading this should populate the Order#shipment_total
5
+ # for legacy orders
6
+ execute "UPDATE spree_orders
7
+ SET shipment_total = (SELECT SUM(spree_shipments.cost) AS sum_id
8
+ FROM spree_shipments
9
+ WHERE spree_shipments.order_id = spree_orders.id)
10
+ WHERE spree_orders.completed_at IS NOT NULL AND spree_orders.shipment_total = 0"
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ class AddDeletedAtToSpreePromotionActions < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_promotion_actions, :deleted_at, :datetime
4
+ add_index :spree_promotion_actions, :deleted_at
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class RemoveUncapturedAmountFromSpreePayments < ActiveRecord::Migration
2
+ def change
3
+ remove_column :spree_payments, :uncaptured_amount
4
+ end
5
+ end
@@ -2,8 +2,12 @@ module Spree
2
2
  class ProductDuplicator
3
3
  attr_accessor :product
4
4
 
5
- def initialize(product)
5
+ @@clone_images_default = true
6
+ mattr_accessor :clone_images_default
7
+
8
+ def initialize(product, include_images = @@clone_images_default)
6
9
  @product = product
10
+ @include_images = include_images
7
11
  end
8
12
 
9
13
  def duplicate
@@ -38,7 +42,7 @@ module Spree
38
42
  master.dup.tap do |new_master|
39
43
  new_master.sku = "COPY OF #{master.sku}"
40
44
  new_master.deleted_at = nil
41
- new_master.images = master.images.map { |image| duplicate_image image }
45
+ new_master.images = master.images.map { |image| duplicate_image image } if @include_images
42
46
  new_master.price = master.price
43
47
  new_master.currency = master.currency
44
48
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
2
  def self.version
3
- "2.2.2"
3
+ "2.2.3"
4
4
  end
5
5
  end
@@ -2,7 +2,7 @@ FactoryGirl.define do
2
2
  factory :promotion, class: Spree::Promotion do
3
3
  name 'Promo'
4
4
 
5
- factory :promotion_with_item_adjustment do
5
+ trait :with_line_item_adjustment do
6
6
  ignore do
7
7
  adjustment_rate 10
8
8
  end
@@ -15,6 +15,36 @@ FactoryGirl.define do
15
15
  promotion.save
16
16
  end
17
17
  end
18
+ factory :promotion_with_item_adjustment, traits: [:with_line_item_adjustment]
19
+
20
+ trait :with_order_adjustment do
21
+ ignore do
22
+ order_adjustment_amount 10
23
+ end
24
+
25
+ after(:create) do |promotion, evaluator|
26
+ calculator = Spree::Calculator::FlatRate.new
27
+ calculator.preferred_amount = evaluator.order_adjustment_amount
28
+ action = Spree::Promotion::Actions::CreateAdjustment.create!(:calculator => calculator)
29
+ promotion.actions << action
30
+ promotion.save!
31
+ end
32
+ end
33
+
34
+ trait :with_item_total_rule do
35
+ ignore do
36
+ item_total_threshold_amount 10
37
+ end
38
+
39
+ after(:create) do |promotion, evaluator|
40
+ rule = Spree::Promotion::Rules::ItemTotal.create!(
41
+ preferred_operator: 'gte',
42
+ preferred_amount: evaluator.item_total_threshold_amount
43
+ )
44
+ promotion.rules << rule
45
+ promotion.save!
46
+ end
47
+ end
18
48
 
19
49
  end
20
50
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Schofield
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-15 00:00:00.000000000 Z
11
+ date: 2014-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemerchant
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.42.3
19
+ version: 1.43.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.42.3
26
+ version: 1.43.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: acts_as_list
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -112,16 +112,16 @@ dependencies:
112
112
  name: friendly_id
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '='
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 5.0.3
117
+ version: 5.0.4
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '='
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 5.0.3
124
+ version: 5.0.4
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: highline
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -226,14 +226,14 @@ dependencies:
226
226
  requirements:
227
227
  - - "~>"
228
228
  - !ruby/object:Gem::Version
229
- version: 4.0.5
229
+ version: 4.0.6
230
230
  type: :runtime
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
234
  - - "~>"
235
235
  - !ruby/object:Gem::Version
236
- version: 4.0.5
236
+ version: 4.0.6
237
237
  - !ruby/object:Gem::Dependency
238
238
  name: ransack
239
239
  requirement: !ruby/object:Gem::Requirement
@@ -590,6 +590,9 @@ files:
590
590
  - db/migrate/20140219060952_add_considered_risky_to_orders.rb
591
591
  - db/migrate/20140307235515_add_user_id_to_spree_credit_cards.rb
592
592
  - db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb
593
+ - db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb
594
+ - db/migrate/20140609201656_add_deleted_at_to_spree_promotion_actions.rb
595
+ - db/migrate/20140616202624_remove_uncaptured_amount_from_spree_payments.rb
593
596
  - db/seeds.rb
594
597
  - lib/generators/spree/custom_user/custom_user_generator.rb
595
598
  - lib/generators/spree/custom_user/templates/authentication_helpers.rb.tt
@@ -738,7 +741,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
738
741
  version: '0'
739
742
  requirements: []
740
743
  rubyforge_project:
741
- rubygems_version: 2.2.0
744
+ rubygems_version: 2.2.2
742
745
  signing_key:
743
746
  specification_version: 4
744
747
  summary: The bare bones necessary for Spree.