spree_core 2.3.13 → 2.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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