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.
Files changed (232) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree.js.coffee.erb +1 -5
  3. data/app/helpers/spree/base_helper.rb +22 -11
  4. data/app/helpers/spree/products_helper.rb +8 -7
  5. data/app/mailers/spree/base_mailer.rb +1 -0
  6. data/app/mailers/spree/reimbursement_mailer.rb +10 -0
  7. data/app/mailers/spree/test_mailer.rb +2 -3
  8. data/app/models/concerns/spree/adjustment_source.rb +24 -0
  9. data/app/models/concerns/spree/calculated_adjustments.rb +33 -0
  10. data/app/models/concerns/spree/named_type.rb +12 -0
  11. data/app/models/concerns/spree/user_address.rb +30 -0
  12. data/app/models/concerns/spree/user_payment_source.rb +19 -0
  13. data/app/models/spree/address.rb +13 -6
  14. data/app/models/spree/adjustment.rb +5 -5
  15. data/app/models/spree/app_configuration.rb +8 -4
  16. data/app/models/spree/asset.rb +1 -1
  17. data/app/models/spree/base.rb +0 -3
  18. data/app/models/spree/calculator/flat_rate.rb +1 -5
  19. data/app/models/spree/calculator/returns/default_refund_amount.rb +36 -0
  20. data/app/models/spree/classification.rb +1 -1
  21. data/app/models/spree/credit_card.rb +18 -22
  22. data/app/models/spree/customer_return.rb +70 -0
  23. data/app/models/spree/exchange.rb +42 -0
  24. data/app/models/spree/gateway/bogus.rb +3 -3
  25. data/app/models/spree/image.rb +1 -1
  26. data/app/models/spree/inventory_unit.rb +32 -8
  27. data/app/models/spree/item_adjustments.rb +7 -11
  28. data/app/models/spree/legacy_user.rb +2 -2
  29. data/app/models/spree/line_item.rb +25 -12
  30. data/app/models/spree/option_type.rb +1 -1
  31. data/app/models/spree/option_value.rb +1 -8
  32. data/app/models/spree/order.rb +163 -145
  33. data/app/models/spree/order/checkout.rb +35 -23
  34. data/app/models/spree/order/payments.rb +66 -0
  35. data/app/models/spree/order_contents.rb +34 -24
  36. data/app/models/spree/order_populator.rb +6 -4
  37. data/app/models/spree/order_updater.rb +10 -1
  38. data/app/models/spree/payment.rb +19 -16
  39. data/app/models/spree/payment/processing.rb +40 -72
  40. data/app/models/spree/payment_method.rb +1 -1
  41. data/app/models/spree/payment_method/check.rb +0 -2
  42. data/app/models/spree/preference.rb +1 -1
  43. data/app/models/spree/preferences/preferable.rb +20 -0
  44. data/app/models/spree/price.rb +13 -3
  45. data/app/models/spree/product.rb +24 -29
  46. data/app/models/spree/product_property.rb +0 -2
  47. data/app/models/spree/promotion.rb +66 -24
  48. data/app/models/spree/promotion/actions/create_adjustment.rb +2 -2
  49. data/app/models/spree/promotion/actions/create_item_adjustments.rb +15 -11
  50. data/app/models/spree/promotion/actions/create_line_items.rb +2 -12
  51. data/app/models/spree/promotion/rules/first_order.rb +6 -2
  52. data/app/models/spree/promotion/rules/item_total.rb +42 -4
  53. data/app/models/spree/promotion/rules/one_use_per_user.rb +24 -0
  54. data/app/models/spree/promotion/rules/product.rb +13 -11
  55. data/app/models/spree/promotion/rules/taxon.rb +61 -0
  56. data/app/models/spree/promotion/rules/user.rb +1 -1
  57. data/app/models/spree/promotion/rules/user_logged_in.rb +4 -1
  58. data/app/models/spree/promotion_category.rb +6 -0
  59. data/app/models/spree/promotion_handler/cart.rb +14 -18
  60. data/app/models/spree/promotion_handler/coupon.rb +25 -16
  61. data/app/models/spree/promotion_rule.rb +13 -0
  62. data/app/models/spree/property.rb +1 -3
  63. data/app/models/spree/refund.rb +91 -0
  64. data/app/models/spree/refund_reason.rb +13 -0
  65. data/app/models/spree/reimbursement.rb +148 -0
  66. data/app/models/spree/reimbursement/credit.rb +25 -0
  67. data/app/models/spree/reimbursement/reimbursement_type_engine.rb +56 -0
  68. data/app/models/spree/reimbursement/reimbursement_type_validator.rb +12 -0
  69. data/app/models/spree/reimbursement_performer.rb +43 -0
  70. data/app/models/spree/reimbursement_tax_calculator.rb +38 -0
  71. data/app/models/spree/reimbursement_type.rb +16 -0
  72. data/app/models/spree/reimbursement_type/credit.rb +13 -0
  73. data/app/models/spree/reimbursement_type/exchange.rb +9 -0
  74. data/app/models/spree/reimbursement_type/original_payment.rb +13 -0
  75. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +50 -0
  76. data/app/models/spree/return_authorization.rb +52 -68
  77. data/app/models/spree/return_authorization_reason.rb +7 -0
  78. data/app/models/spree/return_item.rb +230 -0
  79. data/app/models/spree/return_item/default_eligibility_validator.rb +27 -0
  80. data/app/models/spree/return_item/eligibility_validator/base_validator.rb +24 -0
  81. data/app/models/spree/return_item/eligibility_validator/rma_required.rb +17 -0
  82. data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +16 -0
  83. data/app/models/spree/return_item/exchange_variant_eligibility/same_option_value.rb +34 -0
  84. data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +10 -0
  85. data/app/models/spree/returns_calculator.rb +8 -0
  86. data/app/models/spree/shipment.rb +209 -154
  87. data/app/models/spree/shipment_handler.rb +43 -0
  88. data/app/models/spree/shipping_calculator.rb +1 -1
  89. data/app/models/spree/shipping_category.rb +2 -2
  90. data/app/models/spree/shipping_method.rb +1 -1
  91. data/app/models/spree/shipping_method_category.rb +1 -1
  92. data/app/models/spree/shipping_rate.rb +4 -0
  93. data/app/models/spree/stock/adjuster.rb +10 -11
  94. data/app/models/spree/stock/availability_validator.rb +6 -10
  95. data/app/models/spree/stock/content_item.rb +48 -0
  96. data/app/models/spree/stock/coordinator.rb +14 -7
  97. data/app/models/spree/stock/estimator.rb +1 -1
  98. data/app/models/spree/stock/inventory_unit_builder.rb +21 -0
  99. data/app/models/spree/stock/package.rb +38 -51
  100. data/app/models/spree/stock/packer.rb +13 -11
  101. data/app/models/spree/stock/prioritizer.rb +7 -7
  102. data/app/models/spree/stock/splitter/base.rb +2 -2
  103. data/app/models/spree/stock_item.rb +6 -9
  104. data/app/models/spree/stock_location.rb +17 -25
  105. data/app/models/spree/stock_movement.rb +1 -8
  106. data/app/models/spree/stock_transfer.rb +0 -2
  107. data/app/models/spree/store.rb +1 -1
  108. data/app/models/spree/tax_category.rb +2 -2
  109. data/app/models/spree/tax_rate.rb +16 -22
  110. data/app/models/spree/taxon.rb +1 -1
  111. data/app/models/spree/variant.rb +45 -23
  112. data/app/models/spree/zone.rb +17 -17
  113. data/app/models/spree/zone_member.rb +1 -1
  114. data/app/views/layouts/spree/base_mailer.html.erb +784 -0
  115. data/app/views/spree/order_mailer/cancel_email.html.erb +45 -0
  116. data/app/views/spree/order_mailer/cancel_email.text.erb +2 -2
  117. data/app/views/spree/order_mailer/confirm_email.html.erb +84 -0
  118. data/app/views/spree/order_mailer/confirm_email.text.erb +2 -2
  119. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +22 -0
  120. data/app/views/spree/shared/_base_mailer_footer.html.erb +20 -0
  121. data/app/views/spree/shared/_base_mailer_header.html.erb +31 -0
  122. data/app/views/spree/shipment_mailer/shipped_email.html.erb +34 -0
  123. data/app/views/spree/test_mailer/test_email.html.erb +40 -0
  124. data/app/views/spree/test_mailer/test_email.text.erb +2 -2
  125. data/config/initializers/friendly_id.rb +88 -0
  126. data/config/initializers/premailer_assets.rb +1 -0
  127. data/config/initializers/user_class_extensions.rb +9 -22
  128. data/config/locales/en.yml +180 -12
  129. data/db/default/spree/states.rb +73 -55
  130. data/db/migrate/20130213191427_create_default_stock.rb +1 -0
  131. data/db/migrate/20130807024301_upgrade_adjustments.rb +4 -5
  132. data/db/migrate/20140309033438_create_store_from_preferences.rb +0 -7
  133. data/db/migrate/20140318191500_create_spree_taxons_promotion_rules.rb +8 -0
  134. data/db/migrate/20140530024945_move_order_token_from_tokenized_permission.rb +1 -1
  135. data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +5 -3
  136. data/db/migrate/20140625214618_create_spree_refunds.rb +12 -0
  137. data/db/migrate/20140702140656_create_spree_return_authorization_inventory_unit.rb +12 -0
  138. data/db/migrate/20140707125621_rename_return_authorization_inventory_unit_to_return_items.rb +5 -0
  139. data/db/migrate/20140709160534_backfill_line_item_pre_tax_amount.rb +10 -0
  140. data/db/migrate/20140710041921_recreate_spree_return_authorizations.rb +55 -0
  141. data/db/migrate/20140710181204_add_amount_fields_to_return_items.rb +7 -0
  142. data/db/migrate/20140710190048_drop_return_authorization_amount.rb +5 -0
  143. data/db/migrate/20140713140455_create_spree_return_authorization_reasons.rb +28 -0
  144. data/db/migrate/20140713140527_create_spree_refund_reasons.rb +14 -0
  145. data/db/migrate/20140713142214_rename_return_authorization_reason.rb +5 -0
  146. data/db/migrate/20140715182625_create_spree_promotion_categories.rb +11 -0
  147. data/db/migrate/20140716204111_drop_received_at_on_return_items.rb +9 -0
  148. data/db/migrate/20140716212330_add_reception_and_acceptance_status_to_return_items.rb +6 -0
  149. data/db/migrate/20140717155155_create_default_refund_reason.rb +9 -0
  150. data/db/migrate/20140717185932_add_default_to_spree_stock_locations.rb +5 -0
  151. data/db/migrate/20140718133010_create_spree_customer_returns.rb +9 -0
  152. data/db/migrate/20140718133349_add_customer_return_id_to_return_item.rb +6 -0
  153. data/db/migrate/20140718195325_create_friendly_id_slugs.rb +15 -0
  154. data/db/migrate/20140723004419_rename_spree_refund_return_authorization_id.rb +5 -0
  155. data/db/migrate/20140723152808_increase_return_item_pre_tax_amount_precision.rb +13 -0
  156. data/db/migrate/20140723214541_copy_product_slugs_to_slug_history.rb +15 -0
  157. data/db/migrate/20140725131539_create_spree_reimbursements.rb +21 -0
  158. data/db/migrate/20140728225422_add_promotionable_to_spree_products.rb +5 -0
  159. data/db/migrate/20140729133613_add_exchange_inventory_unit_foreign_keys.rb +7 -0
  160. data/db/migrate/20140730155938_add_acceptance_status_errors_to_return_item.rb +5 -0
  161. data/db/migrate/20140731150017_create_spree_reimbursement_types.rb +20 -0
  162. data/db/migrate/20140805171035_add_default_to_spree_credit_cards.rb +5 -0
  163. data/db/migrate/20140805171219_make_existing_credit_cards_default.rb +10 -0
  164. data/db/migrate/20140806144901_add_type_to_reimbursement_type.rb +9 -0
  165. data/db/migrate/20140808184039_create_spree_reimbursement_credits.rb +10 -0
  166. data/db/migrate/20140827170513_add_meta_title_to_spree_products.rb +7 -0
  167. data/db/migrate/20140924164824_add_code_to_spree_tax_categories.rb +5 -0
  168. data/db/migrate/20141002191113_add_code_to_spree_shipping_methods.rb +5 -0
  169. data/db/migrate/20141007230328_add_cancel_audit_fields_to_spree_orders.rb +6 -0
  170. data/db/migrate/20141009204607_add_store_id_to_orders.rb +8 -0
  171. data/lib/generators/spree/install/install_generator.rb +7 -3
  172. data/lib/spree/core.rb +11 -10
  173. data/lib/spree/core/controller_helpers/common.rb +3 -10
  174. data/lib/spree/core/controller_helpers/order.rb +15 -12
  175. data/lib/spree/core/controller_helpers/strong_parameters.rb +9 -9
  176. data/lib/spree/core/engine.rb +8 -1
  177. data/lib/spree/core/importer/order.rb +5 -17
  178. data/lib/spree/core/search/base.rb +1 -1
  179. data/lib/spree/core/validators/email.rb +1 -1
  180. data/lib/spree/core/version.rb +1 -1
  181. data/lib/spree/instrumentation.rb +41 -0
  182. data/lib/spree/migrations.rb +3 -7
  183. data/lib/spree/money.rb +2 -2
  184. data/lib/spree/permitted_attributes.rb +10 -9
  185. data/lib/spree/testing_support/ability_helpers.rb +25 -25
  186. data/lib/spree/testing_support/authorization_helpers.rb +3 -5
  187. data/lib/spree/testing_support/capybara_ext.rb +2 -2
  188. data/lib/spree/testing_support/factories/calculator_factory.rb +0 -8
  189. data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
  190. data/lib/spree/testing_support/factories/customer_return_factory.rb +31 -0
  191. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +1 -0
  192. data/lib/spree/testing_support/factories/line_item_factory.rb +2 -1
  193. data/lib/spree/testing_support/factories/order_factory.rb +11 -6
  194. data/lib/spree/testing_support/factories/promotion_category_factory.rb +6 -0
  195. data/lib/spree/testing_support/factories/promotion_factory.rb +9 -7
  196. data/lib/spree/testing_support/factories/refund_factory.rb +14 -0
  197. data/lib/spree/testing_support/factories/reimbursement_factory.rb +16 -0
  198. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +7 -0
  199. data/lib/spree/testing_support/factories/return_authorization_factory.rb +9 -3
  200. data/lib/spree/testing_support/factories/return_item_factory.rb +10 -0
  201. data/lib/spree/testing_support/factories/shipment_factory.rb +1 -0
  202. data/lib/spree/testing_support/factories/shipping_method_factory.rb +3 -2
  203. data/lib/spree/testing_support/factories/stock_factory.rb +12 -11
  204. data/lib/spree/testing_support/flash.rb +2 -2
  205. data/lib/tasks/email.rake +7 -0
  206. data/lib/tasks/exchanges.rake +70 -0
  207. data/vendor/assets/javascripts/jquery.validate/localization/messages_et.js +23 -0
  208. data/vendor/assets/javascripts/jquery.validate/localization/messages_eu.js +25 -0
  209. data/vendor/assets/javascripts/jquery.validate/localization/messages_hr.js +25 -0
  210. data/vendor/assets/javascripts/jquery.validate/localization/messages_ka.js +25 -0
  211. data/vendor/assets/javascripts/jquery.validate/localization/messages_ko.js +25 -0
  212. data/vendor/assets/javascripts/jquery.validate/localization/messages_my.js +25 -0
  213. data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_BR.js +26 -0
  214. data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_PT.js +26 -0
  215. data/vendor/assets/javascripts/jquery.validate/localization/messages_sl.js +25 -0
  216. data/vendor/assets/javascripts/jquery.validate/localization/messages_sv.js +23 -0
  217. data/vendor/assets/javascripts/jquery.validate/localization/messages_uk.js +25 -0
  218. data/vendor/assets/javascripts/jquery.validate/localization/messages_zh.js +25 -0
  219. data/vendor/assets/javascripts/jquery.validate/localization/messages_zh_TW.js +26 -0
  220. metadata +163 -47
  221. data/app/models/concerns/spree/ransackable_attributes.rb +0 -19
  222. data/db/migrate/20141021194502_add_state_lock_version_to_order.rb +0 -5
  223. data/db/migrate/20141101231208_fix_adjustment_order_presence.rb +0 -13
  224. data/db/migrate/20141105213646_update_classifications_positions.rb +0 -9
  225. data/db/migrate/20141120135441_add_guest_token_index_to_spree_orders.rb +0 -5
  226. data/db/migrate/20150515211137_fix_adjustment_order_id.rb +0 -70
  227. data/lib/spree/core/adjustment_source.rb +0 -26
  228. data/lib/spree/core/calculated_adjustments.rb +0 -35
  229. data/lib/spree/core/controller_helpers.rb +0 -20
  230. data/lib/spree/core/user_address.rb +0 -32
  231. data/lib/spree/core/user_payment_source.rb +0 -20
  232. data/lib/spree/localized_number.rb +0 -20
@@ -13,7 +13,7 @@ module Spree
13
13
  after_touch :touch_all_products
14
14
 
15
15
  def touch_all_products
16
- products.find_each(&:touch)
16
+ products.update_all(updated_at: Time.current)
17
17
  end
18
18
  end
19
19
  end
@@ -8,15 +8,8 @@ module Spree
8
8
 
9
9
  after_touch :touch_all_variants
10
10
 
11
- self.whitelisted_ransackable_attributes = ['presentation']
12
-
13
11
  def touch_all_variants
14
- # This can cause a cascade of products to be updated
15
- # To disable it in Rails 4.1, we can do this:
16
- # https://github.com/rails/rails/pull/12772
17
- # Spree::Product.no_touching do
18
- variants.find_each(&:touch)
19
- # end
12
+ variants.update_all(updated_at: Time.current)
20
13
  end
21
14
  end
22
15
  end
@@ -3,8 +3,14 @@ require 'spree/order/checkout'
3
3
 
4
4
  module Spree
5
5
  class Order < Spree::Base
6
+
7
+ ORDER_NUMBER_LENGTH = 9
8
+ ORDER_NUMBER_LETTERS = false
9
+ ORDER_NUMBER_PREFIX = 'R'
10
+
6
11
  include Spree::Order::Checkout
7
12
  include Spree::Order::CurrencyUpdater
13
+ include Spree::Order::Payments
8
14
 
9
15
  checkout_flow do
10
16
  go_to_state :address
@@ -15,20 +21,19 @@ module Spree
15
21
  remove_transition from: :delivery, to: :confirm
16
22
  end
17
23
 
18
- self.whitelisted_ransackable_associations = %w[shipments user promotions bill_address ship_address line_items]
19
- self.whitelisted_ransackable_attributes = %w[completed_at created_at email number state payment_state shipment_state total considered_risky]
20
-
21
24
  attr_reader :coupon_code
22
- attr_accessor :temporary_address
25
+ attr_accessor :temporary_address, :temporary_credit_card
23
26
 
24
27
  if Spree.user_class
25
28
  belongs_to :user, class_name: Spree.user_class.to_s
26
29
  belongs_to :created_by, class_name: Spree.user_class.to_s
27
30
  belongs_to :approver, class_name: Spree.user_class.to_s
31
+ belongs_to :canceler, class_name: Spree.user_class.to_s
28
32
  else
29
33
  belongs_to :user
30
34
  belongs_to :created_by
31
35
  belongs_to :approver
36
+ belongs_to :canceler
32
37
  end
33
38
 
34
39
  belongs_to :bill_address, foreign_key: :bill_address_id, class_name: 'Spree::Address'
@@ -40,20 +45,17 @@ module Spree
40
45
  alias_attribute :ship_total, :shipment_total
41
46
 
42
47
  has_many :state_changes, as: :stateful
43
- has_many :line_items, -> { order("#{LineItem.table_name}.created_at ASC") }, dependent: :destroy, inverse_of: :order
48
+ has_many :line_items, -> { order('created_at ASC') }, dependent: :destroy, inverse_of: :order
44
49
  has_many :payments, dependent: :destroy
45
- has_many :return_authorizations, dependent: :destroy, inverse_of: :order
50
+ has_many :return_authorizations, dependent: :destroy
51
+ has_many :reimbursements, inverse_of: :order
46
52
  has_many :adjustments, -> { order("#{Adjustment.table_name}.created_at ASC") }, as: :adjustable, dependent: :destroy
47
53
  has_many :line_item_adjustments, through: :line_items, source: :adjustments
48
54
  has_many :shipment_adjustments, through: :shipments, source: :adjustments
49
55
  has_many :inventory_units, inverse_of: :order
50
56
  has_many :products, through: :variants
51
57
  has_many :variants, through: :line_items
52
- has_many :all_adjustments,
53
- class_name: 'Spree::Adjustment',
54
- foreign_key: :order_id,
55
- dependent: :destroy,
56
- inverse_of: :order
58
+ has_many :refunds, through: :payments
57
59
 
58
60
  has_and_belongs_to_many :promotions, join_table: 'spree_orders_promotions'
59
61
 
@@ -82,7 +84,7 @@ module Spree
82
84
 
83
85
  validates :email, presence: true, if: :require_email
84
86
  validates :email, email: true, if: :require_email, allow_blank: true
85
- validates :number, presence: true, uniqueness: { allow_blank: true }
87
+ validates :number, uniqueness: true
86
88
  validate :has_available_shipment
87
89
 
88
90
  make_permalink field: :number
@@ -92,6 +94,9 @@ module Spree
92
94
  class_attribute :update_hooks
93
95
  self.update_hooks = Set.new
94
96
 
97
+ class_attribute :line_item_comparison_hooks
98
+ self.line_item_comparison_hooks = Set.new
99
+
95
100
  def self.by_number(number)
96
101
  where(number: number)
97
102
  end
@@ -99,7 +104,8 @@ module Spree
99
104
  scope :created_between, ->(start_date, end_date) { where(created_at: start_date..end_date) }
100
105
  scope :completed_between, ->(start_date, end_date) { where(completed_at: start_date..end_date) }
101
106
 
102
- scope :reverse_chronological, -> { order(created_at: :desc) }
107
+ # shows completed orders first, by their completed_at date, then uncompleted orders by their created_at
108
+ scope :reverse_chronological, -> { order('spree_orders.completed_at IS NULL', completed_at: :desc, created_at: :desc) }
103
109
 
104
110
  def self.by_customer(customer)
105
111
  joins(:user).where("#{Spree.user_class.table_name}.email" => customer)
@@ -129,11 +135,21 @@ module Spree
129
135
  self.line_item_comparison_hooks.add(hook)
130
136
  end
131
137
 
138
+ def all_adjustments
139
+ Adjustment.where("order_id = :order_id OR (adjustable_id = :order_id AND adjustable_type = 'Spree::Order')",
140
+ order_id: self.id)
141
+ end
142
+
132
143
  # For compatiblity with Calculator::PriceSack
133
144
  def amount
134
145
  line_items.inject(0.0) { |sum, li| sum + li.amount }
135
146
  end
136
147
 
148
+ # Sum of all line item amounts pre-tax
149
+ def pre_tax_item_amount
150
+ line_items.to_a.sum(&:pre_tax_amount)
151
+ end
152
+
137
153
  def currency
138
154
  self[:currency] || Spree::Config[:currency]
139
155
  end
@@ -243,8 +259,8 @@ module Spree
243
259
  shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state)
244
260
  end
245
261
 
246
- def awaiting_returns?
247
- return_authorizations.any? { |return_authorization| return_authorization.authorized? }
262
+ def all_inventory_units_returned?
263
+ inventory_units.all? { |inventory_unit| inventory_unit.returned? }
248
264
  end
249
265
 
250
266
  def contents
@@ -265,35 +281,62 @@ module Spree
265
281
  end
266
282
  end
267
283
 
268
- def generate_order_number(digits = 9)
284
+ def generate_order_number(options = {})
285
+ options[:length] ||= ORDER_NUMBER_LENGTH
286
+ options[:letters] ||= ORDER_NUMBER_LETTERS
287
+ options[:prefix] ||= ORDER_NUMBER_PREFIX
288
+
289
+ possible = (0..9).to_a
290
+ possible += ('A'..'Z').to_a if options[:letters]
291
+
269
292
  self.number ||= loop do
270
- # Make a random number.
271
- random = "R#{Array.new(digits){rand(10)}.join}"
272
- # Use the random number if no other order exists with it.
273
- if self.class.exists?(number: random)
274
- # If over half of all possible options are taken add another digit.
275
- digits += 1 if self.class.count > (10 ** digits / 2)
276
- else
277
- break random
278
- end
279
- end
293
+ # Make a random number.
294
+ random = "#{options[:prefix]}#{(0...options[:length]).map { possible.shuffle.first }.join}"
295
+ # Use the random number if no other order exists with it.
296
+ if self.class.exists?(number: random)
297
+ # If over half of all possible options are taken add another digit.
298
+ options[:length] += 1 if self.class.count > (10 ** options[:length] / 2)
299
+ else
300
+ break random
301
+ end
302
+ end
280
303
  end
281
304
 
282
305
  def shipped_shipments
283
306
  shipments.shipped
284
307
  end
285
308
 
286
- def contains?(variant)
287
- find_line_item_by_variant(variant).present?
309
+ def contains?(variant, options = {})
310
+ find_line_item_by_variant(variant, options).present?
288
311
  end
289
312
 
290
- def quantity_of(variant)
291
- line_item = find_line_item_by_variant(variant)
313
+ def quantity_of(variant, options = {})
314
+ line_item = find_line_item_by_variant(variant, options)
292
315
  line_item ? line_item.quantity : 0
293
316
  end
294
317
 
295
- def find_line_item_by_variant(variant)
296
- line_items.detect { |line_item| line_item.variant_id == variant.id }
318
+ def find_line_item_by_variant(variant, options = {})
319
+ line_items.detect { |line_item|
320
+ line_item.variant_id == variant.id &&
321
+ line_item_options_match(line_item, options)
322
+ }
323
+ end
324
+
325
+ # This method enables extensions to participate in the
326
+ # "Are these line items equal" decision.
327
+ #
328
+ # When adding to cart, an extension would send something like:
329
+ # params[:product_customizations]={...}
330
+ #
331
+ # and would provide:
332
+ #
333
+ # def product_customizations_match
334
+ def line_item_options_match(line_item, options)
335
+ return true unless options
336
+
337
+ self.line_item_comparison_hooks.all? { |hook|
338
+ self.send(hook, line_item, options)
339
+ }
297
340
  end
298
341
 
299
342
  # Creates new tax charges if there are any applicable rates. If prices already
@@ -306,7 +349,7 @@ module Spree
306
349
  end
307
350
 
308
351
  def outstanding_balance
309
- if state == 'canceled'
352
+ if self.state == 'canceled' && self.payments.present? && self.payments.completed.size > 0
310
353
  -1 * payment_total
311
354
  else
312
355
  total - payment_total
@@ -314,7 +357,7 @@ module Spree
314
357
  end
315
358
 
316
359
  def outstanding_balance?
317
- self.outstanding_balance != 0
360
+ self.outstanding_balance != 0
318
361
  end
319
362
 
320
363
  def name
@@ -332,6 +375,11 @@ module Spree
332
375
  CreditCard.where(id: credit_card_ids)
333
376
  end
334
377
 
378
+ def valid_credit_cards
379
+ credit_card_ids = payments.from_credit_card.valid.pluck(:source_id).uniq
380
+ CreditCard.where(id: credit_card_ids)
381
+ end
382
+
335
383
  # Finalizes an in progress order after checkout is complete.
336
384
  # Called after transition to complete state when payments will have been processed
337
385
  def finalize!
@@ -346,7 +394,7 @@ module Spree
346
394
  end
347
395
 
348
396
  updater.update_shipment_state
349
- save
397
+ save!
350
398
  updater.run_hooks
351
399
 
352
400
  touch :completed_at
@@ -356,12 +404,6 @@ module Spree
356
404
  consider_risk
357
405
  end
358
406
 
359
- def fulfill!
360
- shipments.each { |shipment| shipment.update!(self) if shipment.persisted? }
361
- updater.update_shipment_state
362
- save!
363
- end
364
-
365
407
  def deliver_order_confirmation_email
366
408
  OrderMailer.confirm_email(self.id).deliver
367
409
  update_column(:confirmation_delivered, true)
@@ -376,46 +418,6 @@ module Spree
376
418
  @available_payment_methods ||= (PaymentMethod.available(:front_end) + PaymentMethod.available(:both)).uniq
377
419
  end
378
420
 
379
- def pending_payments
380
- payments.select { |payment| payment.checkout? || payment.pending? }
381
- end
382
-
383
- # processes any pending payments and must return a boolean as it's
384
- # return value is used by the checkout state_machine to determine
385
- # success or failure of the 'complete' event for the order
386
- #
387
- # Returns:
388
- #
389
- # - true if all pending_payments processed successfully
390
- #
391
- # - true if a payment failed, ie. raised a GatewayError
392
- # which gets rescued and converted to TRUE when
393
- # :allow_checkout_gateway_error is set to true
394
- #
395
- # - false if a payment failed, ie. raised a GatewayError
396
- # which gets rescued and converted to FALSE when
397
- # :allow_checkout_on_gateway_error is set to false
398
- #
399
- def process_payments!\
400
- # Don't run if there is nothing to pay.
401
- return if payment_total >= total
402
- # Prevent orders from transitioning to complete without a successfully processed payment.
403
- raise Core::GatewayError.new(Spree.t(:no_payment_found)) if pending_payments.empty?
404
-
405
- pending_payments.each do |payment|
406
- break if payment_total >= total
407
-
408
- payment.process!
409
-
410
- if payment.completed?
411
- self.payment_total += payment.amount
412
- end
413
- end
414
- rescue Core::GatewayError => e
415
- result = !!Spree::Config[:allow_checkout_on_gateway_error]
416
- errors.add(:base, e.message) and return result
417
- end
418
-
419
421
  def billing_firstname
420
422
  bill_address.try(:firstname)
421
423
  end
@@ -425,29 +427,34 @@ module Spree
425
427
  end
426
428
 
427
429
  def insufficient_stock_lines
428
- line_items.select(&:insufficient_stock?)
430
+ line_items.select(&:insufficient_stock?)
429
431
  end
430
432
 
431
433
  def ensure_line_items_are_in_stock
432
434
  if insufficient_stock_lines.present?
433
- errors.add(:base, Spree.t(:insufficient_stock_lines_present))
434
- restart_checkout_flow
435
- false
436
- else
437
- true
435
+ errors.add(:base, Spree.t(:insufficient_stock_lines_present)) and return false
438
436
  end
439
437
  end
440
438
 
441
439
  def merge!(order, user = nil)
442
- order.line_items.each do |line_item|
443
- next unless line_item.currency == currency
444
- current_line_item = self.line_items.find_by(variant: line_item.variant)
440
+ order.line_items.each do |other_order_line_item|
441
+ next unless other_order_line_item.currency == currency
442
+
443
+ # Compare the line items of the other order with mine.
444
+ # Make sure you allow any extensions to chime in on whether or
445
+ # not the extension-specific parts of the line item match
446
+ current_line_item = self.line_items.detect { |my_li|
447
+ my_li.variant == other_order_line_item.variant &&
448
+ self.line_item_comparison_hooks.all? { |hook|
449
+ self.send(hook, my_li, other_order_line_item)
450
+ }
451
+ }
445
452
  if current_line_item
446
- current_line_item.quantity += line_item.quantity
447
- current_line_item.save
453
+ current_line_item.quantity += other_order_line_item.quantity
454
+ current_line_item.save!
448
455
  else
449
- line_item.order_id = self.id
450
- line_item.save
456
+ other_order_line_item.order_id = self.id
457
+ other_order_line_item.save!
451
458
  end
452
459
  end
453
460
 
@@ -506,15 +513,9 @@ module Spree
506
513
  end
507
514
 
508
515
  def create_proposed_shipments
509
- all_adjustments.shipping.delete_all
516
+ adjustments.shipping.delete_all
510
517
  shipments.destroy_all
511
-
512
- packages = Spree::Stock::Coordinator.new(self).packages
513
- packages.each do |package|
514
- shipments << package.to_shipment
515
- end
516
-
517
- shipments
518
+ self.shipments = Spree::Stock::Coordinator.new(self).shipments
518
519
  end
519
520
 
520
521
  def apply_free_shipping_promotions
@@ -539,9 +540,10 @@ module Spree
539
540
 
540
541
  def restart_checkout_flow
541
542
  self.update_columns(
542
- state: checkout_steps.first,
543
+ state: 'cart',
543
544
  updated_at: Time.now,
544
545
  )
546
+ self.next! if self.line_items.size > 0
545
547
  end
546
548
 
547
549
  def refresh_shipment_rates
@@ -557,11 +559,22 @@ module Spree
557
559
  updater.update_shipment_total
558
560
  persist_totals
559
561
  end
562
+ Spree.instrument_method self, :set_shipments_cost
560
563
 
561
564
  def is_risky?
562
565
  self.payments.risky.count > 0
563
566
  end
564
567
 
568
+ def canceled_by(user)
569
+ self.transaction do
570
+ cancel!
571
+ self.update_columns(
572
+ canceler_id: user.id,
573
+ canceled_at: Time.now,
574
+ )
575
+ end
576
+ end
577
+
565
578
  def approved_by(user)
566
579
  self.transaction do
567
580
  approve!
@@ -620,38 +633,43 @@ module Spree
620
633
  line_items.sum(:quantity)
621
634
  end
622
635
 
636
+ def has_non_reimbursement_related_refunds?
637
+ refunds.non_reimbursement.exists? ||
638
+ payments.offset_payment.exists? # how old versions of spree stored refunds
639
+ end
640
+
623
641
  private
624
642
 
625
- def link_by_email
626
- self.email = user.email if self.user
627
- end
643
+ def link_by_email
644
+ self.email = user.email if self.user
645
+ end
628
646
 
629
- # Determine if email is required (we don't want validation errors before we hit the checkout)
630
- def require_email
631
- true unless new_record? or ['cart', 'address'].include?(state)
632
- end
647
+ # Determine if email is required (we don't want validation errors before we hit the checkout)
648
+ def require_email
649
+ true unless new_record? or ['cart', 'address'].include?(state)
650
+ end
633
651
 
634
- def ensure_line_items_present
635
- unless line_items.present?
636
- errors.add(:base, Spree.t(:there_are_no_items_for_this_order)) and return false
637
- end
652
+ def ensure_line_items_present
653
+ unless line_items.present?
654
+ errors.add(:base, Spree.t(:there_are_no_items_for_this_order)) and return false
638
655
  end
656
+ end
639
657
 
640
- def has_available_shipment
641
- return unless has_step?("delivery")
642
- return unless address?
643
- return unless ship_address && ship_address.valid?
644
- # errors.add(:base, :no_shipping_methods_available) if available_shipping_methods.empty?
645
- end
658
+ def has_available_shipment
659
+ return unless has_step?("delivery")
660
+ return unless has_step?(:address) && address?
661
+ return unless ship_address && ship_address.valid?
662
+ # errors.add(:base, :no_shipping_methods_available) if available_shipping_methods.empty?
663
+ end
646
664
 
647
- def ensure_available_shipping_rates
648
- if shipments.empty? || shipments.any? { |shipment| shipment.shipping_rates.blank? }
649
- # After this point, order redirects back to 'address' state and asks user to pick a proper address
650
- # Therefore, shipments are not necessary at this point.
651
- shipments.delete_all
652
- errors.add(:base, Spree.t(:items_cannot_be_shipped)) and return false
653
- end
665
+ def ensure_available_shipping_rates
666
+ if shipments.empty? || shipments.any? { |shipment| shipment.shipping_rates.blank? }
667
+ # After this point, order redirects back to 'address' state and asks user to pick a proper address
668
+ # Therefore, shipments are not necessary at this point.
669
+ shipments.delete_all
670
+ errors.add(:base, Spree.t(:items_cannot_be_shipped)) and return false
654
671
  end
672
+ end
655
673
 
656
674
  def after_cancel
657
675
  shipments.each { |shipment| shipment.cancel! }
@@ -660,28 +678,28 @@ module Spree
660
678
  self.update!
661
679
  end
662
680
 
663
- def send_cancel_email
664
- OrderMailer.cancel_email(self.id).deliver
665
- end
681
+ def send_cancel_email
682
+ OrderMailer.cancel_email(self.id).deliver
683
+ end
666
684
 
667
- def after_resume
668
- shipments.each { |shipment| shipment.resume! }
669
- consider_risk
670
- end
685
+ def after_resume
686
+ shipments.each { |shipment| shipment.resume! }
687
+ consider_risk
688
+ end
671
689
 
672
- def use_billing?
673
- @use_billing == true || @use_billing == 'true' || @use_billing == '1'
674
- end
690
+ def use_billing?
691
+ @use_billing == true || @use_billing == 'true' || @use_billing == '1'
692
+ end
675
693
 
676
- def set_currency
677
- self.currency = Spree::Config[:currency] if self[:currency].nil?
678
- end
694
+ def set_currency
695
+ self.currency = Spree::Config[:currency] if self[:currency].nil?
696
+ end
679
697
 
680
- def create_token
681
- self.guest_token ||= loop do
682
- random_token = SecureRandom.urlsafe_base64(nil, false)
683
- break random_token unless self.class.exists?(guest_token: random_token)
684
- end
698
+ def create_token
699
+ self.guest_token ||= loop do
700
+ random_token = SecureRandom.urlsafe_base64(nil, false)
701
+ break random_token unless self.class.exists?(guest_token: random_token)
685
702
  end
703
+ end
686
704
  end
687
705
  end