solidus_core 1.3.2 → 1.4.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of solidus_core might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +15 -0
- data/app/helpers/spree/base_helper.rb +6 -2
- data/app/mailers/spree/base_mailer.rb +1 -1
- data/app/mailers/spree/carton_mailer.rb +1 -1
- data/app/mailers/spree/order_mailer.rb +2 -2
- data/app/mailers/spree/reimbursement_mailer.rb +7 -7
- data/app/mailers/spree/test_mailer.rb +3 -2
- data/app/models/concerns/spree/user_payment_source.rb +1 -1
- data/app/models/spree/address.rb +14 -4
- data/app/models/spree/adjustment.rb +11 -19
- data/app/models/spree/app_configuration.rb +23 -1
- data/app/models/spree/base.rb +9 -0
- data/app/models/spree/country.rb +2 -2
- data/app/models/spree/exchange.rb +1 -1
- data/app/models/spree/gateway.rb +1 -1
- data/app/models/spree/item_adjustments.rb +7 -0
- data/app/models/spree/line_item.rb +1 -13
- data/app/models/spree/order/checkout.rb +19 -16
- data/app/models/spree/order/payments.rb +0 -2
- data/app/models/spree/order.rb +7 -21
- data/app/models/spree/order_contents.rb +60 -4
- data/app/models/spree/order_merger.rb +2 -4
- data/app/models/spree/order_shipping.rb +1 -1
- data/app/models/spree/order_update_attributes.rb +0 -2
- data/app/models/spree/order_updater.rb +14 -11
- data/app/models/spree/payment.rb +2 -3
- data/app/models/spree/payment_create.rb +5 -2
- data/app/models/spree/payment_method/store_credit.rb +6 -5
- data/app/models/spree/product/scopes.rb +2 -1
- data/app/models/spree/product.rb +2 -5
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +5 -3
- data/app/models/spree/promotion/actions/free_shipping.rb +1 -1
- data/app/models/spree/promotion/rules/option_value.rb +2 -3
- data/app/models/spree/promotion/rules/product.rb +9 -3
- data/app/models/spree/promotion/rules/taxon.rb +33 -7
- data/app/models/spree/promotion/rules/user.rb +2 -3
- data/app/models/spree/promotion.rb +16 -6
- data/app/models/spree/promotion_handler/coupon.rb +1 -1
- data/app/models/spree/shipment.rb +12 -16
- data/app/models/spree/stock/estimator.rb +1 -1
- data/app/models/spree/stock/package.rb +0 -1
- data/app/models/spree/stock_item.rb +2 -6
- data/app/models/spree/store.rb +2 -1
- data/app/models/spree/tax_rate.rb +1 -1
- data/app/models/spree/wallet/add_payment_sources_to_wallet.rb +29 -0
- data/app/models/spree/wallet/default_payment_builder.rb +26 -0
- data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +46 -0
- data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +1 -1
- data/app/views/spree/shared/_base_mailer_footer.html.erb +1 -20
- data/app/views/spree/shared/_base_mailer_header.html.erb +1 -31
- data/config/initializers/spree_user.rb +1 -1
- data/config/locales/en.yml +58 -34
- data/db/migrate/20130414000512_update_name_fields_on_spree_credit_cards.rb +1 -1
- data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +15 -3
- data/db/migrate/20160527070401_rename_shipment_address_field.rb +7 -0
- data/db/migrate/20160616232103_remove_user_id_from_promotion_rules.rb +11 -0
- data/db/migrate/20160718205341_add_payment_id_index_to_spree_refunds.rb +5 -0
- data/db/migrate/20160718205859_add_reimbursement_id_index_to_spree_refunds.rb +5 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +1 -1
- data/lib/generators/spree/dummy/templates/rails/database.yml +3 -1
- data/lib/generators/spree/dummy/templates/rails/test.rb +4 -2
- data/lib/generators/spree/install/install_generator.rb +6 -16
- data/lib/spree/core/controller_helpers/auth.rb +1 -1
- data/lib/spree/core/controller_helpers/common.rb +1 -1
- data/lib/spree/core/controller_helpers/order.rb +2 -2
- data/lib/spree/core/current_store.rb +5 -1
- data/lib/spree/core/delegate_belongs_to.rb +1 -1
- data/lib/spree/core/routes.rb +6 -32
- data/lib/spree/core/search/base.rb +2 -2
- data/lib/spree/core/stock_configuration.rb +6 -0
- data/lib/spree/core/version.rb +2 -2
- data/lib/spree/core.rb +2 -1
- data/lib/spree/i18n.rb +7 -0
- data/lib/spree/mailer_previews/carton_preview.rb +2 -1
- data/lib/spree/mailer_previews/order_preview.rb +8 -3
- data/lib/spree/mailer_previews/reimbursement_preview.rb +11 -0
- data/lib/spree/migrations.rb +13 -7
- data/lib/spree/permitted_attributes.rb +1 -1
- data/lib/spree/testing_support/capybara_ext.rb +6 -1
- data/lib/spree/testing_support/common_rake.rb +3 -7
- data/lib/spree/testing_support/controller_requests.rb +8 -0
- data/lib/spree/testing_support/factories/order_factory.rb +30 -8
- data/lib/spree/testing_support/factories/payment_factory.rb +1 -1
- data/lib/spree/testing_support/factories/product_factory.rb +6 -0
- data/lib/spree/testing_support/factories/reimbursement_factory.rb +1 -0
- data/lib/tasks/migrations/copy_shipped_shipments_to_cartons.rake +3 -1
- data/solidus_core.gemspec +3 -3
- data/spec/helpers/base_helper_spec.rb +18 -2
- data/spec/lib/i18n_spec.rb +4 -0
- data/spec/lib/spree/core/controller_helpers/order_spec.rb +16 -4
- data/spec/lib/spree/core/controller_helpers/payment_parameters_spec.rb +75 -59
- data/spec/lib/spree/core/delegate_belongs_to_spec.rb +1 -1
- data/spec/lib/spree/core/importer/order_spec.rb +4 -2
- data/spec/lib/spree/core/stock_configuration_spec.rb +17 -0
- data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +195 -0
- data/spec/lib/spree/core/testing_support/factories/payment_factory_spec.rb +6 -0
- data/spec/lib/spree/core/testing_support/factories/reimbursement_factory_spec.rb +6 -0
- data/spec/lib/spree/migrations_spec.rb +3 -9
- data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +1 -1
- data/spec/mailers/carton_mailer_spec.rb +1 -1
- data/spec/mailers/order_mailer_spec.rb +2 -2
- data/spec/mailers/reimbursement_mailer_spec.rb +1 -1
- data/spec/models/spree/address_spec.rb +22 -0
- data/spec/models/spree/adjustment_spec.rb +0 -10
- data/spec/models/spree/carton_spec.rb +1 -1
- data/spec/models/spree/country_spec.rb +9 -1
- data/spec/models/spree/gateway/bogus_spec.rb +1 -1
- data/spec/models/spree/item_adjustments_spec.rb +33 -0
- data/spec/models/spree/line_item_spec.rb +2 -21
- data/spec/models/spree/order/checkout_spec.rb +32 -45
- data/spec/models/spree/order/payment_spec.rb +1 -16
- data/spec/models/spree/order_cancellations_spec.rb +8 -3
- data/spec/models/spree/order_contents_spec.rb +48 -0
- data/spec/models/spree/order_merger_spec.rb +8 -5
- data/spec/models/spree/order_shipping_spec.rb +3 -4
- data/spec/models/spree/order_spec.rb +23 -59
- data/spec/models/spree/order_update_attributes_spec.rb +1 -5
- data/spec/models/spree/order_updater_spec.rb +39 -11
- data/spec/models/spree/payment_create_spec.rb +61 -0
- data/spec/models/spree/payment_method/store_credit_spec.rb +23 -10
- data/spec/models/spree/payment_spec.rb +17 -4
- data/spec/models/spree/product_spec.rb +1 -1
- data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +8 -13
- data/spec/models/spree/promotion/rules/product_spec.rb +33 -0
- data/spec/models/spree/promotion/rules/taxon_spec.rb +52 -8
- data/spec/models/spree/promotion_spec.rb +18 -12
- data/spec/models/spree/return_authorization_spec.rb +0 -16
- data/spec/models/spree/shipment_spec.rb +57 -36
- data/spec/models/spree/stock/coordinator_spec.rb +0 -5
- data/spec/models/spree/stock/package_spec.rb +0 -1
- data/spec/models/spree/stock_item_spec.rb +35 -145
- data/spec/models/spree/store_spec.rb +24 -10
- data/spec/models/spree/tax_rate_spec.rb +2 -2
- data/spec/models/spree/user_spec.rb +1 -1
- data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +20 -12
- data/spec/support/concerns/working_factories.rb +4 -0
- metadata +30 -10
- data/config/routes.rb +0 -1
@@ -6,6 +6,19 @@ module Spree
|
|
6
6
|
@order = order
|
7
7
|
end
|
8
8
|
|
9
|
+
# Add a line items to the order if there is inventory to do so
|
10
|
+
# and populate Promotions
|
11
|
+
#
|
12
|
+
# @params [Spree::Variant] :variant The variant the line_item should
|
13
|
+
# be associated with
|
14
|
+
# @params [Integer] :quantity The line_item quantity
|
15
|
+
# @param [Hash] :options Options for the adding proccess
|
16
|
+
# Valid options:
|
17
|
+
# shipment: [Spree::Shipment] LineItem target shipment
|
18
|
+
# stock_location_quantities:
|
19
|
+
# stock_location_id: The stock location to source from
|
20
|
+
#
|
21
|
+
# @return [Spree::LineItem]
|
9
22
|
def add(variant, quantity = 1, options = {})
|
10
23
|
line_item = add_to_line_item(variant, quantity, options)
|
11
24
|
after_add_or_remove(line_item, options)
|
@@ -17,12 +30,26 @@ module Spree
|
|
17
30
|
end
|
18
31
|
|
19
32
|
def remove_line_item(line_item, options = {})
|
20
|
-
|
33
|
+
order.line_items.destroy(line_item)
|
21
34
|
after_add_or_remove(line_item, options)
|
22
35
|
end
|
23
36
|
|
24
37
|
def update_cart(params)
|
38
|
+
# We need old_tax_address / new_tax_address because we can't rely on methods
|
39
|
+
# offered by ActiveRecord::Dirty to determine if tax_address was updated
|
40
|
+
# because if we update the address, a new record will be created
|
41
|
+
# by the Address.factory instead of the old record being updated
|
42
|
+
|
43
|
+
old_tax_address = order.tax_address
|
44
|
+
|
25
45
|
if order.update_attributes(params)
|
46
|
+
|
47
|
+
new_tax_address = order.tax_address
|
48
|
+
|
49
|
+
if should_recalculate_taxes?(old_tax_address, new_tax_address)
|
50
|
+
order.create_tax_charge!
|
51
|
+
end
|
52
|
+
|
26
53
|
unless order.completed?
|
27
54
|
order.line_items = order.line_items.select { |li| li.quantity > 0 }
|
28
55
|
# Update totals, then check if the order is eligible for any cart promotions.
|
@@ -57,6 +84,36 @@ module Spree
|
|
57
84
|
|
58
85
|
private
|
59
86
|
|
87
|
+
def should_recalculate_taxes?(old_address, new_address)
|
88
|
+
# Related to Solidus issue #894
|
89
|
+
# This is needed because if you update the shipping_address
|
90
|
+
# from the backend on an order that completed checkout,
|
91
|
+
# Taxes were not being recalculated if the Order tax zone
|
92
|
+
# was updated
|
93
|
+
#
|
94
|
+
# Possible cases:
|
95
|
+
#
|
96
|
+
# Case 1:
|
97
|
+
#
|
98
|
+
# If old_address is a TaxLocation it means that the order has not passed
|
99
|
+
# the address checkout state so taxes will be computed by the Order
|
100
|
+
# state machine, so we do not calculate taxes here.
|
101
|
+
#
|
102
|
+
# Case 2 :
|
103
|
+
# If new_address is a TaxLocation, but old_address is not, it means that
|
104
|
+
# an order has somehow lost his TaxAddress. Since it's not supposed to happen,
|
105
|
+
# we do not compute taxes.
|
106
|
+
#
|
107
|
+
# Case 3
|
108
|
+
# Both old_address and new_address are Spree::Address so the order
|
109
|
+
# has completed the checkout or that a registered user has updated his
|
110
|
+
# default addresses. We need to recalculate the taxes.
|
111
|
+
|
112
|
+
return if old_address.is_a?(Spree::Tax::TaxLocation) || new_address.is_a?(Spree::Tax::TaxLocation)
|
113
|
+
|
114
|
+
old_address.try!(:taxation_attributes) != new_address.try!(:taxation_attributes)
|
115
|
+
end
|
116
|
+
|
60
117
|
def after_add_or_remove(line_item, options = {})
|
61
118
|
reload_totals
|
62
119
|
shipment = options[:shipment]
|
@@ -73,7 +130,6 @@ module Spree
|
|
73
130
|
|
74
131
|
def reload_totals
|
75
132
|
order_updater.update
|
76
|
-
order.reload
|
77
133
|
end
|
78
134
|
|
79
135
|
def add_to_line_item(variant, quantity, options = {})
|
@@ -86,7 +142,7 @@ module Spree
|
|
86
142
|
)
|
87
143
|
|
88
144
|
line_item.quantity += quantity.to_i
|
89
|
-
line_item.options = ActionController::Parameters.new(options).permit(PermittedAttributes.line_item_attributes)
|
145
|
+
line_item.options = ActionController::Parameters.new(options).permit(PermittedAttributes.line_item_attributes).to_h
|
90
146
|
|
91
147
|
if line_item.new_record?
|
92
148
|
create_order_stock_locations(line_item, options[:stock_location_quantities])
|
@@ -103,7 +159,7 @@ module Spree
|
|
103
159
|
line_item.target_shipment = options[:shipment]
|
104
160
|
|
105
161
|
if line_item.quantity == 0
|
106
|
-
|
162
|
+
order.line_items.destroy(line_item)
|
107
163
|
else
|
108
164
|
line_item.save!
|
109
165
|
end
|
@@ -111,7 +111,7 @@ module Spree
|
|
111
111
|
current_line_item.quantity += other_order_line_item.quantity
|
112
112
|
handle_error(current_line_item) unless current_line_item.save
|
113
113
|
else
|
114
|
-
|
114
|
+
order.line_items << other_order_line_item
|
115
115
|
handle_error(other_order_line_item) unless other_order_line_item.save
|
116
116
|
end
|
117
117
|
end
|
@@ -135,9 +135,7 @@ module Spree
|
|
135
135
|
# @api private
|
136
136
|
# @return [void]
|
137
137
|
def persist_merge
|
138
|
-
updater.
|
139
|
-
updater.update_item_total
|
140
|
-
updater.persist_totals
|
138
|
+
updater.update
|
141
139
|
end
|
142
140
|
end
|
143
141
|
end
|
@@ -17,7 +17,7 @@ class Spree::OrderShipping
|
|
17
17
|
ship(
|
18
18
|
inventory_units: shipment.inventory_units.shippable,
|
19
19
|
stock_location: shipment.stock_location,
|
20
|
-
address: shipment.
|
20
|
+
address: shipment.order.ship_address,
|
21
21
|
shipping_method: shipment.shipping_method,
|
22
22
|
shipped_at: Time.current,
|
23
23
|
external_number: external_number,
|
@@ -14,8 +14,6 @@ module Spree
|
|
14
14
|
# Assign the attributes to the order and save the order
|
15
15
|
# @return true if saved, otherwise false and errors will be set on the order
|
16
16
|
def apply
|
17
|
-
order.validate_payments_attributes(@payments_attributes)
|
18
|
-
|
19
17
|
assign_order_attributes
|
20
18
|
assign_payments_attributes
|
21
19
|
|
@@ -31,7 +31,11 @@ module Spree
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def recalculate_adjustments
|
34
|
-
|
34
|
+
adjustables = [*line_items, *shipments, order]
|
35
|
+
|
36
|
+
adjustables.each do |adjustable|
|
37
|
+
Spree::ItemAdjustments.new(adjustable).update
|
38
|
+
end
|
35
39
|
end
|
36
40
|
|
37
41
|
# Updates the following Order total values:
|
@@ -63,7 +67,7 @@ module Spree
|
|
63
67
|
end
|
64
68
|
|
65
69
|
def update_shipment_total
|
66
|
-
order.shipment_total = shipments.sum(
|
70
|
+
order.shipment_total = shipments.to_a.sum(&:cost)
|
67
71
|
update_order_total
|
68
72
|
end
|
69
73
|
|
@@ -73,15 +77,14 @@ module Spree
|
|
73
77
|
|
74
78
|
def update_adjustment_total
|
75
79
|
recalculate_adjustments
|
76
|
-
order.adjustment_total = line_items.sum(:adjustment_total) +
|
77
|
-
shipments.sum(:adjustment_total) +
|
78
|
-
adjustments.eligible.sum(:amount)
|
79
|
-
order.included_tax_total = line_items.sum(:included_tax_total) + shipments.sum(:included_tax_total)
|
80
|
-
order.additional_tax_total = line_items.sum(:additional_tax_total) + shipments.sum(:additional_tax_total)
|
81
80
|
|
82
|
-
|
83
|
-
|
84
|
-
adjustments.
|
81
|
+
all_items = line_items + shipments
|
82
|
+
|
83
|
+
order.adjustment_total = all_items.sum(&:adjustment_total) + adjustments.eligible.sum(:amount)
|
84
|
+
order.included_tax_total = all_items.sum(&:included_tax_total)
|
85
|
+
order.additional_tax_total = all_items.sum(&:additional_tax_total)
|
86
|
+
|
87
|
+
order.promo_total = all_items.sum(&:promo_total) + adjustments.promotion.eligible.sum(:amount)
|
85
88
|
|
86
89
|
update_order_total
|
87
90
|
end
|
@@ -91,7 +94,7 @@ module Spree
|
|
91
94
|
end
|
92
95
|
|
93
96
|
def update_item_total
|
94
|
-
order.item_total = line_items.sum(
|
97
|
+
order.item_total = line_items.to_a.sum(&:amount)
|
95
98
|
update_order_total
|
96
99
|
end
|
97
100
|
|
data/app/models/spree/payment.rb
CHANGED
@@ -11,7 +11,7 @@ module Spree
|
|
11
11
|
|
12
12
|
belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :payments
|
13
13
|
belongs_to :source, polymorphic: true
|
14
|
-
belongs_to :payment_method, class_name: 'Spree::PaymentMethod', inverse_of: :payments
|
14
|
+
belongs_to :payment_method, -> { with_deleted }, class_name: 'Spree::PaymentMethod', inverse_of: :payments
|
15
15
|
|
16
16
|
has_many :offsets, -> { offset_payment }, class_name: "Spree::Payment", foreign_key: :source_id
|
17
17
|
has_many :log_entries, as: :source
|
@@ -39,7 +39,6 @@ module Spree
|
|
39
39
|
|
40
40
|
validates :amount, numericality: true
|
41
41
|
validates :source, presence: true, if: :source_required?
|
42
|
-
validates :payment_method, presence: true
|
43
42
|
|
44
43
|
default_scope -> { order(:created_at) }
|
45
44
|
|
@@ -156,7 +155,7 @@ module Spree
|
|
156
155
|
return unless new_record?
|
157
156
|
return if source_attributes.blank?
|
158
157
|
|
159
|
-
|
158
|
+
Spree::Deprecation.warn(<<WARN.squish)
|
160
159
|
Building payment sources by assigning source_attributes on payments is
|
161
160
|
deprecated. Instead use either the PaymentCreate class or the
|
162
161
|
OrderUpdateAttributes class.
|
@@ -2,7 +2,7 @@ module Spree
|
|
2
2
|
# Service object for creating new payments on an Order
|
3
3
|
class PaymentCreate
|
4
4
|
# @param order [Order] The order for the new payment
|
5
|
-
# @param attributes [Hash] attributes which are assigned to the new payment
|
5
|
+
# @param attributes [Hash,ActionController::Parameters] attributes which are assigned to the new payment
|
6
6
|
# * :payment_method_id Id of payment method used for this payment
|
7
7
|
# * :source_attributes Attributes used to build the source of this payment. Usually a {CreditCard}
|
8
8
|
# * :existing_card_id (Integer) The id of an existing {CreditCard} object to use
|
@@ -11,7 +11,10 @@ module Spree
|
|
11
11
|
def initialize(order, attributes, payment: nil, request_env: {})
|
12
12
|
@order = order
|
13
13
|
@payment = payment
|
14
|
-
|
14
|
+
|
15
|
+
# If AC::Params are passed in, attributes.to_h gives us a hash of only
|
16
|
+
# the permitted attributes.
|
17
|
+
@attributes = attributes.to_h.with_indifferent_access
|
15
18
|
@source_attributes = @attributes.delete(:source_attributes) || {}
|
16
19
|
@request_env = request_env
|
17
20
|
end
|
@@ -61,7 +61,7 @@ module Spree
|
|
61
61
|
handle_action(action, :void, auth_code)
|
62
62
|
end
|
63
63
|
|
64
|
-
def credit(amount_in_cents, auth_code, gateway_options)
|
64
|
+
def credit(amount_in_cents, auth_code, gateway_options = {})
|
65
65
|
action = -> (store_credit) do
|
66
66
|
currency = gateway_options[:currency] || store_credit.currency
|
67
67
|
originator = gateway_options[:originator]
|
@@ -77,13 +77,14 @@ module Spree
|
|
77
77
|
store_credit = store_credit_event.try(:store_credit)
|
78
78
|
|
79
79
|
if store_credit_event.nil? || store_credit.nil?
|
80
|
-
|
80
|
+
ActiveMerchant::Billing::Response.new(false, '', {}, {})
|
81
81
|
elsif store_credit_event.capture_action?
|
82
|
-
|
82
|
+
amount_in_cents = (store_credit_event.amount * 100).round
|
83
|
+
credit(amount_in_cents, auth_code)
|
83
84
|
elsif store_credit_event.authorization_action?
|
84
|
-
|
85
|
+
void(auth_code)
|
85
86
|
else
|
86
|
-
|
87
|
+
ActiveMerchant::Billing::Response.new(false, '', {}, {})
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
@@ -172,7 +172,8 @@ module Spree
|
|
172
172
|
end
|
173
173
|
|
174
174
|
# Can't use add_search_scope for this as it needs a default argument
|
175
|
-
def self.available(available_on = nil,
|
175
|
+
def self.available(available_on = nil, currency = nil)
|
176
|
+
Spree::Deprecation.warn("The second currency argument on Product.available has no effect, and is deprecated", caller) if currency
|
176
177
|
joins(master: :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
|
177
178
|
end
|
178
179
|
search_scopes << :available
|
data/app/models/spree/product.rb
CHANGED
@@ -132,10 +132,7 @@ module Spree
|
|
132
132
|
def ensure_option_types_exist_for_values_hash
|
133
133
|
return if option_values_hash.nil?
|
134
134
|
required_option_type_ids = option_values_hash.keys.map(&:to_i)
|
135
|
-
|
136
|
-
missing_option_type_ids.each do |id|
|
137
|
-
product_option_types.create(option_type_id: id)
|
138
|
-
end
|
135
|
+
self.option_type_ids |= required_option_type_ids
|
139
136
|
end
|
140
137
|
|
141
138
|
# Creates a new product with the same attributes, variants, etc.
|
@@ -259,7 +256,7 @@ module Spree
|
|
259
256
|
# @return [Array] all advertised and not-rejected promotions
|
260
257
|
def possible_promotions
|
261
258
|
promotion_ids = promotion_rules.map(&:promotion_id).uniq
|
262
|
-
Spree::Promotion.advertised.where(id: promotion_ids).reject(&:
|
259
|
+
Spree::Promotion.advertised.where(id: promotion_ids).reject(&:inactive?)
|
263
260
|
end
|
264
261
|
|
265
262
|
# The number of on-hand stock items; Infinity if any variant does not track
|
@@ -68,10 +68,12 @@ module Spree
|
|
68
68
|
def line_items_to_adjust(promotion, order)
|
69
69
|
excluded_ids = adjustments.
|
70
70
|
where(adjustable_id: order.line_items.pluck(:id), adjustable_type: 'Spree::LineItem').
|
71
|
-
pluck(:adjustable_id)
|
71
|
+
pluck(:adjustable_id).
|
72
|
+
to_set
|
72
73
|
|
73
|
-
order.line_items.
|
74
|
-
|
74
|
+
order.line_items.select do |line_item|
|
75
|
+
!excluded_ids.include?(line_item.id) &&
|
76
|
+
promotion.line_item_actionable?(order, line_item)
|
75
77
|
end
|
76
78
|
end
|
77
79
|
end
|
@@ -24,15 +24,14 @@ module Spree
|
|
24
24
|
product_ids.include?(pid) && (value_ids(pid) & ovids).present?
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
values =
|
27
|
+
def preferred_eligible_values
|
28
|
+
values = preferences[:eligible_values] || {}
|
29
29
|
Hash[values.keys.map(&:to_i).zip(
|
30
30
|
values.values.map do |v|
|
31
31
|
(v.is_a?(Array) ? v : v.split(",")).map(&:to_i)
|
32
32
|
end
|
33
33
|
)]
|
34
34
|
end
|
35
|
-
alias_method_chain :preferred_eligible_values, :numerification
|
36
35
|
|
37
36
|
private
|
38
37
|
|
@@ -11,6 +11,9 @@ module Spree
|
|
11
11
|
has_many :products, class_name: 'Spree::Product', through: :product_promotion_rules
|
12
12
|
|
13
13
|
MATCH_POLICIES = %w(any all none)
|
14
|
+
|
15
|
+
validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
|
16
|
+
|
14
17
|
preference :match_policy, :string, default: MATCH_POLICIES.first
|
15
18
|
|
16
19
|
# scope/association that is used to test eligibility
|
@@ -25,18 +28,21 @@ module Spree
|
|
25
28
|
def eligible?(order, _options = {})
|
26
29
|
return true if eligible_products.empty?
|
27
30
|
|
28
|
-
|
31
|
+
case preferred_match_policy
|
32
|
+
when 'all'
|
29
33
|
unless eligible_products.all? { |p| order.products.include?(p) }
|
30
34
|
eligibility_errors.add(:base, eligibility_error_message(:missing_product))
|
31
35
|
end
|
32
|
-
|
36
|
+
when 'any'
|
33
37
|
unless order.products.any? { |p| eligible_products.include?(p) }
|
34
38
|
eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products))
|
35
39
|
end
|
36
|
-
|
40
|
+
when 'none'
|
37
41
|
unless order.products.none? { |p| eligible_products.include?(p) }
|
38
42
|
eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product))
|
39
43
|
end
|
44
|
+
else
|
45
|
+
raise "unexpected match policy: #{preferred_match_policy.inspect}"
|
40
46
|
end
|
41
47
|
|
42
48
|
eligibility_errors.empty?
|
@@ -2,33 +2,52 @@ module Spree
|
|
2
2
|
class Promotion
|
3
3
|
module Rules
|
4
4
|
class Taxon < PromotionRule
|
5
|
-
has_many :promotion_rule_taxons, class_name: 'Spree::PromotionRuleTaxon', foreign_key: :promotion_rule_id
|
5
|
+
has_many :promotion_rule_taxons, class_name: 'Spree::PromotionRuleTaxon', foreign_key: :promotion_rule_id,
|
6
|
+
dependent: :destroy
|
6
7
|
has_many :taxons, through: :promotion_rule_taxons, class_name: 'Spree::Taxon'
|
7
8
|
|
8
9
|
MATCH_POLICIES = %w(any all)
|
9
|
-
|
10
|
+
|
11
|
+
validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
|
12
|
+
|
13
|
+
preference :match_policy, :string, default: MATCH_POLICIES.first
|
10
14
|
|
11
15
|
def applicable?(promotable)
|
12
16
|
promotable.is_a?(Spree::Order)
|
13
17
|
end
|
14
18
|
|
15
19
|
def eligible?(order, _options = {})
|
16
|
-
|
17
|
-
|
20
|
+
order_taxons = taxons_in_order_including_parents(order)
|
21
|
+
|
22
|
+
case preferred_match_policy
|
23
|
+
when 'all'
|
24
|
+
unless (taxons.to_a - order_taxons).empty?
|
18
25
|
eligibility_errors.add(:base, eligibility_error_message(:missing_taxon))
|
19
26
|
end
|
20
|
-
|
21
|
-
order_taxons = taxons_in_order_including_parents(order)
|
27
|
+
when 'any'
|
22
28
|
unless taxons.any?{ |taxon| order_taxons.include? taxon }
|
23
29
|
eligibility_errors.add(:base, eligibility_error_message(:no_matching_taxons))
|
24
30
|
end
|
31
|
+
else
|
32
|
+
# Change this to an exception in a future version of Solidus
|
33
|
+
warn_invalid_match_policy(assume: 'any')
|
34
|
+
unless taxons.any? { |taxon| order_taxons.include? taxon }
|
35
|
+
eligibility_errors.add(:base, eligibility_error_message(:no_matching_taxons))
|
36
|
+
end
|
25
37
|
end
|
26
38
|
|
27
39
|
eligibility_errors.empty?
|
28
40
|
end
|
29
41
|
|
30
42
|
def actionable?(line_item)
|
31
|
-
|
43
|
+
case preferred_match_policy
|
44
|
+
when 'any', 'all'
|
45
|
+
taxon_product_ids.include?(line_item.variant.product_id)
|
46
|
+
else
|
47
|
+
# Change this to an exception in a future version of Solidus
|
48
|
+
warn_invalid_match_policy(assume: 'any')
|
49
|
+
taxon_product_ids.include?(line_item.variant.product_id)
|
50
|
+
end
|
32
51
|
end
|
33
52
|
|
34
53
|
def taxon_ids_string
|
@@ -42,6 +61,13 @@ module Spree
|
|
42
61
|
|
43
62
|
private
|
44
63
|
|
64
|
+
def warn_invalid_match_policy(assume:)
|
65
|
+
Spree::Deprecation.warn(
|
66
|
+
"#{self.class.name} id=#{id} has unexpected match policy #{preferred_match_policy.inspect}. "\
|
67
|
+
"Interpreting it as '#{assume}'."
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
45
71
|
# All taxons in an order
|
46
72
|
def order_taxons(order)
|
47
73
|
Spree::Taxon.joins(products: { variants_including_master: :line_items }).where(spree_line_items: { order_id: order.id }).distinct
|
@@ -2,10 +2,9 @@ module Spree
|
|
2
2
|
class Promotion
|
3
3
|
module Rules
|
4
4
|
class User < PromotionRule
|
5
|
-
belongs_to :user, class_name: Spree::UserClassHandle.new
|
6
|
-
|
7
5
|
has_many :promotion_rule_users, class_name: 'Spree::PromotionRuleUser',
|
8
|
-
foreign_key: :promotion_rule_id
|
6
|
+
foreign_key: :promotion_rule_id,
|
7
|
+
dependent: :destroy
|
9
8
|
has_many :users, through: :promotion_rule_users, class_name: Spree::UserClassHandle.new
|
10
9
|
|
11
10
|
def applicable?(promotable)
|
@@ -73,15 +73,24 @@ module Spree
|
|
73
73
|
super
|
74
74
|
end
|
75
75
|
|
76
|
-
def expired?
|
77
|
-
!active?
|
78
|
-
end
|
79
|
-
|
80
76
|
def active?
|
81
77
|
(starts_at.nil? || starts_at < Time.current) &&
|
82
78
|
(expires_at.nil? || expires_at > Time.current)
|
83
79
|
end
|
84
80
|
|
81
|
+
def inactive?
|
82
|
+
!active?
|
83
|
+
end
|
84
|
+
|
85
|
+
def expired?
|
86
|
+
Spree::Deprecation.warn <<-WARN.squish, caller
|
87
|
+
#expired? is deprecated, and will be removed in Solidus 2.0.
|
88
|
+
Please use #inactive? instead.
|
89
|
+
WARN
|
90
|
+
|
91
|
+
inactive?
|
92
|
+
end
|
93
|
+
|
85
94
|
def activate(order:, line_item: nil, user: nil, path: nil, promotion_code: nil)
|
86
95
|
return unless self.class.order_activatable?(order)
|
87
96
|
|
@@ -116,7 +125,7 @@ module Spree
|
|
116
125
|
|
117
126
|
# called anytime order.update! happens
|
118
127
|
def eligible?(promotable, promotion_code: nil)
|
119
|
-
return false if
|
128
|
+
return false if inactive?
|
120
129
|
return false if usage_limit_exceeded?
|
121
130
|
return false if promotion_code && promotion_code.usage_limit_exceeded?
|
122
131
|
return false if blacklisted?(promotable)
|
@@ -176,8 +185,9 @@ module Spree
|
|
176
185
|
count(:order_id)
|
177
186
|
end
|
178
187
|
|
179
|
-
# TODO: specs
|
180
188
|
def line_item_actionable?(order, line_item, promotion_code: nil)
|
189
|
+
return false if blacklisted?(line_item)
|
190
|
+
|
181
191
|
if eligible?(order, promotion_code: promotion_code)
|
182
192
|
rules = eligible_rules(order)
|
183
193
|
if rules.blank?
|
@@ -12,7 +12,7 @@ module Spree
|
|
12
12
|
if order.coupon_code.present?
|
13
13
|
if promotion.present? && promotion.actions.exists?
|
14
14
|
handle_present_promotion(promotion)
|
15
|
-
elsif promotion_code && promotion_code.promotion.
|
15
|
+
elsif promotion_code && promotion_code.promotion.inactive?
|
16
16
|
set_error_code :coupon_code_expired
|
17
17
|
else
|
18
18
|
set_error_code :coupon_code_not_found
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Spree
|
2
2
|
class Shipment < Spree::Base
|
3
3
|
belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :shipments
|
4
|
-
belongs_to :address, class_name: 'Spree::Address'
|
5
4
|
belongs_to :stock_location, class_name: 'Spree::StockLocation'
|
6
5
|
|
7
6
|
has_many :adjustments, as: :adjustable, inverse_of: :adjustable, dependent: :delete_all
|
@@ -11,8 +10,6 @@ module Spree
|
|
11
10
|
has_many :state_changes, as: :stateful
|
12
11
|
has_many :cartons, -> { uniq }, through: :inventory_units
|
13
12
|
|
14
|
-
after_save :update_adjustments
|
15
|
-
|
16
13
|
before_validation :set_cost_zero_when_nil
|
17
14
|
|
18
15
|
before_destroy :ensure_can_destroy
|
@@ -21,7 +18,6 @@ module Spree
|
|
21
18
|
# from outside of the state machine and can actually pass variables through.
|
22
19
|
attr_accessor :special_instructions, :suppress_mailer
|
23
20
|
|
24
|
-
accepts_nested_attributes_for :address
|
25
21
|
accepts_nested_attributes_for :inventory_units
|
26
22
|
|
27
23
|
make_permalink field: :number, length: 11, prefix: 'H'
|
@@ -210,8 +206,13 @@ module Spree
|
|
210
206
|
end
|
211
207
|
|
212
208
|
def selected_shipping_rate_id=(id)
|
213
|
-
|
214
|
-
shipping_rates.
|
209
|
+
selected_shipping_rate.update(selected: false) if selected_shipping_rate
|
210
|
+
new_rate = shipping_rates.detect { |rate| rate.id == id.to_i }
|
211
|
+
fail(
|
212
|
+
ArgumentError,
|
213
|
+
"Could not find shipping rate id #{id} for shipment #{number}"
|
214
|
+
) unless new_rate
|
215
|
+
new_rate.update(selected: true)
|
215
216
|
save!
|
216
217
|
end
|
217
218
|
|
@@ -372,6 +373,11 @@ module Spree
|
|
372
373
|
!stock_location || stock_location.fulfillable?
|
373
374
|
end
|
374
375
|
|
376
|
+
def address
|
377
|
+
Spree::Deprecation.warn("Calling Shipment#address is deprecated. Use Order#ship_address instead", caller)
|
378
|
+
order.ship_address if order
|
379
|
+
end
|
380
|
+
|
375
381
|
private
|
376
382
|
|
377
383
|
def after_ship
|
@@ -396,20 +402,10 @@ module Spree
|
|
396
402
|
stock_location.unstock item.variant, item.quantity, self
|
397
403
|
end
|
398
404
|
|
399
|
-
def recalculate_adjustments
|
400
|
-
Spree::ItemAdjustments.new(self).update
|
401
|
-
end
|
402
|
-
|
403
405
|
def set_cost_zero_when_nil
|
404
406
|
self.cost = 0 unless cost
|
405
407
|
end
|
406
408
|
|
407
|
-
def update_adjustments
|
408
|
-
if cost_changed? && state != 'shipped'
|
409
|
-
recalculate_adjustments
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
409
|
def ensure_can_destroy
|
414
410
|
unless pending?
|
415
411
|
errors.add(:state, :cannot_destroy, state: state)
|
@@ -45,7 +45,7 @@ module Spree
|
|
45
45
|
|
46
46
|
def shipping_methods(package)
|
47
47
|
package.shipping_methods
|
48
|
-
.available_for_address(package.shipment.
|
48
|
+
.available_for_address(package.shipment.order.ship_address)
|
49
49
|
.includes(:calculator, tax_category: :tax_rates)
|
50
50
|
.to_a
|
51
51
|
.select do |ship_method|
|
@@ -8,7 +8,7 @@ module Spree
|
|
8
8
|
|
9
9
|
validates :stock_location, :variant, presence: true
|
10
10
|
validates :variant_id, uniqueness: { scope: [:stock_location_id, :deleted_at] }, allow_blank: true, unless: :deleted_at
|
11
|
-
validates :count_on_hand, numericality: { greater_than_or_equal_to: 0 },
|
11
|
+
validates :count_on_hand, numericality: { greater_than_or_equal_to: 0 }, unless: :backorderable?
|
12
12
|
|
13
13
|
delegate :weight, :should_track_inventory?, to: :variant
|
14
14
|
|
@@ -83,10 +83,6 @@ module Spree
|
|
83
83
|
|
84
84
|
private
|
85
85
|
|
86
|
-
def verify_count_on_hand?
|
87
|
-
count_on_hand_changed? && !backorderable? && (count_on_hand < count_on_hand_was) && (count_on_hand < 0)
|
88
|
-
end
|
89
|
-
|
90
86
|
def count_on_hand=(value)
|
91
87
|
write_attribute(:count_on_hand, value)
|
92
88
|
end
|
@@ -114,7 +110,7 @@ module Spree
|
|
114
110
|
def inventory_cache_threshold
|
115
111
|
# only warn if store is setting binary_inventory_cache (default = false)
|
116
112
|
@cache_threshold ||= if Spree::Config.binary_inventory_cache
|
117
|
-
|
113
|
+
Spree::Deprecation.warn "Spree::Config.binary_inventory_cache=true is DEPRECATED. Instead use Spree::Config.inventory_cache_threshold=1"
|
118
114
|
1
|
119
115
|
else
|
120
116
|
Spree::Config.inventory_cache_threshold
|