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
@@ -58,7 +58,7 @@ module Spree
58
58
  end
59
59
 
60
60
  event :return do
61
- transition to: :returned, from: :awaiting_return, unless: :awaiting_returns?
61
+ transition to: :returned, from: [:complete, :awaiting_return, :canceled], if: :all_inventory_units_returned?
62
62
  end
63
63
 
64
64
  event :resume do
@@ -78,6 +78,10 @@ module Spree
78
78
  order.process_payments!
79
79
  end
80
80
  end
81
+ after_transition to: :complete, do: :persist_user_credit_card
82
+ before_transition to: :payment, do: :set_shipments_cost
83
+ before_transition to: :payment, do: :create_tax_charge!
84
+ before_transition to: :payment, do: :assign_default_credit_card
81
85
  end
82
86
 
83
87
  before_transition from: :cart, do: :ensure_line_items_present
@@ -88,11 +92,6 @@ module Spree
88
92
  before_transition from: :address, do: :persist_user_address!
89
93
  end
90
94
 
91
- if states[:payment]
92
- before_transition to: :payment, do: :set_shipments_cost
93
- before_transition to: :payment, do: :create_tax_charge!
94
- end
95
-
96
95
  if states[:delivery]
97
96
  before_transition to: :delivery, do: :create_proposed_shipments
98
97
  before_transition to: :delivery, do: :ensure_available_shipping_rates
@@ -102,8 +101,6 @@ module Spree
102
101
 
103
102
  before_transition to: :resumed, do: :ensure_line_items_are_in_stock
104
103
 
105
- before_transition to: :complete, do: :ensure_line_items_are_in_stock
106
-
107
104
  after_transition to: :complete, do: :finalize!
108
105
  after_transition to: :resumed, do: :after_resume
109
106
  after_transition to: :canceled, do: :after_cancel
@@ -203,8 +200,12 @@ module Spree
203
200
  step.present? && self.checkout_steps.include?(step)
204
201
  end
205
202
 
203
+ def passed_checkout_step?(step)
204
+ has_checkout_step?(step) && checkout_step_index(step) < checkout_step_index(self.state)
205
+ end
206
+
206
207
  def checkout_step_index(step)
207
- self.checkout_steps.index(step)
208
+ self.checkout_steps.index(step).to_i
208
209
  end
209
210
 
210
211
  def self.removed_transitions
@@ -228,10 +229,7 @@ module Spree
228
229
 
229
230
  # Set existing card after setting permitted parameters because
230
231
  # rails would slice parameters containg ruby objects, apparently
231
- #
232
- # Need to check both outside and inside :order beacuse frontend
233
- # sends existing_card out of :order
234
- existing_card_id = @updating_params[:existing_card] || (@updating_params[:order] ? @updating_params[:order][:existing_card] : nil)
232
+ existing_card_id = @updating_params[:order] ? @updating_params[:order][:existing_card] : nil
235
233
 
236
234
  if existing_card_id.present?
237
235
  credit_card = CreditCard.find existing_card_id
@@ -273,6 +271,22 @@ module Spree
273
271
  end
274
272
  end
275
273
 
274
+ def persist_user_credit_card
275
+ if !self.temporary_credit_card && self.user_id && self.valid_credit_cards.present?
276
+ default_cc = self.valid_credit_cards.first
277
+ default_cc.user_id = self.user_id
278
+ default_cc.default = true
279
+ default_cc.save
280
+ end
281
+ end
282
+
283
+ def assign_default_credit_card
284
+ if self.payments.from_credit_card.count == 0 && self.user && self.user.default_credit_card.try(:valid?)
285
+ cc = self.user.default_credit_card
286
+ self.payments.create!(payment_method_id: cc.payment_method_id, source: cc)
287
+ end
288
+ end
289
+
276
290
  private
277
291
  # For payment step, filter order parameters to produce the expected nested
278
292
  # attributes for a single payment and its source, discarding attributes
@@ -288,19 +302,17 @@ module Spree
288
302
  # }
289
303
  #
290
304
  def update_params_payment_source
291
- if has_checkout_step?("payment") && self.payment?
292
- if @updating_params[:payment_source].present?
293
- source_params = @updating_params.delete(:payment_source)[@updating_params[:order][:payments_attributes].first[:payment_method_id].to_s]
305
+ if @updating_params[:payment_source].present?
306
+ source_params = @updating_params.delete(:payment_source)[@updating_params[:order][:payments_attributes].first[:payment_method_id].to_s]
294
307
 
295
- if source_params
296
- @updating_params[:order][:payments_attributes].first[:source_attributes] = source_params
297
- end
308
+ if source_params
309
+ @updating_params[:order][:payments_attributes].first[:source_attributes] = source_params
298
310
  end
311
+ end
299
312
 
300
- if @updating_params[:order][:payments_attributes] || @updating_params[:order][:existing_card]
301
- @updating_params[:order][:payments_attributes] ||= [{}]
302
- @updating_params[:order][:payments_attributes].first[:amount] = self.total
303
- end
313
+ if @updating_params[:order] && (@updating_params[:order][:payments_attributes] || @updating_params[:order][:existing_card])
314
+ @updating_params[:order][:payments_attributes] ||= [{}]
315
+ @updating_params[:order][:payments_attributes].first[:amount] = self.total
304
316
  end
305
317
  end
306
318
  end
@@ -0,0 +1,66 @@
1
+ module Spree
2
+ class Order < Spree::Base
3
+ module Payments
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ # processes any pending payments and must return a boolean as it's
7
+ # return value is used by the checkout state_machine to determine
8
+ # success or failure of the 'complete' event for the order
9
+ #
10
+ # Returns:
11
+ #
12
+ # - true if all pending_payments processed successfully
13
+ #
14
+ # - true if a payment failed, ie. raised a GatewayError
15
+ # which gets rescued and converted to TRUE when
16
+ # :allow_checkout_gateway_error is set to true
17
+ #
18
+ # - false if a payment failed, ie. raised a GatewayError
19
+ # which gets rescued and converted to FALSE when
20
+ # :allow_checkout_on_gateway_error is set to false
21
+ #
22
+ def process_payments!
23
+ process_payments_with(:process!)
24
+ end
25
+
26
+ def authorize_payments!
27
+ process_payments_with(:authorize!)
28
+ end
29
+
30
+ def capture_payments!
31
+ process_payments_with(:purchase!)
32
+ end
33
+
34
+ def pending_payments
35
+ payments.select { |payment| payment.pending? }
36
+ end
37
+
38
+ def unprocessed_payments
39
+ payments.select { |payment| payment.checkout? }
40
+ end
41
+
42
+ private
43
+
44
+ def process_payments_with(method)
45
+ # Don't run if there is nothing to pay.
46
+ return if payment_total >= total
47
+ # Prevent orders from transitioning to complete without a successfully processed payment.
48
+ raise Core::GatewayError.new(Spree.t(:no_payment_found)) if unprocessed_payments.empty?
49
+
50
+ unprocessed_payments.each do |payment|
51
+ break if payment_total >= total
52
+
53
+ payment.public_send(method)
54
+
55
+ if payment.completed?
56
+ self.payment_total += payment.amount
57
+ end
58
+ end
59
+ rescue Core::GatewayError => e
60
+ result = !!Spree::Config[:allow_checkout_on_gateway_error]
61
+ errors.add(:base, e.message) and return result
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -6,9 +6,10 @@ module Spree
6
6
  @order = order
7
7
  end
8
8
 
9
- def add(variant, quantity = 1, currency = nil, shipment = nil)
10
- line_item = add_to_line_item(variant, quantity, currency, shipment)
9
+ def add(variant, quantity = 1, options = {})
10
+ line_item = add_to_line_item(variant, quantity, options)
11
11
  reload_totals
12
+ shipment = options[:shipment]
12
13
  shipment.present? ? shipment.update_amounts : order.ensure_updated_shipments
13
14
  PromotionHandler::Cart.new(order, line_item).activate
14
15
  ItemAdjustments.new(line_item).update
@@ -16,9 +17,10 @@ module Spree
16
17
  line_item
17
18
  end
18
19
 
19
- def remove(variant, quantity = 1, shipment = nil)
20
- line_item = remove_from_line_item(variant, quantity, shipment)
20
+ def remove(variant, quantity = 1, options = {})
21
+ line_item = remove_from_line_item(variant, quantity, options)
21
22
  reload_totals
23
+ shipment = options[:shipment]
22
24
  shipment.present? ? shipment.update_amounts : order.ensure_updated_shipments
23
25
  PromotionHandler::Cart.new(order, line_item).activate
24
26
  ItemAdjustments.new(line_item).update
@@ -27,8 +29,8 @@ module Spree
27
29
  end
28
30
 
29
31
  def update_cart(params)
30
- if order.update_attributes(params)
31
- order.line_items = order.line_items.select {|li| li.quantity > 0 }
32
+ if order.update_attributes(filter_order_items(params))
33
+ order.line_items = order.line_items.select { |li| li.quantity > 0 }
32
34
  # Update totals, then check if the order is eligible for any cart promotions.
33
35
  # If we do not update first, then the item total will be wrong and ItemTotal
34
36
  # promotion rules would not be triggered.
@@ -43,6 +45,18 @@ module Spree
43
45
  end
44
46
 
45
47
  private
48
+
49
+ def filter_order_items(params)
50
+ filtered_params = params.symbolize_keys
51
+ return filtered_params if filtered_params[:line_items_attributes].nil? || filtered_params[:line_items_attributes][:id]
52
+
53
+ params[:line_items_attributes].each_pair do |id, value|
54
+ line_item_id = value[:id]
55
+ filtered_params[:line_items_attributes].delete(id) unless Spree::LineItem.find_by_id(line_item_id.to_i)
56
+ end
57
+ filtered_params
58
+ end
59
+
46
60
  def order_updater
47
61
  @updater ||= OrderUpdater.new(order)
48
62
  end
@@ -53,32 +67,28 @@ module Spree
53
67
  order.reload
54
68
  end
55
69
 
56
- def add_to_line_item(variant, quantity, currency=nil, shipment=nil)
57
- line_item = grab_line_item_by_variant(variant)
70
+ def add_to_line_item(variant, quantity, options = {})
71
+ line_item = grab_line_item_by_variant(variant, false, options)
58
72
 
59
73
  if line_item
60
- line_item.target_shipment = shipment
61
74
  line_item.quantity += quantity.to_i
62
75
  line_item.currency = currency unless currency.nil?
63
76
  else
64
- line_item = order.line_items.new(quantity: quantity, variant: variant)
65
- line_item.target_shipment = shipment
66
- if currency
67
- line_item.currency = currency
68
- line_item.price = variant.price_in(currency).amount
69
- else
70
- line_item.price = variant.price
71
- end
77
+ opts = { currency: order.currency }.merge ActionController::Parameters.new(options).
78
+ permit(PermittedAttributes.line_item_attributes)
79
+ line_item = order.line_items.new(quantity: quantity,
80
+ variant: variant,
81
+ options: opts)
72
82
  end
73
-
74
- line_item.save
83
+ line_item.target_shipment = options[:shipment] if options.has_key? :shipment
84
+ line_item.save!
75
85
  line_item
76
86
  end
77
87
 
78
- def remove_from_line_item(variant, quantity, shipment=nil)
79
- line_item = grab_line_item_by_variant(variant, true)
88
+ def remove_from_line_item(variant, quantity, options = {})
89
+ line_item = grab_line_item_by_variant(variant, true, options)
80
90
  line_item.quantity -= quantity
81
- line_item.target_shipment= shipment
91
+ line_item.target_shipment= options[:shipment]
82
92
 
83
93
  if line_item.quantity == 0
84
94
  line_item.destroy
@@ -89,8 +99,8 @@ module Spree
89
99
  line_item
90
100
  end
91
101
 
92
- def grab_line_item_by_variant(variant, raise_error = false)
93
- line_item = order.find_line_item_by_variant(variant)
102
+ def grab_line_item_by_variant(variant, raise_error = false, options = {})
103
+ line_item = order.find_line_item_by_variant(variant, options)
94
104
 
95
105
  if !line_item.present? && raise_error
96
106
  raise ActiveRecord::RecordNotFound, "Line item not found for variant #{variant.sku}"
@@ -9,8 +9,10 @@ module Spree
9
9
  @errors = ActiveModel::Errors.new(self)
10
10
  end
11
11
 
12
- def populate(variant_id, quantity)
13
- attempt_cart_add(variant_id, quantity)
12
+ def populate(variant_id, quantity, options = {})
13
+ # protect against passing a nil hash being passed in
14
+ # due to an empty params[:options]
15
+ attempt_cart_add(variant_id, quantity, options || {})
14
16
  valid?
15
17
  end
16
18
 
@@ -20,7 +22,7 @@ module Spree
20
22
 
21
23
  private
22
24
 
23
- def attempt_cart_add(variant_id, quantity)
25
+ def attempt_cart_add(variant_id, quantity, options = {})
24
26
  quantity = quantity.to_i
25
27
  # 2,147,483,647 is crazy.
26
28
  # See issue #2695.
@@ -31,7 +33,7 @@ module Spree
31
33
 
32
34
  variant = Spree::Variant.find(variant_id)
33
35
  if quantity > 0
34
- line_item = @order.contents.add(variant, quantity, currency)
36
+ line_item = @order.contents.add(variant, quantity, options.merge(currency: currency))
35
37
  unless line_item.valid?
36
38
  errors.add(:base, line_item.errors.messages.values.join(" "))
37
39
  return false
@@ -24,6 +24,7 @@ module Spree
24
24
  run_hooks
25
25
  persist_totals
26
26
  end
27
+ Spree.instrument_method self, :update
27
28
 
28
29
  def run_hooks
29
30
  update_hooks.each { |hook| order.send hook }
@@ -111,6 +112,7 @@ module Spree
111
112
  updated_at: Time.now,
112
113
  )
113
114
  end
115
+ Spree.instrument_method self, :persist_totals
114
116
 
115
117
  # Updates the +shipment_state+ attribute according to the following logic:
116
118
  #
@@ -158,7 +160,9 @@ module Spree
158
160
  last_state = order.payment_state
159
161
  if payments.present? && payments.valid.size == 0
160
162
  order.payment_state = 'failed'
161
- elsif order.state == 'canceled' && order.payment_total == 0
163
+ elsif !payments.present? && order.state == 'canceled'
164
+ order.payment_state = 'void'
165
+ elsif order.state == 'canceled' && order.payment_total == 0 && payments.completed.size > 0
162
166
  order.payment_state = 'void'
163
167
  else
164
168
  order.payment_state = 'balance_due' if order.outstanding_balance > 0
@@ -168,5 +172,10 @@ module Spree
168
172
  order.state_changed('payment') if last_state != order.payment_state
169
173
  order.payment_state
170
174
  end
175
+
176
+ private
177
+ def round_money(n)
178
+ (n * 100).round / 100.0
179
+ end
171
180
  end
172
181
  end
@@ -8,15 +8,14 @@ module Spree
8
8
 
9
9
  belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :payments
10
10
  belongs_to :source, polymorphic: true
11
- belongs_to :payment_method, class_name: 'Spree::PaymentMethod', inverse_of: :payments
11
+ belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
12
12
 
13
- has_many :offsets, -> { where("source_type = 'Spree::Payment' AND amount < 0 AND state = 'completed'") },
14
- class_name: "Spree::Payment", foreign_key: :source_id
13
+ has_many :offsets, -> { offset_payment }, class_name: "Spree::Payment", foreign_key: :source_id
15
14
  has_many :log_entries, as: :source
16
15
  has_many :state_changes, as: :stateful
17
16
  has_many :capture_events, :class_name => 'Spree::PaymentCaptureEvent'
17
+ has_many :refunds, inverse_of: :payment
18
18
 
19
- validates_presence_of :payment_method
20
19
  before_validation :validate_source
21
20
  before_create :set_unique_identifier
22
21
 
@@ -39,13 +38,23 @@ module Spree
39
38
 
40
39
  scope :from_credit_card, -> { where(source_type: 'Spree::CreditCard') }
41
40
  scope :with_state, ->(s) { where(state: s.to_s) }
41
+ # "offset" is reserved by activerecord
42
+ scope :offset_payment, -> { where("source_type = 'Spree::Payment' AND amount < 0 AND state = 'completed'") }
43
+
44
+ scope :checkout, -> { with_state('checkout') }
42
45
  scope :completed, -> { with_state('completed') }
43
46
  scope :pending, -> { with_state('pending') }
44
47
  scope :processing, -> { with_state('processing') }
45
48
  scope :failed, -> { with_state('failed') }
49
+
46
50
  scope :risky, -> { where("avs_response IN (?) OR (cvv_response_code IS NOT NULL and cvv_response_code != 'M') OR state = 'failed'", RISKY_AVS_CODES) }
47
51
  scope :valid, -> { where.not(state: %w(failed invalid)) }
48
52
 
53
+ # transaction_id is much easier to understand
54
+ def transaction_id
55
+ response_code
56
+ end
57
+
49
58
  def persist_invalid
50
59
  return unless ['failed', 'invalid'].include?(state)
51
60
  state_will_change!
@@ -115,7 +124,7 @@ module Spree
115
124
  end
116
125
 
117
126
  def credit_allowed
118
- amount - offsets_total.abs
127
+ amount - (offsets_total.abs + refunds.sum(:amount))
119
128
  end
120
129
 
121
130
  def can_credit?
@@ -158,10 +167,6 @@ module Spree
158
167
  amount - capture_events.sum(:amount)
159
168
  end
160
169
 
161
- def editable?
162
- checkout? || pending?
163
- end
164
-
165
170
  private
166
171
 
167
172
  def validate_source
@@ -179,8 +184,8 @@ module Spree
179
184
  end
180
185
 
181
186
  def create_payment_profile
182
- return unless source.respond_to?(:has_payment_profile?) && !source.has_payment_profile?
183
- return if %w(invalid failed).include?(state)
187
+ # Payment profile cannot be created without source
188
+ return unless source
184
189
  # Imported payments shouldn't create a payment profile.
185
190
  return if source.imported
186
191
 
@@ -190,15 +195,13 @@ module Spree
190
195
  end
191
196
 
192
197
  def invalidate_old_payments
193
- if state != 'invalid' and state != 'failed'
194
- order.payments.with_state('checkout').where("id != ?", self.id).each do |payment|
195
- payment.invalidate!
196
- end
198
+ order.payments.with_state('checkout').where("id != ?", self.id).each do |payment|
199
+ payment.invalidate!
197
200
  end
198
201
  end
199
202
 
200
203
  def update_order
201
- if completed? || void?
204
+ if self.completed?
202
205
  order.updater.update_payment_total
203
206
  end
204
207