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
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class ReturnItem::DefaultEligibilityValidator < Spree::ReturnItem::EligibilityValidator::BaseValidator
|
|
3
|
+
class_attribute :permitted_eligibility_validators
|
|
4
|
+
self.permitted_eligibility_validators = [
|
|
5
|
+
ReturnItem::EligibilityValidator::TimeSincePurchase,
|
|
6
|
+
ReturnItem::EligibilityValidator::RMARequired,
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
def eligible_for_return?
|
|
10
|
+
validators.all? {|v| v.eligible_for_return? }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def requires_manual_intervention?
|
|
14
|
+
validators.any? {|v| v.requires_manual_intervention? }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def errors
|
|
18
|
+
validators.map(&:errors).reduce({}, :merge)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def validators
|
|
24
|
+
@validators ||= permitted_eligibility_validators.map{|v| v.new(@return_item) }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class Spree::ReturnItem::EligibilityValidator::BaseValidator
|
|
3
|
+
attr_reader :errors
|
|
4
|
+
|
|
5
|
+
def initialize(return_item)
|
|
6
|
+
@return_item = return_item
|
|
7
|
+
@errors = {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def eligible_for_return?
|
|
11
|
+
raise 'Implement me'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def requires_manual_intervention?
|
|
15
|
+
raise 'Implement me'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def add_error(key, error)
|
|
21
|
+
@errors[key] = error
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class ReturnItem::EligibilityValidator::RMARequired < Spree::ReturnItem::EligibilityValidator::BaseValidator
|
|
3
|
+
def eligible_for_return?
|
|
4
|
+
if @return_item.return_authorization.present?
|
|
5
|
+
return true
|
|
6
|
+
else
|
|
7
|
+
add_error(:rma_required, Spree.t('return_item_rma_ineligible'))
|
|
8
|
+
return false
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def requires_manual_intervention?
|
|
13
|
+
false
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class ReturnItem::EligibilityValidator::TimeSincePurchase < Spree::ReturnItem::EligibilityValidator::BaseValidator
|
|
3
|
+
def eligible_for_return?
|
|
4
|
+
if (@return_item.inventory_unit.created_at + Spree::Config[:return_eligibility_number_of_days].days) > Time.now
|
|
5
|
+
return true
|
|
6
|
+
else
|
|
7
|
+
add_error(:number_of_days, Spree.t('return_item_time_period_ineligible'))
|
|
8
|
+
return false
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def requires_manual_intervention?
|
|
13
|
+
false
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module ReturnItem::ExchangeVariantEligibility
|
|
3
|
+
class SameOptionValue
|
|
4
|
+
class_attribute :option_type_restrictions
|
|
5
|
+
self.option_type_restrictions = []
|
|
6
|
+
# This can be configured in an initializer, e.g.:
|
|
7
|
+
# Spree::ReturnItem::ExchangeVariantEligibility::SameOptionValue.option_type_restrictions = ["size", "color"]
|
|
8
|
+
#
|
|
9
|
+
# This restriction causes only variants that share the same option value for the
|
|
10
|
+
# specified option types to be returned. e.g.:
|
|
11
|
+
#
|
|
12
|
+
# option_type_restrictions = ["color", "waist"]
|
|
13
|
+
# Variant: blue pants with 32 waist and 30 inseam
|
|
14
|
+
#
|
|
15
|
+
# can be exchanged for:
|
|
16
|
+
# blue pants with 32 waist and 31 inseam
|
|
17
|
+
#
|
|
18
|
+
# cannot be exchanged for:
|
|
19
|
+
# green pants with 32 waist and 30 inseam
|
|
20
|
+
# blue pants with 34 waist and 32 inseam
|
|
21
|
+
|
|
22
|
+
def self.eligible_variants(variant)
|
|
23
|
+
product_variants = SameProduct.eligible_variants(variant).includes(option_values: :option_type)
|
|
24
|
+
|
|
25
|
+
relevant_option_values = variant.option_values.select { |ov| option_type_restrictions.include? ov.option_type.name }
|
|
26
|
+
if relevant_option_values.present?
|
|
27
|
+
product_variants.select { |v| (relevant_option_values & v.option_values) == relevant_option_values }
|
|
28
|
+
else
|
|
29
|
+
product_variants
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -2,15 +2,15 @@ require 'ostruct'
|
|
|
2
2
|
|
|
3
3
|
module Spree
|
|
4
4
|
class Shipment < Spree::Base
|
|
5
|
-
belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :shipments
|
|
6
5
|
belongs_to :address, class_name: 'Spree::Address', inverse_of: :shipments
|
|
6
|
+
belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :shipments
|
|
7
7
|
belongs_to :stock_location, class_name: 'Spree::StockLocation'
|
|
8
8
|
|
|
9
|
+
has_many :adjustments, as: :adjustable, dependent: :delete_all
|
|
10
|
+
has_many :inventory_units, dependent: :delete_all, inverse_of: :shipment
|
|
9
11
|
has_many :shipping_rates, -> { order('cost ASC') }, dependent: :delete_all
|
|
10
12
|
has_many :shipping_methods, through: :shipping_rates
|
|
11
13
|
has_many :state_changes, as: :stateful
|
|
12
|
-
has_many :inventory_units, dependent: :delete_all, inverse_of: :shipment
|
|
13
|
-
has_many :adjustments, as: :adjustable, dependent: :delete_all
|
|
14
14
|
|
|
15
15
|
after_save :update_adjustments
|
|
16
16
|
|
|
@@ -23,11 +23,13 @@ module Spree
|
|
|
23
23
|
|
|
24
24
|
make_permalink field: :number, length: 11, prefix: 'H'
|
|
25
25
|
|
|
26
|
-
scope :shipped, -> { with_state('shipped') }
|
|
27
|
-
scope :ready, -> { with_state('ready') }
|
|
28
26
|
scope :pending, -> { with_state('pending') }
|
|
29
|
-
scope :
|
|
27
|
+
scope :ready, -> { with_state('ready') }
|
|
28
|
+
scope :shipped, -> { with_state('shipped') }
|
|
30
29
|
scope :trackable, -> { where("tracking IS NOT NULL AND tracking != ''") }
|
|
30
|
+
scope :with_state, ->(*s) { where(state: s) }
|
|
31
|
+
# sort by most recent shipped_at, falling back to created_at. add "id desc" to make specs that involve this scope more deterministic.
|
|
32
|
+
scope :reverse_chronological, -> { order('coalesce(spree_shipments.shipped_at, spree_shipments.created_at) desc', id: :desc) }
|
|
31
33
|
|
|
32
34
|
# shipment state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
|
|
33
35
|
state_machine initial: :pending, use_transactions: false do
|
|
@@ -43,7 +45,7 @@ module Spree
|
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
event :ship do
|
|
46
|
-
transition from: :ready, to: :shipped
|
|
48
|
+
transition from: [:ready, :canceled], to: :shipped
|
|
47
49
|
end
|
|
48
50
|
after_transition to: :shipped, do: :after_ship
|
|
49
51
|
|
|
@@ -61,7 +63,7 @@ module Spree
|
|
|
61
63
|
}
|
|
62
64
|
transition from: :canceled, to: :pending
|
|
63
65
|
end
|
|
64
|
-
after_transition from: :canceled, to: [:pending, :ready], do: :after_resume
|
|
66
|
+
after_transition from: :canceled, to: [:pending, :ready, :shipped], do: :after_resume
|
|
65
67
|
|
|
66
68
|
after_transition do |shipment, transition|
|
|
67
69
|
shipment.state_changes.create!(
|
|
@@ -72,113 +74,96 @@ module Spree
|
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def backordered?
|
|
80
|
-
inventory_units.any? { |inventory_unit| inventory_unit.backordered? }
|
|
77
|
+
def add_shipping_method(shipping_method, selected = false)
|
|
78
|
+
shipping_rates.create(shipping_method: shipping_method, selected: selected, cost: cost)
|
|
81
79
|
end
|
|
82
80
|
|
|
83
|
-
def
|
|
84
|
-
|
|
81
|
+
def after_cancel
|
|
82
|
+
manifest.each { |item| manifest_restock(item) }
|
|
85
83
|
end
|
|
86
84
|
|
|
87
|
-
def
|
|
88
|
-
|
|
89
|
-
self.shipped_at = Time.now
|
|
85
|
+
def after_resume
|
|
86
|
+
manifest.each { |item| manifest_unstock(item) }
|
|
90
87
|
end
|
|
91
88
|
|
|
92
|
-
def
|
|
93
|
-
|
|
89
|
+
def backordered?
|
|
90
|
+
inventory_units.any? { |inventory_unit| inventory_unit.backordered? }
|
|
94
91
|
end
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def add_shipping_method(shipping_method, selected = false)
|
|
99
|
-
shipping_rates.create(shipping_method: shipping_method, selected: selected, cost: cost)
|
|
93
|
+
def currency
|
|
94
|
+
order ? order.currency : Spree::Config[:currency]
|
|
100
95
|
end
|
|
101
96
|
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
# Determines the appropriate +state+ according to the following logic:
|
|
98
|
+
#
|
|
99
|
+
# pending unless order is complete and +order.payment_state+ is +paid+
|
|
100
|
+
# shipped if already shipped (ie. does not change the state)
|
|
101
|
+
# ready all other cases
|
|
102
|
+
def determine_state(order)
|
|
103
|
+
return 'canceled' if order.canceled?
|
|
104
|
+
return 'pending' unless order.can_ship?
|
|
105
|
+
return 'pending' if inventory_units.any? &:backordered?
|
|
106
|
+
return 'shipped' if state == 'shipped'
|
|
107
|
+
order.paid? || Spree::Config[:auto_capture_on_dispatch] ? 'ready' : 'pending'
|
|
104
108
|
end
|
|
105
109
|
|
|
106
|
-
def
|
|
107
|
-
|
|
110
|
+
def discounted_cost
|
|
111
|
+
cost + promo_total
|
|
108
112
|
end
|
|
113
|
+
alias discounted_amount discounted_cost
|
|
109
114
|
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
shipping_rates.update(id, selected: true)
|
|
113
|
-
self.save!
|
|
115
|
+
def display_cost
|
|
116
|
+
Spree::Money.new(cost, { currency: currency })
|
|
114
117
|
end
|
|
118
|
+
alias display_amount display_cost
|
|
115
119
|
|
|
116
|
-
def
|
|
117
|
-
|
|
120
|
+
def display_discounted_cost
|
|
121
|
+
Spree::Money.new(discounted_cost, { currency: currency })
|
|
118
122
|
end
|
|
119
123
|
|
|
120
|
-
def
|
|
121
|
-
|
|
122
|
-
return [] unless can_get_rates?
|
|
123
|
-
|
|
124
|
-
# StockEstimator.new assigment below will replace the current shipping_method
|
|
125
|
-
original_shipping_method_id = shipping_method.try(:id)
|
|
126
|
-
|
|
127
|
-
self.shipping_rates = Stock::Estimator.new(order).shipping_rates(to_package)
|
|
128
|
-
|
|
129
|
-
if shipping_method
|
|
130
|
-
selected_rate = shipping_rates.detect { |rate|
|
|
131
|
-
rate.shipping_method_id == original_shipping_method_id
|
|
132
|
-
}
|
|
133
|
-
self.selected_shipping_rate_id = selected_rate.id if selected_rate
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
shipping_rates
|
|
124
|
+
def display_final_price
|
|
125
|
+
Spree::Money.new(final_price, { currency: currency })
|
|
137
126
|
end
|
|
138
127
|
|
|
139
|
-
def
|
|
140
|
-
|
|
128
|
+
def display_item_cost
|
|
129
|
+
Spree::Money.new(item_cost, { currency: currency })
|
|
141
130
|
end
|
|
142
131
|
|
|
143
|
-
def
|
|
144
|
-
|
|
132
|
+
def editable_by?(user)
|
|
133
|
+
!shipped?
|
|
145
134
|
end
|
|
146
|
-
alias display_amount display_cost
|
|
147
135
|
|
|
148
|
-
def
|
|
149
|
-
|
|
136
|
+
def final_price
|
|
137
|
+
cost + adjustment_total
|
|
150
138
|
end
|
|
151
139
|
|
|
152
|
-
def
|
|
153
|
-
|
|
140
|
+
def final_price_with_items
|
|
141
|
+
item_cost + final_price
|
|
154
142
|
end
|
|
155
|
-
alias discounted_amount discounted_cost
|
|
156
143
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def tax_total
|
|
161
|
-
included_tax_total + additional_tax_total
|
|
144
|
+
def finalize!
|
|
145
|
+
InventoryUnit.finalize_units!(inventory_units)
|
|
146
|
+
manifest.each { |item| manifest_unstock(item) }
|
|
162
147
|
end
|
|
163
148
|
|
|
164
|
-
def
|
|
165
|
-
|
|
149
|
+
def include?(variant)
|
|
150
|
+
inventory_units_for(variant).present?
|
|
166
151
|
end
|
|
167
152
|
|
|
168
|
-
def
|
|
169
|
-
|
|
153
|
+
def inventory_units_for(variant)
|
|
154
|
+
inventory_units.where(variant_id: variant.id)
|
|
170
155
|
end
|
|
171
156
|
|
|
172
|
-
def
|
|
173
|
-
|
|
157
|
+
def inventory_units_for_item(line_item, variant = nil)
|
|
158
|
+
inventory_units.where(line_item_id: line_item.id, variant_id: line_item.variant.id || variant.id)
|
|
174
159
|
end
|
|
175
160
|
|
|
176
|
-
def
|
|
177
|
-
|
|
161
|
+
def item_cost
|
|
162
|
+
line_items.map(&:amount).sum
|
|
178
163
|
end
|
|
179
164
|
|
|
180
|
-
def
|
|
181
|
-
|
|
165
|
+
def line_items
|
|
166
|
+
inventory_units.includes(:line_item).map(&:line_item).uniq
|
|
182
167
|
end
|
|
183
168
|
|
|
184
169
|
ManifestItem = Struct.new(:line_item, :variant, :quantity, :states)
|
|
@@ -199,84 +184,119 @@ module Spree
|
|
|
199
184
|
end.flatten
|
|
200
185
|
end
|
|
201
186
|
|
|
202
|
-
def
|
|
203
|
-
|
|
187
|
+
def process_order_payments
|
|
188
|
+
pending_payments = order.pending_payments
|
|
189
|
+
.sort_by(&:uncaptured_amount).reverse
|
|
190
|
+
|
|
191
|
+
# NOTE Do we really need to force orders to have pending payments on dispatch?
|
|
192
|
+
if pending_payments.empty?
|
|
193
|
+
raise Spree::Core::GatewayError, Spree.t(:no_pending_payments)
|
|
194
|
+
else
|
|
195
|
+
shipment_to_pay = final_price_with_items
|
|
196
|
+
payments_amount = 0
|
|
197
|
+
|
|
198
|
+
payments_pool = pending_payments.each_with_object([]) do |payment, pool|
|
|
199
|
+
next if payments_amount >= shipment_to_pay
|
|
200
|
+
payments_amount += payment.uncaptured_amount
|
|
201
|
+
pool << payment
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
payments_pool.each do |payment|
|
|
205
|
+
capturable_amount = if payment.amount >= shipment_to_pay
|
|
206
|
+
shipment_to_pay
|
|
207
|
+
else
|
|
208
|
+
payment.amount
|
|
209
|
+
end
|
|
210
|
+
cents = (capturable_amount * 100).to_i
|
|
211
|
+
payment.capture!(cents)
|
|
212
|
+
shipment_to_pay -= capturable_amount
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
rescue Spree::Core::GatewayError => e
|
|
216
|
+
errors.add(:base, e.message)
|
|
217
|
+
return !!Spree::Config[:allow_checkout_on_gateway_error]
|
|
204
218
|
end
|
|
205
219
|
|
|
206
|
-
def
|
|
207
|
-
|
|
208
|
-
manifest.each { |item| manifest_unstock(item) }
|
|
220
|
+
def ready_or_pending?
|
|
221
|
+
self.ready? || self.pending?
|
|
209
222
|
end
|
|
210
223
|
|
|
211
|
-
def
|
|
212
|
-
|
|
224
|
+
def refresh_rates
|
|
225
|
+
return shipping_rates if shipped?
|
|
226
|
+
return [] unless can_get_rates?
|
|
227
|
+
|
|
228
|
+
# StockEstimator.new assigment below will replace the current shipping_method
|
|
229
|
+
original_shipping_method_id = shipping_method.try(:id)
|
|
230
|
+
|
|
231
|
+
self.shipping_rates = Stock::Estimator.new(order).shipping_rates(to_package)
|
|
232
|
+
|
|
233
|
+
if shipping_method
|
|
234
|
+
selected_rate = shipping_rates.detect { |rate|
|
|
235
|
+
rate.shipping_method_id == original_shipping_method_id
|
|
236
|
+
}
|
|
237
|
+
self.selected_shipping_rate_id = selected_rate.id if selected_rate
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
shipping_rates
|
|
213
241
|
end
|
|
214
242
|
|
|
215
|
-
def
|
|
216
|
-
|
|
243
|
+
def selected_shipping_rate
|
|
244
|
+
shipping_rates.where(selected: true).first
|
|
217
245
|
end
|
|
218
246
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
# yield the correct results.
|
|
222
|
-
def update!(order)
|
|
223
|
-
old_state = state
|
|
224
|
-
new_state = determine_state(order)
|
|
225
|
-
update_columns(
|
|
226
|
-
state: new_state,
|
|
227
|
-
updated_at: Time.now,
|
|
228
|
-
)
|
|
229
|
-
after_ship if new_state == 'shipped' and old_state != 'shipped'
|
|
247
|
+
def selected_shipping_rate_id
|
|
248
|
+
selected_shipping_rate.try(:id)
|
|
230
249
|
end
|
|
231
250
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
# ready all other cases
|
|
237
|
-
def determine_state(order)
|
|
238
|
-
return 'canceled' if order.canceled?
|
|
239
|
-
return 'pending' unless order.can_ship?
|
|
240
|
-
return 'pending' if inventory_units.any? &:backordered?
|
|
241
|
-
return 'shipped' if state == 'shipped'
|
|
242
|
-
order.paid? ? 'ready' : 'pending'
|
|
251
|
+
def selected_shipping_rate_id=(id)
|
|
252
|
+
shipping_rates.update_all(selected: false)
|
|
253
|
+
shipping_rates.update(id, selected: true)
|
|
254
|
+
self.save!
|
|
243
255
|
end
|
|
244
256
|
|
|
245
|
-
def
|
|
246
|
-
|
|
257
|
+
def set_up_inventory(state, variant, order, line_item)
|
|
258
|
+
self.inventory_units.create(
|
|
259
|
+
state: state,
|
|
260
|
+
variant_id: variant.id,
|
|
261
|
+
order_id: order.id,
|
|
262
|
+
line_item_id: line_item.id
|
|
263
|
+
)
|
|
247
264
|
end
|
|
248
265
|
|
|
249
|
-
def
|
|
250
|
-
|
|
266
|
+
def shipped=(value)
|
|
267
|
+
return unless value == '1' && shipped_at.nil?
|
|
268
|
+
self.shipped_at = Time.now
|
|
251
269
|
end
|
|
252
270
|
|
|
253
|
-
def
|
|
254
|
-
|
|
271
|
+
def shipping_method
|
|
272
|
+
selected_shipping_rate.try(:shipping_method) || shipping_rates.first.try(:shipping_method)
|
|
255
273
|
end
|
|
256
274
|
|
|
257
|
-
def
|
|
258
|
-
|
|
275
|
+
def tax_category
|
|
276
|
+
selected_shipping_rate.try(:tax_rate).try(:tax_category)
|
|
259
277
|
end
|
|
260
278
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
279
|
+
# Only one of either included_tax_total or additional_tax_total is set
|
|
280
|
+
# This method returns the total of the two. Saves having to check if
|
|
281
|
+
# tax is included or additional.
|
|
282
|
+
def tax_total
|
|
283
|
+
included_tax_total + additional_tax_total
|
|
284
|
+
end
|
|
266
285
|
|
|
267
|
-
|
|
268
|
-
|
|
286
|
+
def to_package
|
|
287
|
+
package = Stock::Package.new(stock_location)
|
|
288
|
+
inventory_units.group_by(&:state).each do |state, state_inventory_units|
|
|
289
|
+
package.add_multiple state_inventory_units, state.to_sym
|
|
269
290
|
end
|
|
270
291
|
package
|
|
271
292
|
end
|
|
272
293
|
|
|
273
|
-
def
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
)
|
|
294
|
+
def to_param
|
|
295
|
+
number
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def tracking_url
|
|
299
|
+
@tracking_url ||= shipping_method.build_tracking_url(tracking)
|
|
280
300
|
end
|
|
281
301
|
|
|
282
302
|
def update_amounts
|
|
@@ -319,10 +339,63 @@ module Spree
|
|
|
319
339
|
end
|
|
320
340
|
end
|
|
321
341
|
|
|
342
|
+
# Updates various aspects of the Shipment while bypassing any callbacks. Note that this method takes an explicit reference to the
|
|
343
|
+
# Order object. This is necessary because the association actually has a stale (and unsaved) copy of the Order and so it will not
|
|
344
|
+
# yield the correct results.
|
|
345
|
+
def update!(order)
|
|
346
|
+
old_state = state
|
|
347
|
+
new_state = determine_state(order)
|
|
348
|
+
update_columns(
|
|
349
|
+
state: new_state,
|
|
350
|
+
updated_at: Time.now,
|
|
351
|
+
)
|
|
352
|
+
after_ship if new_state == 'shipped' and old_state != 'shipped'
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def transfer_to_location(variant, quantity, stock_location)
|
|
356
|
+
if quantity <= 0
|
|
357
|
+
raise ArgumentError
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
transaction do
|
|
361
|
+
new_shipment = order.shipments.create!(stock_location: stock_location)
|
|
362
|
+
|
|
363
|
+
order.contents.remove(variant, quantity, {shipment: self})
|
|
364
|
+
order.contents.add(variant, quantity, {shipment: new_shipment})
|
|
365
|
+
|
|
366
|
+
refresh_rates
|
|
367
|
+
save!
|
|
368
|
+
new_shipment.save!
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def transfer_to_shipment(variant, quantity, shipment_to_transfer_to)
|
|
373
|
+
quantity_already_shipment_to_transfer_to = shipment_to_transfer_to.manifest.find{|mi| mi.line_item.variant == variant}.try(:quantity) || 0
|
|
374
|
+
final_quantity = quantity + quantity_already_shipment_to_transfer_to
|
|
375
|
+
|
|
376
|
+
if (quantity <= 0 || self == shipment_to_transfer_to)
|
|
377
|
+
raise ArgumentError
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
transaction do
|
|
381
|
+
order.contents.remove(variant, quantity, {shipment: self})
|
|
382
|
+
order.contents.add(variant, quantity, {shipment: shipment_to_transfer_to})
|
|
383
|
+
|
|
384
|
+
refresh_rates
|
|
385
|
+
save!
|
|
386
|
+
shipment_to_transfer_to.refresh_rates
|
|
387
|
+
shipment_to_transfer_to.save!
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
322
391
|
private
|
|
323
392
|
|
|
324
|
-
def
|
|
325
|
-
|
|
393
|
+
def after_ship
|
|
394
|
+
ShipmentHandler.factory(self).perform
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def can_get_rates?
|
|
398
|
+
order.ship_address && order.ship_address.valid?
|
|
326
399
|
end
|
|
327
400
|
|
|
328
401
|
def manifest_restock(item)
|
|
@@ -335,23 +408,12 @@ module Spree
|
|
|
335
408
|
end
|
|
336
409
|
end
|
|
337
410
|
|
|
338
|
-
def
|
|
339
|
-
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
def after_ship
|
|
343
|
-
inventory_units.each &:ship!
|
|
344
|
-
send_shipped_email
|
|
345
|
-
touch :shipped_at
|
|
346
|
-
update_order_shipment_state
|
|
411
|
+
def manifest_unstock(item)
|
|
412
|
+
stock_location.unstock item.variant, item.quantity, self
|
|
347
413
|
end
|
|
348
414
|
|
|
349
|
-
def
|
|
350
|
-
|
|
351
|
-
order.update_columns(
|
|
352
|
-
shipment_state: new_state,
|
|
353
|
-
updated_at: Time.now,
|
|
354
|
-
)
|
|
415
|
+
def recalculate_adjustments
|
|
416
|
+
Spree::ItemAdjustments.new(self).update
|
|
355
417
|
end
|
|
356
418
|
|
|
357
419
|
def send_shipped_email
|
|
@@ -368,12 +430,5 @@ module Spree
|
|
|
368
430
|
end
|
|
369
431
|
end
|
|
370
432
|
|
|
371
|
-
def recalculate_adjustments
|
|
372
|
-
Spree::ItemAdjustments.new(self).update
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
def can_get_rates?
|
|
376
|
-
order.ship_address && order.ship_address.valid?
|
|
377
|
-
end
|
|
378
433
|
end
|
|
379
434
|
end
|