spree_core 2.3.13 → 2.4.0.rc1
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/spree.js.coffee.erb +1 -5
- data/app/helpers/spree/base_helper.rb +22 -11
- data/app/helpers/spree/products_helper.rb +8 -7
- data/app/mailers/spree/base_mailer.rb +1 -0
- data/app/mailers/spree/reimbursement_mailer.rb +10 -0
- data/app/mailers/spree/test_mailer.rb +2 -3
- data/app/models/concerns/spree/adjustment_source.rb +24 -0
- data/app/models/concerns/spree/calculated_adjustments.rb +33 -0
- data/app/models/concerns/spree/named_type.rb +12 -0
- data/app/models/concerns/spree/user_address.rb +30 -0
- data/app/models/concerns/spree/user_payment_source.rb +19 -0
- data/app/models/spree/address.rb +13 -6
- data/app/models/spree/adjustment.rb +5 -5
- data/app/models/spree/app_configuration.rb +8 -4
- data/app/models/spree/asset.rb +1 -1
- data/app/models/spree/base.rb +0 -3
- data/app/models/spree/calculator/flat_rate.rb +1 -5
- data/app/models/spree/calculator/returns/default_refund_amount.rb +36 -0
- data/app/models/spree/classification.rb +1 -1
- data/app/models/spree/credit_card.rb +18 -22
- data/app/models/spree/customer_return.rb +70 -0
- data/app/models/spree/exchange.rb +42 -0
- data/app/models/spree/gateway/bogus.rb +3 -3
- data/app/models/spree/image.rb +1 -1
- data/app/models/spree/inventory_unit.rb +32 -8
- data/app/models/spree/item_adjustments.rb +7 -11
- data/app/models/spree/legacy_user.rb +2 -2
- data/app/models/spree/line_item.rb +25 -12
- data/app/models/spree/option_type.rb +1 -1
- data/app/models/spree/option_value.rb +1 -8
- data/app/models/spree/order.rb +163 -145
- data/app/models/spree/order/checkout.rb +35 -23
- data/app/models/spree/order/payments.rb +66 -0
- data/app/models/spree/order_contents.rb +34 -24
- data/app/models/spree/order_populator.rb +6 -4
- data/app/models/spree/order_updater.rb +10 -1
- data/app/models/spree/payment.rb +19 -16
- data/app/models/spree/payment/processing.rb +40 -72
- data/app/models/spree/payment_method.rb +1 -1
- data/app/models/spree/payment_method/check.rb +0 -2
- data/app/models/spree/preference.rb +1 -1
- data/app/models/spree/preferences/preferable.rb +20 -0
- data/app/models/spree/price.rb +13 -3
- data/app/models/spree/product.rb +24 -29
- data/app/models/spree/product_property.rb +0 -2
- data/app/models/spree/promotion.rb +66 -24
- data/app/models/spree/promotion/actions/create_adjustment.rb +2 -2
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +15 -11
- data/app/models/spree/promotion/actions/create_line_items.rb +2 -12
- data/app/models/spree/promotion/rules/first_order.rb +6 -2
- data/app/models/spree/promotion/rules/item_total.rb +42 -4
- data/app/models/spree/promotion/rules/one_use_per_user.rb +24 -0
- data/app/models/spree/promotion/rules/product.rb +13 -11
- data/app/models/spree/promotion/rules/taxon.rb +61 -0
- data/app/models/spree/promotion/rules/user.rb +1 -1
- data/app/models/spree/promotion/rules/user_logged_in.rb +4 -1
- data/app/models/spree/promotion_category.rb +6 -0
- data/app/models/spree/promotion_handler/cart.rb +14 -18
- data/app/models/spree/promotion_handler/coupon.rb +25 -16
- data/app/models/spree/promotion_rule.rb +13 -0
- data/app/models/spree/property.rb +1 -3
- data/app/models/spree/refund.rb +91 -0
- data/app/models/spree/refund_reason.rb +13 -0
- data/app/models/spree/reimbursement.rb +148 -0
- data/app/models/spree/reimbursement/credit.rb +25 -0
- data/app/models/spree/reimbursement/reimbursement_type_engine.rb +56 -0
- data/app/models/spree/reimbursement/reimbursement_type_validator.rb +12 -0
- data/app/models/spree/reimbursement_performer.rb +43 -0
- data/app/models/spree/reimbursement_tax_calculator.rb +38 -0
- data/app/models/spree/reimbursement_type.rb +16 -0
- data/app/models/spree/reimbursement_type/credit.rb +13 -0
- data/app/models/spree/reimbursement_type/exchange.rb +9 -0
- data/app/models/spree/reimbursement_type/original_payment.rb +13 -0
- data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +50 -0
- data/app/models/spree/return_authorization.rb +52 -68
- data/app/models/spree/return_authorization_reason.rb +7 -0
- data/app/models/spree/return_item.rb +230 -0
- data/app/models/spree/return_item/default_eligibility_validator.rb +27 -0
- data/app/models/spree/return_item/eligibility_validator/base_validator.rb +24 -0
- data/app/models/spree/return_item/eligibility_validator/rma_required.rb +17 -0
- data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +16 -0
- data/app/models/spree/return_item/exchange_variant_eligibility/same_option_value.rb +34 -0
- data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +10 -0
- data/app/models/spree/returns_calculator.rb +8 -0
- data/app/models/spree/shipment.rb +209 -154
- data/app/models/spree/shipment_handler.rb +43 -0
- data/app/models/spree/shipping_calculator.rb +1 -1
- data/app/models/spree/shipping_category.rb +2 -2
- data/app/models/spree/shipping_method.rb +1 -1
- data/app/models/spree/shipping_method_category.rb +1 -1
- data/app/models/spree/shipping_rate.rb +4 -0
- data/app/models/spree/stock/adjuster.rb +10 -11
- data/app/models/spree/stock/availability_validator.rb +6 -10
- data/app/models/spree/stock/content_item.rb +48 -0
- data/app/models/spree/stock/coordinator.rb +14 -7
- data/app/models/spree/stock/estimator.rb +1 -1
- data/app/models/spree/stock/inventory_unit_builder.rb +21 -0
- data/app/models/spree/stock/package.rb +38 -51
- data/app/models/spree/stock/packer.rb +13 -11
- data/app/models/spree/stock/prioritizer.rb +7 -7
- data/app/models/spree/stock/splitter/base.rb +2 -2
- data/app/models/spree/stock_item.rb +6 -9
- data/app/models/spree/stock_location.rb +17 -25
- data/app/models/spree/stock_movement.rb +1 -8
- data/app/models/spree/stock_transfer.rb +0 -2
- data/app/models/spree/store.rb +1 -1
- data/app/models/spree/tax_category.rb +2 -2
- data/app/models/spree/tax_rate.rb +16 -22
- data/app/models/spree/taxon.rb +1 -1
- data/app/models/spree/variant.rb +45 -23
- data/app/models/spree/zone.rb +17 -17
- data/app/models/spree/zone_member.rb +1 -1
- data/app/views/layouts/spree/base_mailer.html.erb +784 -0
- data/app/views/spree/order_mailer/cancel_email.html.erb +45 -0
- data/app/views/spree/order_mailer/cancel_email.text.erb +2 -2
- data/app/views/spree/order_mailer/confirm_email.html.erb +84 -0
- data/app/views/spree/order_mailer/confirm_email.text.erb +2 -2
- data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +22 -0
- data/app/views/spree/shared/_base_mailer_footer.html.erb +20 -0
- data/app/views/spree/shared/_base_mailer_header.html.erb +31 -0
- data/app/views/spree/shipment_mailer/shipped_email.html.erb +34 -0
- data/app/views/spree/test_mailer/test_email.html.erb +40 -0
- data/app/views/spree/test_mailer/test_email.text.erb +2 -2
- data/config/initializers/friendly_id.rb +88 -0
- data/config/initializers/premailer_assets.rb +1 -0
- data/config/initializers/user_class_extensions.rb +9 -22
- data/config/locales/en.yml +180 -12
- data/db/default/spree/states.rb +73 -55
- data/db/migrate/20130213191427_create_default_stock.rb +1 -0
- data/db/migrate/20130807024301_upgrade_adjustments.rb +4 -5
- data/db/migrate/20140309033438_create_store_from_preferences.rb +0 -7
- data/db/migrate/20140318191500_create_spree_taxons_promotion_rules.rb +8 -0
- data/db/migrate/20140530024945_move_order_token_from_tokenized_permission.rb +1 -1
- data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +5 -3
- data/db/migrate/20140625214618_create_spree_refunds.rb +12 -0
- data/db/migrate/20140702140656_create_spree_return_authorization_inventory_unit.rb +12 -0
- data/db/migrate/20140707125621_rename_return_authorization_inventory_unit_to_return_items.rb +5 -0
- data/db/migrate/20140709160534_backfill_line_item_pre_tax_amount.rb +10 -0
- data/db/migrate/20140710041921_recreate_spree_return_authorizations.rb +55 -0
- data/db/migrate/20140710181204_add_amount_fields_to_return_items.rb +7 -0
- data/db/migrate/20140710190048_drop_return_authorization_amount.rb +5 -0
- data/db/migrate/20140713140455_create_spree_return_authorization_reasons.rb +28 -0
- data/db/migrate/20140713140527_create_spree_refund_reasons.rb +14 -0
- data/db/migrate/20140713142214_rename_return_authorization_reason.rb +5 -0
- data/db/migrate/20140715182625_create_spree_promotion_categories.rb +11 -0
- data/db/migrate/20140716204111_drop_received_at_on_return_items.rb +9 -0
- data/db/migrate/20140716212330_add_reception_and_acceptance_status_to_return_items.rb +6 -0
- data/db/migrate/20140717155155_create_default_refund_reason.rb +9 -0
- data/db/migrate/20140717185932_add_default_to_spree_stock_locations.rb +5 -0
- data/db/migrate/20140718133010_create_spree_customer_returns.rb +9 -0
- data/db/migrate/20140718133349_add_customer_return_id_to_return_item.rb +6 -0
- data/db/migrate/20140718195325_create_friendly_id_slugs.rb +15 -0
- data/db/migrate/20140723004419_rename_spree_refund_return_authorization_id.rb +5 -0
- data/db/migrate/20140723152808_increase_return_item_pre_tax_amount_precision.rb +13 -0
- data/db/migrate/20140723214541_copy_product_slugs_to_slug_history.rb +15 -0
- data/db/migrate/20140725131539_create_spree_reimbursements.rb +21 -0
- data/db/migrate/20140728225422_add_promotionable_to_spree_products.rb +5 -0
- data/db/migrate/20140729133613_add_exchange_inventory_unit_foreign_keys.rb +7 -0
- data/db/migrate/20140730155938_add_acceptance_status_errors_to_return_item.rb +5 -0
- data/db/migrate/20140731150017_create_spree_reimbursement_types.rb +20 -0
- data/db/migrate/20140805171035_add_default_to_spree_credit_cards.rb +5 -0
- data/db/migrate/20140805171219_make_existing_credit_cards_default.rb +10 -0
- data/db/migrate/20140806144901_add_type_to_reimbursement_type.rb +9 -0
- data/db/migrate/20140808184039_create_spree_reimbursement_credits.rb +10 -0
- data/db/migrate/20140827170513_add_meta_title_to_spree_products.rb +7 -0
- data/db/migrate/20140924164824_add_code_to_spree_tax_categories.rb +5 -0
- data/db/migrate/20141002191113_add_code_to_spree_shipping_methods.rb +5 -0
- data/db/migrate/20141007230328_add_cancel_audit_fields_to_spree_orders.rb +6 -0
- data/db/migrate/20141009204607_add_store_id_to_orders.rb +8 -0
- data/lib/generators/spree/install/install_generator.rb +7 -3
- data/lib/spree/core.rb +11 -10
- data/lib/spree/core/controller_helpers/common.rb +3 -10
- data/lib/spree/core/controller_helpers/order.rb +15 -12
- data/lib/spree/core/controller_helpers/strong_parameters.rb +9 -9
- data/lib/spree/core/engine.rb +8 -1
- data/lib/spree/core/importer/order.rb +5 -17
- data/lib/spree/core/search/base.rb +1 -1
- data/lib/spree/core/validators/email.rb +1 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/instrumentation.rb +41 -0
- data/lib/spree/migrations.rb +3 -7
- data/lib/spree/money.rb +2 -2
- data/lib/spree/permitted_attributes.rb +10 -9
- data/lib/spree/testing_support/ability_helpers.rb +25 -25
- data/lib/spree/testing_support/authorization_helpers.rb +3 -5
- data/lib/spree/testing_support/capybara_ext.rb +2 -2
- data/lib/spree/testing_support/factories/calculator_factory.rb +0 -8
- data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
- data/lib/spree/testing_support/factories/customer_return_factory.rb +31 -0
- data/lib/spree/testing_support/factories/inventory_unit_factory.rb +1 -0
- data/lib/spree/testing_support/factories/line_item_factory.rb +2 -1
- data/lib/spree/testing_support/factories/order_factory.rb +11 -6
- data/lib/spree/testing_support/factories/promotion_category_factory.rb +6 -0
- data/lib/spree/testing_support/factories/promotion_factory.rb +9 -7
- data/lib/spree/testing_support/factories/refund_factory.rb +14 -0
- data/lib/spree/testing_support/factories/reimbursement_factory.rb +16 -0
- data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +7 -0
- data/lib/spree/testing_support/factories/return_authorization_factory.rb +9 -3
- data/lib/spree/testing_support/factories/return_item_factory.rb +10 -0
- data/lib/spree/testing_support/factories/shipment_factory.rb +1 -0
- data/lib/spree/testing_support/factories/shipping_method_factory.rb +3 -2
- data/lib/spree/testing_support/factories/stock_factory.rb +12 -11
- data/lib/spree/testing_support/flash.rb +2 -2
- data/lib/tasks/email.rake +7 -0
- data/lib/tasks/exchanges.rake +70 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_et.js +23 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_eu.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_hr.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_ka.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_ko.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_my.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_BR.js +26 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_PT.js +26 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_sl.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_sv.js +23 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_uk.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_zh.js +25 -0
- data/vendor/assets/javascripts/jquery.validate/localization/messages_zh_TW.js +26 -0
- metadata +163 -47
- data/app/models/concerns/spree/ransackable_attributes.rb +0 -19
- data/db/migrate/20141021194502_add_state_lock_version_to_order.rb +0 -5
- data/db/migrate/20141101231208_fix_adjustment_order_presence.rb +0 -13
- data/db/migrate/20141105213646_update_classifications_positions.rb +0 -9
- data/db/migrate/20141120135441_add_guest_token_index_to_spree_orders.rb +0 -5
- data/db/migrate/20150515211137_fix_adjustment_order_id.rb +0 -70
- data/lib/spree/core/adjustment_source.rb +0 -26
- data/lib/spree/core/calculated_adjustments.rb +0 -35
- data/lib/spree/core/controller_helpers.rb +0 -20
- data/lib/spree/core/user_address.rb +0 -32
- data/lib/spree/core/user_payment_source.rb +0 -20
- data/lib/spree/localized_number.rb +0 -20
|
@@ -2,37 +2,20 @@ module Spree
|
|
|
2
2
|
class Payment < Spree::Base
|
|
3
3
|
module Processing
|
|
4
4
|
def process!
|
|
5
|
-
if payment_method && payment_method.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if payment_method.auto_capture?
|
|
10
|
-
purchase!
|
|
11
|
-
else
|
|
12
|
-
authorize!
|
|
13
|
-
end
|
|
14
|
-
else
|
|
15
|
-
invalidate!
|
|
16
|
-
raise Core::GatewayError.new(Spree.t(:payment_method_not_supported))
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
else
|
|
20
|
-
raise Core::GatewayError.new(Spree.t(:payment_processing_failed))
|
|
21
|
-
end
|
|
5
|
+
if payment_method && payment_method.auto_capture?
|
|
6
|
+
purchase!
|
|
7
|
+
else
|
|
8
|
+
authorize!
|
|
22
9
|
end
|
|
23
10
|
end
|
|
24
11
|
|
|
25
12
|
def authorize!
|
|
26
|
-
|
|
27
|
-
gateway_action(source, :authorize, :pend)
|
|
13
|
+
handle_payment_preconditions { process_authorization }
|
|
28
14
|
end
|
|
29
15
|
|
|
30
16
|
# Captures the entire amount of a payment.
|
|
31
17
|
def purchase!
|
|
32
|
-
|
|
33
|
-
result = gateway_action(source, :purchase, :complete)
|
|
34
|
-
# This won't be called if gateway_action raises a GatewayError
|
|
35
|
-
capture_events.create!(amount: amount)
|
|
18
|
+
handle_payment_preconditions { process_purchase }
|
|
36
19
|
end
|
|
37
20
|
|
|
38
21
|
# Takes the amount in cents to capture.
|
|
@@ -80,56 +63,8 @@ module Spree
|
|
|
80
63
|
end
|
|
81
64
|
end
|
|
82
65
|
|
|
83
|
-
def credit!(credit_amount=nil)
|
|
84
|
-
raise Core::GatewayError.new(Spree.t(:payment_processing_failed)) if processing?
|
|
85
|
-
|
|
86
|
-
# Calculate credit amount before marking as processing since it messes up the order totals not having payment in completed state.
|
|
87
|
-
credit_amount ||= credit_allowed >= order.outstanding_balance.abs ? order.outstanding_balance.abs : credit_allowed.abs
|
|
88
|
-
credit_amount = credit_amount.to_f
|
|
89
|
-
|
|
90
|
-
# Mark as processing to avoid race condition that could send multiple credits to the gateway.
|
|
91
|
-
started_processing!
|
|
92
|
-
protect_from_connection_error do
|
|
93
|
-
check_environment
|
|
94
|
-
|
|
95
|
-
credit_cents = Spree::Money.new(credit_amount, currency: currency).money.cents
|
|
96
|
-
|
|
97
|
-
if payment_method.payment_profiles_supported?
|
|
98
|
-
response = payment_method.credit(credit_cents, source, response_code, gateway_options)
|
|
99
|
-
else
|
|
100
|
-
response = payment_method.credit(credit_cents, response_code, gateway_options)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
record_response(response)
|
|
104
|
-
# Always set back to 'completed' as initial payment record was successful.
|
|
105
|
-
self.update_column(:state, 'completed')
|
|
106
|
-
|
|
107
|
-
if response.success?
|
|
108
|
-
self.class.create!(
|
|
109
|
-
:order => order,
|
|
110
|
-
:source => self,
|
|
111
|
-
:payment_method => payment_method,
|
|
112
|
-
:amount => credit_amount.abs * -1,
|
|
113
|
-
:response_code => response.authorization,
|
|
114
|
-
:state => 'completed'
|
|
115
|
-
)
|
|
116
|
-
else
|
|
117
|
-
gateway_error(response)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
66
|
def cancel!
|
|
123
|
-
|
|
124
|
-
payment_method.cancel(response_code)
|
|
125
|
-
else
|
|
126
|
-
credit!(credit_allowed.abs)
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def partial_credit(amount)
|
|
131
|
-
return if amount > credit_allowed
|
|
132
|
-
credit!(amount)
|
|
67
|
+
payment_method.cancel(response_code)
|
|
133
68
|
end
|
|
134
69
|
|
|
135
70
|
def gateway_options
|
|
@@ -158,6 +93,39 @@ module Spree
|
|
|
158
93
|
|
|
159
94
|
private
|
|
160
95
|
|
|
96
|
+
def process_authorization
|
|
97
|
+
started_processing!
|
|
98
|
+
gateway_action(source, :authorize, :pend)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def process_purchase
|
|
102
|
+
started_processing!
|
|
103
|
+
result = gateway_action(source, :purchase, :complete)
|
|
104
|
+
# This won't be called if gateway_action raises a GatewayError
|
|
105
|
+
capture_events.create!(amount: amount)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def handle_payment_preconditions(&block)
|
|
109
|
+
unless block_given?
|
|
110
|
+
raise ArgumentError.new("handle_payment_preconditions must be called with a block")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
if payment_method && payment_method.source_required?
|
|
114
|
+
if source
|
|
115
|
+
if !processing?
|
|
116
|
+
if payment_method.supports?(source) || token_based?
|
|
117
|
+
yield
|
|
118
|
+
else
|
|
119
|
+
invalidate!
|
|
120
|
+
raise Core::GatewayError.new(Spree.t(:payment_method_not_supported))
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
else
|
|
124
|
+
raise Core::GatewayError.new(Spree.t(:payment_processing_failed))
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
161
129
|
def gateway_action(source, action, success_state)
|
|
162
130
|
protect_from_connection_error do
|
|
163
131
|
check_environment
|
|
@@ -8,7 +8,7 @@ module Spree
|
|
|
8
8
|
|
|
9
9
|
validates :name, presence: true
|
|
10
10
|
|
|
11
|
-
has_many :payments, class_name: "Spree::Payment"
|
|
11
|
+
has_many :payments, class_name: "Spree::Payment"
|
|
12
12
|
has_many :credit_cards, class_name: "Spree::CreditCard"
|
|
13
13
|
|
|
14
14
|
def self.providers
|
|
@@ -106,6 +106,26 @@ module Spree::Preferences::Preferable
|
|
|
106
106
|
else
|
|
107
107
|
true
|
|
108
108
|
end
|
|
109
|
+
when :array
|
|
110
|
+
value.is_a?(Array) ? value : Array.wrap(value)
|
|
111
|
+
when :hash
|
|
112
|
+
case value.class.to_s
|
|
113
|
+
when "Hash"
|
|
114
|
+
value
|
|
115
|
+
when "String"
|
|
116
|
+
# only works with hashes whose keys are strings
|
|
117
|
+
JSON.parse value.gsub('=>', ':')
|
|
118
|
+
when "Array"
|
|
119
|
+
begin
|
|
120
|
+
value.try(:to_h)
|
|
121
|
+
rescue TypeError
|
|
122
|
+
Hash[*value]
|
|
123
|
+
rescue ArgumentError
|
|
124
|
+
raise 'An even count is required when passing an array to be converted to a hash'
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
value.class.ancestors.include?(Hash) ? value : {}
|
|
128
|
+
end
|
|
109
129
|
else
|
|
110
130
|
value
|
|
111
131
|
end
|
data/app/models/spree/price.rb
CHANGED
|
@@ -12,8 +12,6 @@ module Spree
|
|
|
12
12
|
end
|
|
13
13
|
alias :display_price :display_amount
|
|
14
14
|
|
|
15
|
-
self.whitelisted_ransackable_attributes = ['amount']
|
|
16
|
-
|
|
17
15
|
def money
|
|
18
16
|
Spree::Money.new(amount || 0, { currency: currency })
|
|
19
17
|
end
|
|
@@ -23,7 +21,7 @@ module Spree
|
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
def price=(price)
|
|
26
|
-
self[:amount] =
|
|
24
|
+
self[:amount] = parse_price(price)
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
# Remove variant default_scope `deleted_at: nil`
|
|
@@ -41,6 +39,18 @@ module Spree
|
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
41
|
|
|
42
|
+
# strips all non-price-like characters from the price, taking into account locale settings
|
|
43
|
+
def parse_price(price)
|
|
44
|
+
return price unless price.is_a?(String)
|
|
45
|
+
|
|
46
|
+
separator, delimiter = I18n.t([:'number.currency.format.separator', :'number.currency.format.delimiter'])
|
|
47
|
+
non_price_characters = /[^0-9\-#{separator}]/
|
|
48
|
+
price.gsub!(non_price_characters, '') # strip everything else first
|
|
49
|
+
price.gsub!(separator, '.') unless separator == '.' # then replace the locale-specific decimal separator with the standard separator if necessary
|
|
50
|
+
|
|
51
|
+
price.to_d
|
|
52
|
+
end
|
|
53
|
+
|
|
44
54
|
def maximum_amount
|
|
45
55
|
BigDecimal '999999.99'
|
|
46
56
|
end
|
data/app/models/spree/product.rb
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
module Spree
|
|
22
22
|
class Product < Spree::Base
|
|
23
23
|
extend FriendlyId
|
|
24
|
-
friendly_id :slug_candidates, use: :
|
|
24
|
+
friendly_id :slug_candidates, use: :history
|
|
25
25
|
|
|
26
26
|
acts_as_paranoid
|
|
27
27
|
|
|
@@ -40,7 +40,8 @@ module Spree
|
|
|
40
40
|
has_one :master,
|
|
41
41
|
-> { where is_master: true },
|
|
42
42
|
inverse_of: :product,
|
|
43
|
-
class_name: 'Spree::Variant'
|
|
43
|
+
class_name: 'Spree::Variant',
|
|
44
|
+
dependent: :destroy
|
|
44
45
|
|
|
45
46
|
has_many :variants,
|
|
46
47
|
-> { where(is_master: false).order("#{::Spree::Variant.quoted_table_name}.position ASC") },
|
|
@@ -56,7 +57,7 @@ module Spree
|
|
|
56
57
|
has_many :prices, -> { order('spree_variants.position, spree_variants.id, currency') }, through: :variants
|
|
57
58
|
|
|
58
59
|
has_many :stock_items, through: :variants_including_master
|
|
59
|
-
|
|
60
|
+
|
|
60
61
|
has_many :line_items, through: :variants_including_master
|
|
61
62
|
has_many :orders, through: :line_items
|
|
62
63
|
|
|
@@ -64,32 +65,29 @@ module Spree
|
|
|
64
65
|
|
|
65
66
|
delegate_belongs_to :master, :cost_price
|
|
66
67
|
|
|
67
|
-
delegate :images, to: :master, prefix: true
|
|
68
|
-
alias_method :images, :master_images
|
|
69
|
-
|
|
70
|
-
has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
|
|
71
|
-
|
|
72
68
|
after_create :set_master_variant_defaults
|
|
73
69
|
after_create :add_properties_and_option_types_from_prototype
|
|
74
70
|
after_create :build_variants_from_option_values_hash, if: :option_values_hash
|
|
75
71
|
|
|
76
|
-
after_destroy :punch_slug
|
|
77
|
-
|
|
78
|
-
after_initialize :ensure_master
|
|
79
|
-
|
|
80
72
|
after_save :save_master
|
|
81
73
|
after_save :run_touch_callbacks, if: :anything_changed?
|
|
82
74
|
after_save :reset_nested_changes
|
|
83
75
|
after_touch :touch_taxons
|
|
84
76
|
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
delegate :images, to: :master, prefix: true
|
|
78
|
+
alias_method :images, :master_images
|
|
79
|
+
|
|
80
|
+
has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
|
|
87
81
|
|
|
88
|
-
validates :meta_keywords, length: { maximum: 255 }
|
|
89
82
|
validates :name, presence: true
|
|
90
83
|
validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
|
|
91
84
|
validates :shipping_category_id, presence: true
|
|
92
|
-
validates :slug, length: { minimum: 3 }
|
|
85
|
+
validates :slug, length: { minimum: 3 }
|
|
86
|
+
validates :slug, uniqueness: true
|
|
87
|
+
|
|
88
|
+
before_validation :normalize_slug, on: :update
|
|
89
|
+
|
|
90
|
+
after_destroy :punch_slug
|
|
93
91
|
|
|
94
92
|
attr_accessor :option_values_hash
|
|
95
93
|
|
|
@@ -97,8 +95,7 @@ module Spree
|
|
|
97
95
|
|
|
98
96
|
alias :options :product_option_types
|
|
99
97
|
|
|
100
|
-
|
|
101
|
-
self.whitelisted_ransackable_attributes = %w[slug]
|
|
98
|
+
after_initialize :ensure_master
|
|
102
99
|
|
|
103
100
|
# the master variant is not a member of the variants array
|
|
104
101
|
def has_variants?
|
|
@@ -279,21 +276,19 @@ module Spree
|
|
|
279
276
|
# when saving so we force a save using a hook
|
|
280
277
|
# Fix for issue #5306
|
|
281
278
|
def save_master
|
|
282
|
-
|
|
283
|
-
master.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
279
|
+
begin
|
|
280
|
+
if master && (master.changed? || master.new_record? || (master.default_price && (master.default_price.changed? || master.default_price.new_record?)))
|
|
281
|
+
master.save!
|
|
282
|
+
@nested_changes = true
|
|
283
|
+
end
|
|
287
284
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
# We call master.default_price here to ensure price is initialized.
|
|
292
|
-
# Required to avoid Variant#check_price validation failing on create.
|
|
293
|
-
unless master.default_price && master.valid?
|
|
285
|
+
# If the master cannot be saved, the Product object will get its errors
|
|
286
|
+
# and will be destroyed
|
|
287
|
+
rescue ActiveRecord::RecordInvalid
|
|
294
288
|
master.errors.each do |att, error|
|
|
295
289
|
self.errors.add(att, error)
|
|
296
290
|
end
|
|
291
|
+
raise
|
|
297
292
|
end
|
|
298
293
|
end
|
|
299
294
|
|
|
@@ -3,6 +3,10 @@ module Spree
|
|
|
3
3
|
MATCH_POLICIES = %w(all any)
|
|
4
4
|
UNACTIVATABLE_ORDER_STATES = ["complete", "awaiting_return", "returned"]
|
|
5
5
|
|
|
6
|
+
attr_reader :eligibility_errors
|
|
7
|
+
|
|
8
|
+
belongs_to :promotion_category
|
|
9
|
+
|
|
6
10
|
has_many :promotion_rules, autosave: true, dependent: :destroy
|
|
7
11
|
alias_method :rules, :promotion_rules
|
|
8
12
|
|
|
@@ -16,23 +20,24 @@ module Spree
|
|
|
16
20
|
validates_associated :rules
|
|
17
21
|
|
|
18
22
|
validates :name, presence: true
|
|
19
|
-
validates :path, uniqueness:
|
|
23
|
+
validates :path, uniqueness: true, allow_blank: true
|
|
20
24
|
validates :usage_limit, numericality: { greater_than: 0, allow_nil: true }
|
|
21
25
|
validates :description, length: { maximum: 255 }
|
|
22
26
|
|
|
23
27
|
before_save :normalize_blank_values
|
|
24
28
|
|
|
25
29
|
scope :coupons, ->{ where("#{table_name}.code IS NOT NULL") }
|
|
26
|
-
scope :applied, -> { joins(:orders).uniq }
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
order_join_table = reflect_on_association(:orders).join_table
|
|
32
|
+
|
|
33
|
+
scope :applied, -> { joins("INNER JOIN #{order_join_table} ON #{order_join_table}.promotion_id = #{table_name}.id").uniq }
|
|
29
34
|
|
|
30
35
|
def self.advertised
|
|
31
36
|
where(advertise: true)
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
def self.with_coupon_code(coupon_code)
|
|
35
|
-
where("lower(
|
|
40
|
+
where("lower(code) = ?", coupon_code.strip.downcase).first
|
|
36
41
|
end
|
|
37
42
|
|
|
38
43
|
def self.active
|
|
@@ -52,6 +57,8 @@ module Spree
|
|
|
52
57
|
order = payload[:order]
|
|
53
58
|
return unless self.class.order_activatable?(order)
|
|
54
59
|
|
|
60
|
+
payload[:promotion] = self
|
|
61
|
+
|
|
55
62
|
# Track results from actions to see if any action has been taken.
|
|
56
63
|
# Actions should return nil/false if no action has been taken.
|
|
57
64
|
# If an action returns true, then an action has been taken.
|
|
@@ -73,36 +80,39 @@ module Spree
|
|
|
73
80
|
|
|
74
81
|
# called anytime order.update! happens
|
|
75
82
|
def eligible?(promotable)
|
|
76
|
-
return false if expired? || usage_limit_exceeded?(promotable)
|
|
77
|
-
|
|
83
|
+
return false if expired? || usage_limit_exceeded?(promotable) || blacklisted?(promotable)
|
|
84
|
+
!!eligible_rules(promotable, {})
|
|
78
85
|
end
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
# eligible_rules returns an array of promotion rules where eligible? is true for the promotable
|
|
88
|
+
# if there are no such rules, an empty array is returned
|
|
89
|
+
# if the rules make this promotable ineligible, then nil is returned (i.e. this promotable is not eligible)
|
|
90
|
+
def eligible_rules(promotable, options = {})
|
|
81
91
|
# Promotions without rules are eligible by default.
|
|
82
|
-
return
|
|
92
|
+
return [] if rules.none?
|
|
83
93
|
eligible = lambda { |r| r.eligible?(promotable, options) }
|
|
84
94
|
specific_rules = rules.for(promotable)
|
|
85
|
-
return
|
|
86
|
-
|
|
95
|
+
return [] if specific_rules.none?
|
|
96
|
+
|
|
97
|
+
if match_all?
|
|
87
98
|
# If there are rules for this promotion, but no rules for this
|
|
88
99
|
# particular promotable, then the promotion is ineligible by default.
|
|
89
|
-
|
|
100
|
+
unless specific_rules.all?(&eligible)
|
|
101
|
+
@eligibility_errors = specific_rules.map(&:eligibility_errors).detect(&:present?)
|
|
102
|
+
return nil
|
|
103
|
+
end
|
|
104
|
+
specific_rules
|
|
90
105
|
else
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
106
|
+
unless specific_rules.any?(&eligible)
|
|
107
|
+
@eligibility_errors = specific_rules.map(&:eligibility_errors).detect(&:present?)
|
|
108
|
+
return nil
|
|
109
|
+
end
|
|
110
|
+
specific_rules.select(&eligible)
|
|
94
111
|
end
|
|
95
112
|
end
|
|
96
113
|
|
|
97
|
-
# Products assigned to all product rules
|
|
98
114
|
def products
|
|
99
|
-
|
|
100
|
-
rule.respond_to?(:products) ? products << rule.products : products
|
|
101
|
-
end.flatten.uniq
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def product_ids
|
|
105
|
-
products.map(&:id)
|
|
115
|
+
rules.where(type: "Spree::Promotion::Rules::Product").map(&:products).flatten.uniq
|
|
106
116
|
end
|
|
107
117
|
|
|
108
118
|
def usage_limit_exceeded?(promotable)
|
|
@@ -110,8 +120,7 @@ module Spree
|
|
|
110
120
|
end
|
|
111
121
|
|
|
112
122
|
def adjusted_credits_count(promotable)
|
|
113
|
-
|
|
114
|
-
credits_count - adjustments.promotion.where(:source_id => actions.pluck(:id)).count
|
|
123
|
+
credits_count - promotable.adjustments.promotion.where(:source_id => actions.pluck(:id)).count
|
|
115
124
|
end
|
|
116
125
|
|
|
117
126
|
def credits
|
|
@@ -122,11 +131,44 @@ module Spree
|
|
|
122
131
|
credits.count
|
|
123
132
|
end
|
|
124
133
|
|
|
134
|
+
def line_item_actionable?(order, line_item)
|
|
135
|
+
if eligible? order
|
|
136
|
+
rules = eligible_rules(order)
|
|
137
|
+
if rules.blank?
|
|
138
|
+
true
|
|
139
|
+
else
|
|
140
|
+
rules.send(match_all? ? :all? : :any?) do |rule|
|
|
141
|
+
rule.actionable? line_item
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
false
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def used_by?(user, excluded_orders = [])
|
|
150
|
+
orders.where.not(id: excluded_orders.map(&:id)).complete.where(user_id: user.id).exists?
|
|
151
|
+
end
|
|
152
|
+
|
|
125
153
|
private
|
|
154
|
+
def blacklisted?(promotable)
|
|
155
|
+
case promotable
|
|
156
|
+
when Spree::LineItem
|
|
157
|
+
!promotable.product.promotionable?
|
|
158
|
+
when Spree::Order
|
|
159
|
+
promotable.line_items.any? &&
|
|
160
|
+
!promotable.line_items.joins(:product).where(spree_products: {promotionable: true}).any?
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
126
164
|
def normalize_blank_values
|
|
127
165
|
[:code, :path].each do |column|
|
|
128
166
|
self[column] = nil if self[column].blank?
|
|
129
167
|
end
|
|
130
168
|
end
|
|
169
|
+
|
|
170
|
+
def match_all?
|
|
171
|
+
match_policy == 'all'
|
|
172
|
+
end
|
|
131
173
|
end
|
|
132
174
|
end
|