Class: Spree::Order
Defined Under Namespace
Modules: Checkout, CurrencyUpdater, Payments, StoreCredit
Constant Summary
collapse
- PAYMENT_STATES =
%w(balance_due credit_owed failed paid void)
- SHIPMENT_STATES =
%w(backorder canceled partial pending ready shipped)
- MONEY_THRESHOLD =
100_000_000
- MONEY_VALIDATION =
{
presence: true,
numericality: {
greater_than: -MONEY_THRESHOLD,
less_than: MONEY_THRESHOLD,
allow_blank: true
},
format: { with: /\A-?\d+(?:\.\d{1,2})?\z/, allow_blank: true }
}.freeze
- POSITIVE_MONEY_VALIDATION =
MONEY_VALIDATION.deep_dup.tap do |validation|
validation.fetch(:numericality)[:greater_than_or_equal_to] = 0
end.freeze
- NEGATIVE_MONEY_VALIDATION =
MONEY_VALIDATION.deep_dup.tap do |validation|
validation.fetch(:numericality)[:less_than_or_equal_to] = 0
end.freeze
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
money_methods
#to_param
#generate_token
#add_store_credit_payments, #could_use_store_credit?, #covered_by_store_credit?, #display_order_total_after_store_credit, #display_store_credit_remaining_after_capture, #display_total_applicable_store_credit, #display_total_applied_store_credit, #display_total_available_store_credit, #order_total_after_store_credit, #remove_store_credit_payments, #total_applicable_store_credit, #total_applied_store_credit, #total_available_store_credit, #using_store_credit?
#price_from_line_item, #update_line_item_currencies!, #update_line_item_price!
Methods included from Checkout
included
Methods inherited from Base
belongs_to_required_by_default, page, spree_base_scopes
#clear_preferences, #default_preferences, #defined_preferences, #get_preference, #has_preference!, #has_preference?, #preference_default, #preference_type, #set_preference
Instance Attribute Details
#coupon_code ⇒ Object
Returns the value of attribute coupon_code.
80
81
82
|
# File 'app/models/spree/order.rb', line 80
def coupon_code
@coupon_code
end
|
#temporary_address ⇒ Object
Returns the value of attribute temporary_address.
81
82
83
|
# File 'app/models/spree/order.rb', line 81
def temporary_address
@temporary_address
end
|
#temporary_credit_card ⇒ Object
Returns the value of attribute temporary_credit_card.
81
82
83
|
# File 'app/models/spree/order.rb', line 81
def temporary_credit_card
@temporary_credit_card
end
|
#use_billing ⇒ Object
Returns the value of attribute use_billing.
144
145
146
|
# File 'app/models/spree/order.rb', line 144
def use_billing
@use_billing
end
|
Class Method Details
.register_line_item_comparison_hook(hook) ⇒ Object
Use this method in other gems that wish to register their own custom logic that should be called when determining if two line items are equal.
191
192
193
194
195
196
197
198
|
# File 'app/models/spree/order.rb', line 191
def self.register_line_item_comparison_hook(hook)
ActiveSupport::Deprecation.warn(<<-EOS, caller)
Order.register_line_item_comparison_hook is deprecated and will be removed in Spree 4.0. Please use
`Rails.application.config.spree.line_item_comparison_hooks << hook` instead.
EOS
Rails.application.config.spree.line_item_comparison_hooks << hook
end
|
.register_update_hook(hook) ⇒ Object
Use this method in other gems that wish to register their own custom logic that should be called after Order#update
185
186
187
|
# File 'app/models/spree/order.rb', line 185
def self.register_update_hook(hook)
update_hooks.add(hook)
end
|
Instance Method Details
#all_inventory_units_returned? ⇒ Boolean
287
288
289
|
# File 'app/models/spree/order.rb', line 287
def all_inventory_units_returned?
inventory_units.all?(&:returned?)
end
|
#allow_cancel? ⇒ Boolean
281
282
283
284
285
|
# File 'app/models/spree/order.rb', line 281
def allow_cancel?
return false if !completed? || canceled?
shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state)
end
|
#amount ⇒ Object
For compatiblity with Calculator::PriceSack
201
202
203
|
# File 'app/models/spree/order.rb', line 201
def amount
line_items.inject(0.0) { |sum, li| sum + li.amount }
end
|
#approve! ⇒ Object
621
622
623
|
# File 'app/models/spree/order.rb', line 621
def approve!
update_column(:considered_risky, false)
end
|
#approved? ⇒ Boolean
605
606
607
|
# File 'app/models/spree/order.rb', line 605
def approved?
!!approved_at
end
|
#approved_by(user) ⇒ Object
595
596
597
598
599
600
601
602
603
|
# File 'app/models/spree/order.rb', line 595
def approved_by(user)
transaction do
approve!
update_columns(
approver_id: user.id,
approved_at: Time.current
)
end
end
|
#associate_user!(user, override_email = true) ⇒ Object
Associates the specified user with the order.
296
297
298
299
300
301
302
303
304
305
306
307
308
|
# File 'app/models/spree/order.rb', line 296
def associate_user!(user, override_email = true)
self.user = user
self.email = user.email if override_email
self.created_by ||= user
self.bill_address ||= user.bill_address.try(:clone)
self.ship_address ||= user.ship_address.try(:clone)
changes = slice(:user_id, :email, :created_by_id, :bill_address_id, :ship_address_id)
self.class.unscoped.where(id: self).update_all(changes)
end
|
#available_payment_methods ⇒ Object
435
436
437
|
# File 'app/models/spree/order.rb', line 435
def available_payment_methods
@available_payment_methods ||= collect_payment_methods
end
|
#backordered? ⇒ Boolean
241
242
243
|
# File 'app/models/spree/order.rb', line 241
def backordered?
shipments.any?(&:backordered?)
end
|
#can_add_coupon? ⇒ Boolean
#can_approve? ⇒ Boolean
609
610
611
|
# File 'app/models/spree/order.rb', line 609
def can_approve?
!approved?
end
|
#can_ship? ⇒ Boolean
381
382
383
|
# File 'app/models/spree/order.rb', line 381
def can_ship?
complete? || resumed? || awaiting_return? || returned?
end
|
#canceled_by(user) ⇒ Object
585
586
587
588
589
590
591
592
593
|
# File 'app/models/spree/order.rb', line 585
def canceled_by(user)
transaction do
cancel!
update_columns(
canceler_id: user.id,
canceled_at: Time.current
)
end
end
|
#checkout_allowed? ⇒ Boolean
Indicates whether or not the user is allowed to proceed to checkout. Currently this is implemented as a check for whether or not there is at least one LineItem in the Order. Feel free to override this logic in your own application if you require additional steps before allowing a checkout.
222
223
224
|
# File 'app/models/spree/order.rb', line 222
def checkout_allowed?
line_items.exists?
end
|
#clone_billing_address ⇒ Object
268
269
270
271
272
273
274
275
|
# File 'app/models/spree/order.rb', line 268
def clone_billing_address
if bill_address && ship_address.nil?
self.ship_address = bill_address.clone
else
ship_address.attributes = bill_address.attributes.except('id', 'updated_at', 'created_at')
end
true
end
|
#collect_backend_payment_methods ⇒ Object
643
644
645
|
# File 'app/models/spree/order.rb', line 643
def collect_backend_payment_methods
PaymentMethod.available_on_back_end.select { |pm| pm.available_for_order?(self) }
end
|
#completed? ⇒ Boolean
214
215
216
|
# File 'app/models/spree/order.rb', line 214
def completed?
completed_at.present?
end
|
#confirmation_required? ⇒ Boolean
If true, causes the confirmation step to happen during the checkout process
232
233
234
235
236
237
238
239
|
# File 'app/models/spree/order.rb', line 232
def confirmation_required?
Spree::Config[:always_include_confirm_step] ||
payments.valid.map(&:payment_method).compact.any?(&:payment_profiles_supported?) ||
confirm?
end
|
#consider_risk ⇒ Object
613
614
615
|
# File 'app/models/spree/order.rb', line 613
def consider_risk
considered_risky! if is_risky? && !approved?
end
|
#considered_risky! ⇒ Object
617
618
619
|
# File 'app/models/spree/order.rb', line 617
def considered_risky!
update_column(:considered_risky, true)
end
|
#contents ⇒ Object
291
292
293
|
# File 'app/models/spree/order.rb', line 291
def contents
@contents ||= Spree::OrderContents.new(self)
end
|
#create_proposed_shipments ⇒ Object
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
|
# File 'app/models/spree/order.rb', line 524
def create_proposed_shipments
all_adjustments.shipping.delete_all
shipment_ids = shipments.map(&:id)
StateChange.where(stateful_type: 'Spree::Shipment', stateful_id: shipment_ids).delete_all
ShippingRate.where(shipment_id: shipment_ids).delete_all
shipments.delete_all
inventory_units.on_hand_or_backordered.delete_all
self.shipments = Spree::Stock::Coordinator.new(self).shipments
end
|
#create_shipment_tax_charge! ⇒ Object
348
349
350
|
# File 'app/models/spree/order.rb', line 348
def create_shipment_tax_charge!
Spree::TaxRate.adjust(self, shipments) if shipments.any?
end
|
#create_tax_charge! ⇒ Object
Creates new tax charges if there are any applicable rates. If prices already include taxes then price adjustments are created instead.
343
344
345
346
|
# File 'app/models/spree/order.rb', line 343
def create_tax_charge!
Spree::TaxRate.adjust(self, line_items)
Spree::TaxRate.adjust(self, shipments) if shipments.any?
end
|
#credit_cards ⇒ Object
385
386
387
388
|
# File 'app/models/spree/order.rb', line 385
def credit_cards
credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq
CreditCard.where(id: credit_card_ids)
end
|
#deliver_order_confirmation_email ⇒ Object
425
426
427
428
|
# File 'app/models/spree/order.rb', line 425
def deliver_order_confirmation_email
OrderMailer.confirm_email(id).deliver_later
update_column(:confirmation_delivered, true)
end
|
#empty! ⇒ Object
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
|
# File 'app/models/spree/order.rb', line 466
def empty!
if completed?
raise Spree.t(:cannot_empty_completed_order)
else
line_items.destroy_all
updater.update_item_count
adjustments.destroy_all
shipments.destroy_all
state_changes.destroy_all
order_promotions.destroy_all
update_totals
persist_totals
restart_checkout_flow
self
end
end
|
#ensure_line_item_variants_are_not_discontinued ⇒ Object
Check to see if any line item variants are discontinued. If so add error and restart checkout.
446
447
448
449
450
451
452
453
454
|
# File 'app/models/spree/order.rb', line 446
def ensure_line_item_variants_are_not_discontinued
if line_items.any? { |li| !li.variant || li.variant.discontinued? }
restart_checkout_flow
errors.add(:base, Spree.t(:discontinued_variants_present))
false
else
true
end
end
|
#ensure_line_items_are_in_stock ⇒ Object
456
457
458
459
460
461
462
463
464
|
# File 'app/models/spree/order.rb', line 456
def ensure_line_items_are_in_stock
if insufficient_stock_lines.present?
restart_checkout_flow
errors.add(:base, Spree.t(:insufficient_stock_lines_present))
false
else
true
end
end
|
#ensure_store_presence ⇒ Object
277
278
279
|
# File 'app/models/spree/order.rb', line 277
def ensure_store_presence
self.store ||= Spree::Store.default
end
|
#ensure_updated_shipments ⇒ Object
Clean shipments and make order back to address state
At some point the might need to force the order to transition from address to delivery again so that proper updated shipments are created. e.g. customer goes back from payment step and changes order items
551
552
553
554
555
556
557
|
# File 'app/models/spree/order.rb', line 551
def ensure_updated_shipments
if shipments.any? && !completed?
shipments.destroy_all
update_column(:shipment_total, 0)
restart_checkout_flow
end
end
|
#finalize! ⇒ Object
Finalizes an in progress order after checkout is complete. Called after transition to complete state when payments will have been processed
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
|
# File 'app/models/spree/order.rb', line 397
def finalize!
all_adjustments.each(&:close)
updater.update_payment_state
shipments.each do |shipment|
shipment.update!(self)
shipment.finalize!
end
updater.update_shipment_state
save!
updater.run_hooks
touch :completed_at
deliver_order_confirmation_email unless confirmation_delivered?
consider_risk
end
|
#find_line_item_by_variant(variant, options = {}) ⇒ Object
315
316
317
318
319
320
|
# File 'app/models/spree/order.rb', line 315
def find_line_item_by_variant(variant, options = {})
line_items.detect do |line_item|
line_item.variant_id == variant.id &&
line_item_options_match(line_item, options)
end
end
|
#fulfill! ⇒ Object
419
420
421
422
423
|
# File 'app/models/spree/order.rb', line 419
def fulfill!
shipments.each { |shipment| shipment.update!(self) if shipment.persisted? }
updater.update_shipment_state
save!
end
|
#fully_discounted? ⇒ Boolean
Also known as:
fully_discounted
determines whether the inventory is fully discounted
Returns
652
653
654
|
# File 'app/models/spree/order.rb', line 652
def fully_discounted?
adjustment_total + line_items.map(&:final_amount).sum == 0.0
end
|
#guest_token ⇒ Object
25
26
27
28
29
30
31
|
# File 'app/models/spree/order.rb', line 25
def guest_token
ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
Order#guest_token is deprecated and will be removed in Spree 4.0. Please use Order#token instead
DEPRECATION
token
end
|
#guest_token=(value) ⇒ Object
41
42
43
44
45
46
47
|
# File 'app/models/spree/order.rb', line 41
def guest_token=(value)
ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
Order#guest_token= is deprecated and will be removed in Spree 4.0. Please use Order#token= instead
DEPRECATION
self.token = value
end
|
#guest_token? ⇒ Boolean
33
34
35
36
37
38
39
|
# File 'app/models/spree/order.rb', line 33
def guest_token?
ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
Order#guest_token? is deprecated and will be removed in Spree 4.0. Please use Order#token? instead
DEPRECATION
token?
end
|
638
639
640
641
|
# File 'app/models/spree/order.rb', line 638
def has_non_reimbursement_related_refunds?
refunds.non_reimbursement.exists? ||
payments.offset_payment.exists? end
|
#has_step?(step) ⇒ Boolean
484
485
486
|
# File 'app/models/spree/order.rb', line 484
def has_step?(step)
checkout_steps.include?(step)
end
|
#insufficient_stock_lines ⇒ Object
439
440
441
|
# File 'app/models/spree/order.rb', line 439
def insufficient_stock_lines
line_items.select(&:insufficient_stock?)
end
|
#is_risky? ⇒ Boolean
581
582
583
|
# File 'app/models/spree/order.rb', line 581
def is_risky?
!payments.risky.empty?
end
|
#line_item_options_match(line_item, options) ⇒ Object
This method enables extensions to participate in the “Are these line items equal” decision.
When adding to cart, an extension would send something like: params=…
and would provide:
def product_customizations_match
331
332
333
334
335
336
337
338
339
|
# File 'app/models/spree/order.rb', line 331
def line_item_options_match(line_item, options)
ActiveSupport::Deprecation.warn(<<-EOS, caller)
Order#add is deprecated and will be removed in Spree 4.0. Please use
Spree::CompareLineItems service instead.
EOS
return true unless options
Spree::Dependencies.cart_compare_line_items_service.constantize.new.call(order: self, line_item: line_item, options: options).value
end
|
#log_state_changes(state_name:, old_state:, new_state:) ⇒ Object
499
500
501
502
503
504
505
506
|
# File 'app/models/spree/order.rb', line 499
def log_state_changes(state_name:, old_state:, new_state:)
state_changes.create(
previous_state: old_state,
next_state: new_state,
name: state_name,
user_id: user_id
)
end
|
#merger ⇒ Object
264
265
266
|
# File 'app/models/spree/order.rb', line 264
def merger
@merger ||= Spree::OrderMerger.new(self)
end
|
#name ⇒ Object
375
376
377
378
379
|
# File 'app/models/spree/order.rb', line 375
def name
if (address = bill_address || ship_address)
address.full_name
end
end
|
#outstanding_balance ⇒ Object
359
360
361
362
363
364
365
366
367
368
369
|
# File 'app/models/spree/order.rb', line 359
def outstanding_balance
if canceled?
-1 * payment_total
elsif refunds.exists?
total - (payment_total + refunds.sum(:amount))
else
total - payment_total
end
end
|
#outstanding_balance? ⇒ Boolean
371
372
373
|
# File 'app/models/spree/order.rb', line 371
def outstanding_balance?
outstanding_balance != 0
end
|
#paid? ⇒ Boolean
Helper methods for checkout steps
431
432
433
|
# File 'app/models/spree/order.rb', line 431
def paid?
payment_state == 'paid' || payment_state == 'credit_owed'
end
|
#payment_required? ⇒ Boolean
Is this a free order in which case the payment step should be skipped
227
228
229
|
# File 'app/models/spree/order.rb', line 227
def payment_required?
total.to_f > 0.0
end
|
#payments_attributes=(attributes) ⇒ Object
661
662
663
664
|
# File 'app/models/spree/order.rb', line 661
def payments_attributes=(attributes)
validate_payments_attributes(attributes)
super(attributes)
end
|
#pre_tax_item_amount ⇒ Object
Sum of all line item amounts pre-tax
206
207
208
|
# File 'app/models/spree/order.rb', line 206
def pre_tax_item_amount
line_items.to_a.sum(&:pre_tax_amount)
end
|
657
658
659
|
# File 'app/models/spree/order.rb', line 657
def promo_code
promotions.pluck(:code).compact.first
end
|
#quantity ⇒ Object
634
635
636
|
# File 'app/models/spree/order.rb', line 634
def quantity
line_items.sum(:quantity)
end
|
#quantity_of(variant, options = {}) ⇒ Object
310
311
312
313
|
# File 'app/models/spree/order.rb', line 310
def quantity_of(variant, options = {})
line_item = find_line_item_by_variant(variant, options)
line_item ? line_item.quantity : 0
end
|
#refresh_shipment_rates(shipping_method_filter = ShippingMethod::DISPLAY_ON_FRONT_END) ⇒ Object
567
568
569
|
# File 'app/models/spree/order.rb', line 567
def refresh_shipment_rates(shipping_method_filter = ShippingMethod::DISPLAY_ON_FRONT_END)
shipments.map { |s| s.refresh_rates(shipping_method_filter) }
end
|
#reload(options = nil) ⇒ Object
625
626
627
628
|
# File 'app/models/spree/order.rb', line 625
def reload(options = nil)
remove_instance_variable(:@tax_zone) if defined?(@tax_zone)
super
end
|
#restart_checkout_flow ⇒ Object
559
560
561
562
563
564
565
|
# File 'app/models/spree/order.rb', line 559
def restart_checkout_flow
update_columns(
state: 'cart',
updated_at: Time.current
)
next! unless line_items.empty?
end
|
#set_shipments_cost ⇒ Object
575
576
577
578
579
|
# File 'app/models/spree/order.rb', line 575
def set_shipments_cost
shipments.each(&:update_amounts)
updater.update_shipment_total
persist_totals
end
|
#shipped? ⇒ Boolean
520
521
522
|
# File 'app/models/spree/order.rb', line 520
def shipped?
%w(partial shipped).include?(shipment_state)
end
|
#shipping_discount ⇒ Object
210
211
212
|
# File 'app/models/spree/order.rb', line 210
def shipping_discount
shipment_adjustments.non_tax.eligible.sum(:amount) * - 1
end
|
#shipping_eq_billing_address? ⇒ Boolean
571
572
573
|
# File 'app/models/spree/order.rb', line 571
def shipping_eq_billing_address?
bill_address == ship_address
end
|
#state_changed(name) ⇒ Object
488
489
490
491
492
493
494
495
496
497
|
# File 'app/models/spree/order.rb', line 488
def state_changed(name)
state = "#{name}_state"
if persisted?
old_state = send("#{state}_was")
new_state = send(state)
unless old_state == new_state
log_state_changes(state_name: name, old_state: old_state, new_state: new_state)
end
end
end
|
#tax_address ⇒ Object
Returns the address for taxation based on configuration
252
253
254
|
# File 'app/models/spree/order.rb', line 252
def tax_address
Spree::Config[:tax_using_ship_address] ? ship_address : bill_address
end
|
#tax_total ⇒ Object
630
631
632
|
# File 'app/models/spree/order.rb', line 630
def tax_total
included_tax_total + additional_tax_total
end
|
#tax_zone ⇒ Object
Returns the relevant zone (if any) to be used for taxation purposes. Uses default tax zone unless there is a specific match
247
248
249
|
# File 'app/models/spree/order.rb', line 247
def tax_zone
@tax_zone ||= Zone.match(tax_address) || Zone.default_tax
end
|
#update_line_item_prices! ⇒ Object
352
353
354
355
356
357
|
# File 'app/models/spree/order.rb', line 352
def update_line_item_prices!
transaction do
line_items.reload.each(&:update_price)
save!
end
end
|
#update_with_updater! ⇒ Object
260
261
262
|
# File 'app/models/spree/order.rb', line 260
def update_with_updater!
updater.update
end
|
#updater ⇒ Object
256
257
258
|
# File 'app/models/spree/order.rb', line 256
def updater
@updater ||= OrderUpdater.new(self)
end
|
#valid_credit_cards ⇒ Object
390
391
392
393
|
# File 'app/models/spree/order.rb', line 390
def valid_credit_cards
credit_card_ids = payments.from_credit_card.valid.pluck(:source_id).uniq
CreditCard.where(id: credit_card_ids)
end
|
#validate_payments_attributes(attributes) ⇒ Object
666
667
668
669
670
671
672
673
674
675
|
# File 'app/models/spree/order.rb', line 666
def validate_payments_attributes(attributes)
payment_methods = Spree::PaymentMethod.where(id: available_payment_methods.map(&:id))
attributes.each do |payment_attributes|
payment_method_id = payment_attributes[:payment_method_id]
payment_methods.find(payment_method_id) if payment_method_id
end
end
|