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.
- checksums.yaml +4 -4
- data/app/models/friendly_id/slug_decorator.rb +3 -0
- data/app/models/spree/adjustment.rb +1 -1
- data/app/models/spree/calculator.rb +5 -0
- data/app/models/spree/customer_return.rb +22 -16
- data/app/models/spree/gateway/bogus.rb +4 -0
- data/app/models/spree/line_item.rb +1 -1
- data/app/models/spree/order.rb +22 -48
- data/app/models/spree/order/checkout.rb +270 -255
- data/app/models/spree/order_contents.rb +64 -61
- data/app/models/spree/order_merger.rb +65 -0
- data/app/models/spree/payment.rb +5 -0
- data/app/models/spree/payment/processing.rb +2 -1
- data/app/models/spree/payment_method/check.rb +12 -2
- data/app/models/spree/product.rb +5 -0
- data/app/models/spree/promotion_handler/cart.rb +18 -14
- data/app/models/spree/shipment.rb +1 -1
- data/app/models/spree/stock/availability_validator.rb +10 -9
- data/app/models/spree/stock/content_item.rb +8 -0
- data/app/models/spree/stock/package.rb +8 -0
- data/app/models/spree/stock_item.rb +5 -1
- data/app/models/spree/stock_movement.rb +6 -1
- data/app/models/spree/variant.rb +18 -15
- data/app/models/spree/zone.rb +39 -29
- data/app/views/spree/order_mailer/cancel_email.html.erb +1 -1
- data/app/views/spree/order_mailer/cancel_email.text.erb +1 -1
- data/app/views/spree/order_mailer/confirm_email.html.erb +1 -1
- data/app/views/spree/order_mailer/confirm_email.text.erb +1 -1
- data/config/initializers/user_class_extensions.rb +4 -0
- data/config/locales/en.yml +4 -3
- data/db/default/spree/default_reimbursement_type.rb +1 -0
- data/db/migrate/20150515211137_fix_adjustment_order_id.rb +70 -0
- data/db/migrate/20150522181728_add_deleted_at_to_friendly_id_slugs.rb +6 -0
- data/db/migrate/20150609093816_increase_scale_on_pre_tax_amounts.rb +16 -0
- data/db/migrate/20150707204155_enable_acts_as_paranoid_on_calculators.rb +6 -0
- data/lib/spree/core/controller_helpers/auth.rb +1 -1
- data/lib/spree/core/engine.rb +7 -0
- data/lib/spree/core/validators/email.rb +7 -3
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/permitted_attributes.rb +2 -2
- data/lib/spree/testing_support/common_rake.rb +3 -8
- data/lib/spree/testing_support/factories.rb +1 -1
- data/lib/spree/testing_support/factories/order_factory.rb +11 -0
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- metadata +11 -4
@@ -7,7 +7,9 @@ module Spree
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def add(variant, quantity = 1, options = {})
|
10
|
+
timestamp = Time.now
|
10
11
|
line_item = add_to_line_item(variant, quantity, options)
|
12
|
+
options[:line_item_created] = true if timestamp <= line_item.created_at
|
11
13
|
after_add_or_remove(line_item, options)
|
12
14
|
end
|
13
15
|
|
@@ -22,10 +24,10 @@ module Spree
|
|
22
24
|
# Update totals, then check if the order is eligible for any cart promotions.
|
23
25
|
# If we do not update first, then the item total will be wrong and ItemTotal
|
24
26
|
# promotion rules would not be triggered.
|
25
|
-
|
27
|
+
persist_totals
|
26
28
|
PromotionHandler::Cart.new(order).activate
|
27
29
|
order.ensure_updated_shipments
|
28
|
-
|
30
|
+
persist_totals
|
29
31
|
true
|
30
32
|
else
|
31
33
|
false
|
@@ -33,80 +35,81 @@ module Spree
|
|
33
35
|
end
|
34
36
|
|
35
37
|
private
|
36
|
-
def after_add_or_remove(line_item, options = {})
|
37
|
-
reload_totals
|
38
|
-
shipment = options[:shipment]
|
39
|
-
shipment.present? ? shipment.update_amounts : order.ensure_updated_shipments
|
40
|
-
PromotionHandler::Cart.new(order, line_item).activate
|
41
|
-
Adjustable::AdjustmentsUpdater.update(line_item)
|
42
|
-
reload_totals
|
43
|
-
line_item
|
44
|
-
end
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
39
|
+
def after_add_or_remove(line_item, options = {})
|
40
|
+
persist_totals
|
41
|
+
shipment = options[:shipment]
|
42
|
+
shipment.present? ? shipment.update_amounts : order.ensure_updated_shipments
|
43
|
+
PromotionHandler::Cart.new(order, line_item).activate
|
44
|
+
Adjustable::AdjustmentsUpdater.update(line_item)
|
45
|
+
TaxRate.adjust(order, [line_item]) if options[:line_item_created]
|
46
|
+
persist_totals
|
47
|
+
line_item
|
48
|
+
end
|
49
|
+
|
50
|
+
def filter_order_items(params)
|
51
|
+
filtered_params = params.symbolize_keys
|
52
|
+
return filtered_params if filtered_params[:line_items_attributes].nil? || filtered_params[:line_items_attributes][:id]
|
49
53
|
|
50
|
-
|
54
|
+
line_item_ids = order.line_items.pluck(:id)
|
51
55
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
56
|
+
params[:line_items_attributes].each_pair do |id, value|
|
57
|
+
unless line_item_ids.include?(value[:id].to_i) || value[:variant_id].present?
|
58
|
+
filtered_params[:line_items_attributes].delete(id)
|
56
59
|
end
|
57
|
-
filtered_params
|
58
60
|
end
|
61
|
+
filtered_params
|
62
|
+
end
|
59
63
|
|
60
|
-
|
61
|
-
|
62
|
-
|
64
|
+
def order_updater
|
65
|
+
@updater ||= OrderUpdater.new(order)
|
66
|
+
end
|
63
67
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
68
|
+
def persist_totals
|
69
|
+
order_updater.update_item_count
|
70
|
+
order_updater.update
|
71
|
+
end
|
69
72
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
if line_item
|
74
|
-
line_item.quantity += quantity.to_i
|
75
|
-
line_item.currency = currency unless currency.nil?
|
76
|
-
else
|
77
|
-
opts = { currency: order.currency }.merge ActionController::Parameters.new(options).
|
78
|
-
permit(PermittedAttributes.line_item_attributes)
|
79
|
-
line_item = order.line_items.new(quantity: quantity,
|
80
|
-
variant: variant,
|
81
|
-
options: opts)
|
82
|
-
end
|
83
|
-
line_item.target_shipment = options[:shipment] if options.has_key? :shipment
|
84
|
-
line_item.save!
|
85
|
-
line_item
|
86
|
-
end
|
73
|
+
def add_to_line_item(variant, quantity, options = {})
|
74
|
+
line_item = grab_line_item_by_variant(variant, false, options)
|
87
75
|
|
88
|
-
|
89
|
-
line_item
|
90
|
-
line_item.
|
91
|
-
|
76
|
+
if line_item
|
77
|
+
line_item.quantity += quantity.to_i
|
78
|
+
line_item.currency = currency unless currency.nil?
|
79
|
+
else
|
80
|
+
opts = { currency: order.currency }.merge ActionController::Parameters.new(options).
|
81
|
+
permit(PermittedAttributes.line_item_attributes)
|
82
|
+
line_item = order.line_items.new(quantity: quantity,
|
83
|
+
variant: variant,
|
84
|
+
options: opts)
|
85
|
+
end
|
86
|
+
line_item.target_shipment = options[:shipment] if options.has_key? :shipment
|
87
|
+
line_item.save!
|
88
|
+
line_item
|
89
|
+
end
|
92
90
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
91
|
+
def remove_from_line_item(variant, quantity, options = {})
|
92
|
+
line_item = grab_line_item_by_variant(variant, true, options)
|
93
|
+
line_item.quantity -= quantity
|
94
|
+
line_item.target_shipment= options[:shipment]
|
98
95
|
|
99
|
-
|
96
|
+
if line_item.quantity.zero?
|
97
|
+
order.line_items.destroy(line_item)
|
98
|
+
else
|
99
|
+
line_item.save!
|
100
100
|
end
|
101
101
|
|
102
|
-
|
103
|
-
|
102
|
+
line_item
|
103
|
+
end
|
104
104
|
|
105
|
-
|
106
|
-
|
107
|
-
end
|
105
|
+
def grab_line_item_by_variant(variant, raise_error = false, options = {})
|
106
|
+
line_item = order.find_line_item_by_variant(variant, options)
|
108
107
|
|
109
|
-
|
108
|
+
if !line_item.present? && raise_error
|
109
|
+
raise ActiveRecord::RecordNotFound, "Line item not found for variant #{variant.sku}"
|
110
110
|
end
|
111
|
+
|
112
|
+
line_item
|
113
|
+
end
|
111
114
|
end
|
112
115
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Spree
|
2
|
+
class OrderMerger
|
3
|
+
attr_accessor :order
|
4
|
+
delegate :updater, to: :order
|
5
|
+
|
6
|
+
def initialize(order)
|
7
|
+
@order = order
|
8
|
+
end
|
9
|
+
|
10
|
+
def merge!(other_order, user = nil)
|
11
|
+
other_order.line_items.each do |other_order_line_item|
|
12
|
+
next unless other_order_line_item.currency == order.currency
|
13
|
+
|
14
|
+
current_line_item = find_matching_line_item(other_order_line_item)
|
15
|
+
handle_merge(current_line_item, other_order_line_item)
|
16
|
+
end
|
17
|
+
|
18
|
+
set_user(user)
|
19
|
+
persist_merge
|
20
|
+
|
21
|
+
# So that the destroy doesn't take out line items which may have been re-assigned
|
22
|
+
other_order.line_items.reload
|
23
|
+
other_order.destroy
|
24
|
+
end
|
25
|
+
|
26
|
+
# Compare the line item of the other order with mine.
|
27
|
+
# Make sure you allow any extensions to chime in on whether or
|
28
|
+
# not the extension-specific parts of the line item match
|
29
|
+
def find_matching_line_item(other_order_line_item)
|
30
|
+
order.line_items.detect do |my_li|
|
31
|
+
my_li.variant == other_order_line_item.variant &&
|
32
|
+
order.line_item_comparison_hooks.all? do |hook|
|
33
|
+
order.send(hook, my_li, other_order_line_item.serializable_hash)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_user(user = nil)
|
39
|
+
order.associate_user!(user) if !order.user && !user.blank?
|
40
|
+
end
|
41
|
+
|
42
|
+
# The idea is the end developer can choose to override the merge
|
43
|
+
# to their own choosing. Default is merge with errors.
|
44
|
+
def handle_merge(current_line_item, other_order_line_item)
|
45
|
+
if current_line_item
|
46
|
+
current_line_item.quantity += other_order_line_item.quantity
|
47
|
+
handle_error(current_line_item) unless current_line_item.save
|
48
|
+
else
|
49
|
+
other_order_line_item.order_id = order.id
|
50
|
+
handle_error(other_order_line_item) unless other_order_line_item.save
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Change the error messages as you choose.
|
55
|
+
def handle_error(line_item)
|
56
|
+
order.errors[:base] << line_item.errors.full_messages
|
57
|
+
end
|
58
|
+
|
59
|
+
def persist_merge
|
60
|
+
updater.update_item_count
|
61
|
+
updater.update_item_total
|
62
|
+
updater.persist_totals
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/app/models/spree/payment.rb
CHANGED
@@ -26,6 +26,7 @@ module Spree
|
|
26
26
|
has_many :capture_events, class_name: 'Spree::PaymentCaptureEvent'
|
27
27
|
has_many :refunds, inverse_of: :payment
|
28
28
|
|
29
|
+
validates_presence_of :payment_method
|
29
30
|
before_validation :validate_source
|
30
31
|
|
31
32
|
after_save :create_payment_profile, if: :profiles_supported?
|
@@ -173,6 +174,10 @@ module Spree
|
|
173
174
|
amount - captured_amount
|
174
175
|
end
|
175
176
|
|
177
|
+
def editable?
|
178
|
+
checkout? || pending?
|
179
|
+
end
|
180
|
+
|
176
181
|
private
|
177
182
|
|
178
183
|
def validate_source
|
@@ -15,17 +15,27 @@ module Spree
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def capture(*args)
|
18
|
-
|
18
|
+
simulated_successful_billing_response
|
19
19
|
end
|
20
20
|
|
21
21
|
def cancel(response); end
|
22
22
|
|
23
23
|
def void(*args)
|
24
|
-
|
24
|
+
simulated_successful_billing_response
|
25
25
|
end
|
26
26
|
|
27
27
|
def source_required?
|
28
28
|
false
|
29
29
|
end
|
30
|
+
|
31
|
+
def credit(*args)
|
32
|
+
simulated_successful_billing_response
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def simulated_successful_billing_response
|
38
|
+
ActiveMerchant::Billing::Response.new(true, "", {}, {})
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
data/app/models/spree/product.rb
CHANGED
@@ -74,6 +74,7 @@ module Spree
|
|
74
74
|
after_create :build_variants_from_option_values_hash, if: :option_values_hash
|
75
75
|
|
76
76
|
after_destroy :punch_slug
|
77
|
+
after_restore :update_slug_history
|
77
78
|
|
78
79
|
after_initialize :ensure_master
|
79
80
|
|
@@ -267,6 +268,10 @@ module Spree
|
|
267
268
|
update_column :slug, "#{Time.now.to_i}_#{slug}"[0..254] unless frozen?
|
268
269
|
end
|
269
270
|
|
271
|
+
def update_slug_history
|
272
|
+
self.save!
|
273
|
+
end
|
274
|
+
|
270
275
|
def anything_changed?
|
271
276
|
changed? || @nested_changes
|
272
277
|
end
|
@@ -29,22 +29,26 @@ module Spree
|
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
32
|
-
def promotions
|
33
|
-
promo_table = Promotion.arel_table
|
34
|
-
join_table = Arel::Table.new(:spree_orders_promotions)
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
def promotions
|
34
|
+
# AR cannot bind raw ASTs to prepared statements. There always must be a manager around.
|
35
|
+
# Also Postgresql requires an aliased table for `SELECT * FROM (subexpression) AS alias`.
|
36
|
+
# And Sqlite3 cannot work on outher parenthesis from `(left UNION right)`.
|
37
|
+
# So this construct makes both happy.
|
38
|
+
select = Arel::SelectManager.new(
|
39
|
+
Promotion,
|
40
|
+
Promotion.arel_table.create_table_alias(
|
41
|
+
order.promotions.active.union(Promotion.active.where(code: nil, path: nil)),
|
42
|
+
Promotion.table_name
|
43
|
+
),
|
44
|
+
)
|
45
|
+
select.project(Arel.star)
|
39
46
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
).or(join_table[:order_id].eq(order.id))
|
46
|
-
).distinct
|
47
|
-
end
|
47
|
+
Promotion.find_by_sql(
|
48
|
+
select,
|
49
|
+
order.promotions.bind_values
|
50
|
+
)
|
51
|
+
end
|
48
52
|
end
|
49
53
|
end
|
50
54
|
end
|
@@ -5,19 +5,20 @@ module Spree
|
|
5
5
|
unit_count = line_item.inventory_units.size
|
6
6
|
return if unit_count >= line_item.quantity
|
7
7
|
quantity = line_item.quantity - unit_count
|
8
|
+
return if quantity.zero?
|
8
9
|
|
9
10
|
quantifier = Stock::Quantifier.new(line_item.variant)
|
10
11
|
|
11
|
-
|
12
|
-
variant = line_item.variant
|
13
|
-
display_name = %Q{#{variant.name}}
|
14
|
-
display_name += %Q{ (#{variant.options_text})} unless variant.options_text.blank?
|
12
|
+
return if quantifier.can_supply?(quantity)
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
variant = line_item.variant
|
15
|
+
display_name = "#{variant.name}"
|
16
|
+
display_name += " (#{variant.options_text})" unless variant.options_text.blank?
|
17
|
+
|
18
|
+
line_item.errors[:quantity] << Spree.t(
|
19
|
+
:selected_quantity_not_available,
|
20
|
+
item: display_name.inspect
|
21
|
+
)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
@@ -8,7 +8,11 @@ module Spree
|
|
8
8
|
|
9
9
|
validates_presence_of :stock_location, :variant
|
10
10
|
validates_uniqueness_of :variant_id, scope: [:stock_location_id, :deleted_at]
|
11
|
-
|
11
|
+
|
12
|
+
validates_numericality_of :count_on_hand,
|
13
|
+
greater_than_or_equal_to: 0,
|
14
|
+
less_than_or_equal_to: 2**31 - 1,
|
15
|
+
only_integer: true, if: :verify_count_on_hand?
|
12
16
|
|
13
17
|
delegate :weight, :should_track_inventory?, to: :variant
|
14
18
|
|
@@ -6,7 +6,12 @@ module Spree
|
|
6
6
|
after_create :update_stock_item_quantity
|
7
7
|
|
8
8
|
validates :stock_item, presence: true
|
9
|
-
validates :quantity, presence: true
|
9
|
+
validates :quantity, presence: true, numericality: {
|
10
|
+
greater_than_or_equal_to: -2**31,
|
11
|
+
less_than_or_equal_to: 2**31-1,
|
12
|
+
only_integer: true,
|
13
|
+
allow_nil: true
|
14
|
+
}
|
10
15
|
|
11
16
|
scope :recent, -> { order('created_at DESC') }
|
12
17
|
|