Class: Spree::Order
- Inherits:
-
Base
- Object
- ActiveRecord::Base
- Base
- Spree::Order
show all
- Extended by:
- DisplayMoney
- Includes:
- Payments
- Defined in:
- app/models/spree/order.rb,
app/models/spree/order/payments.rb
Overview
The customers cart until completed, then acts as permanent record of the transaction.
‘Spree::Order` is the heart of the Solidus system, as it acts as the customer’s cart as they shop. Once an order is complete, it serves as the permanent record of their purchase. It has many responsibilities:
‘Spree::LineItem` as an ActiveRecord model.
‘checkout_allowed?` or `payment_required?`.
* Implements an interface for mutating the order with methods like
‘empty!` and `fulfill!`.
Defined Under Namespace
Modules: Payments
Classes: CannotRebuildShipments, InsufficientStock, NumberGenerator
Constant Summary
collapse
- ORDER_NUMBER_LENGTH =
9
- ORDER_NUMBER_LETTERS =
false
- ORDER_NUMBER_PREFIX =
'R'
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
money_methods
Methods included from Payments
#authorize_payments!, #capture_payments!, #process_payments!, #unprocessed_payments
Methods inherited from Base
display_includes
#generate_permalink, #save_permalink
Instance Attribute Details
#coupon_code ⇒ Object
Returns the value of attribute coupon_code.
68
69
70
|
# File 'app/models/spree/order.rb', line 68
def coupon_code
@coupon_code
end
|
#temporary_address ⇒ Object
Returns the value of attribute temporary_address.
69
70
71
|
# File 'app/models/spree/order.rb', line 69
def temporary_address
@temporary_address
end
|
#temporary_payment_source ⇒ Object
Returns the value of attribute temporary_payment_source.
71
72
73
|
# File 'app/models/spree/order.rb', line 71
def temporary_payment_source
@temporary_payment_source
end
|
#use_billing ⇒ Object
Returns the value of attribute use_billing.
143
144
145
|
# File 'app/models/spree/order.rb', line 143
def use_billing
@use_billing
end
|
#use_shipping ⇒ Object
Returns the value of attribute use_shipping.
144
145
146
|
# File 'app/models/spree/order.rb', line 144
def use_shipping
@use_shipping
end
|
Class Method Details
.by_customer(customer) ⇒ Object
179
180
181
|
# File 'app/models/spree/order.rb', line 179
def self.by_customer(customer)
joins(:user).where("#{Spree.user_class.table_name}.email" => customer)
end
|
.by_state(state) ⇒ Object
183
184
185
|
# File 'app/models/spree/order.rb', line 183
def self.by_state(state)
where(state: state)
end
|
.canceled ⇒ Object
195
196
197
|
# File 'app/models/spree/order.rb', line 195
def self.canceled
where(state: 'canceled')
end
|
.complete ⇒ Object
187
188
189
|
# File 'app/models/spree/order.rb', line 187
def self.complete
where.not(completed_at: nil)
end
|
.find_by_param(value) ⇒ Object
155
156
157
|
# File 'app/models/spree/order.rb', line 155
def self.find_by_param(value)
find_by number: value
end
|
.find_by_param!(value) ⇒ Object
159
160
161
|
# File 'app/models/spree/order.rb', line 159
def self.find_by_param!(value)
find_by! number: value
end
|
.incomplete ⇒ Object
191
192
193
|
# File 'app/models/spree/order.rb', line 191
def self.incomplete
where(completed_at: nil)
end
|
.not_canceled ⇒ Object
199
200
201
|
# File 'app/models/spree/order.rb', line 199
def self.not_canceled
where.not(state: 'canceled')
end
|
.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.
205
206
207
|
# File 'app/models/spree/order.rb', line 205
def self.register_line_item_comparison_hook(hook)
line_item_comparison_hooks.add(hook)
end
|
Instance Method Details
#add_default_payment_from_wallet ⇒ Object
691
692
693
694
695
696
697
698
699
700
701
702
703
|
# File 'app/models/spree/order.rb', line 691
def add_default_payment_from_wallet
builder = Spree::Config.default_payment_builder_class.new(self)
if payment = builder.build
payments << payment
if bill_address.nil?
self.bill_address = payment.source.try(:address) ||
user.bill_address
end
end
end
|
#add_payment_sources_to_wallet ⇒ Object
685
686
687
688
689
|
# File 'app/models/spree/order.rb', line 685
def add_payment_sources_to_wallet
Spree::Config.
add_payment_sources_to_wallet_class.new(self).
add_to_wallet
end
|
#add_store_credit_payments ⇒ Object
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
|
# File 'app/models/spree/order.rb', line 578
def add_store_credit_payments
return if user.nil?
return if payments.store_credits.checkout.empty? && user.available_store_credit_total(currency: currency).zero?
payments.store_credits.checkout.each(&:invalidate!)
authorized_total = payments.pending.sum(:amount)
remaining_total = outstanding_balance - authorized_total
matching_store_credits = user.store_credits.where(currency: currency)
if matching_store_credits.any?
payment_method = Spree::PaymentMethod::StoreCredit.first
sorter = Spree::Config.store_credit_prioritizer_class.new(matching_store_credits, self)
sorter.call.each do |credit|
break if remaining_total.zero?
next if credit.amount_remaining.zero?
amount_to_take = [credit.amount_remaining, remaining_total].min
payments.create!(source: credit,
payment_method: payment_method,
amount: amount_to_take,
state: 'checkout',
response_code: credit.generate_authorization_code)
remaining_total -= amount_to_take
end
end
other_payments = payments.checkout.not_store_credits
if remaining_total.zero?
other_payments.each(&:invalidate!)
elsif other_payments.size == 1
other_payments.first.update!(amount: remaining_total)
end
payments.reset
if payments.where(state: %w(checkout pending completed)).sum(:amount) != total
errors.add(:base, I18n.t('spree.store_credit.errors.unable_to_fund')) && (return false)
end
end
|
#all_inventory_units_returned? ⇒ Boolean
290
291
292
293
294
295
296
|
# File 'app/models/spree/order.rb', line 290
def all_inventory_units_returned?
inventory_units.reload.all?(&:returned?)
end
|
#allow_cancel? ⇒ Boolean
285
286
287
288
|
# File 'app/models/spree/order.rb', line 285
def allow_cancel?
return false unless completed? && state != 'canceled'
shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state)
end
|
#amount ⇒ Object
For compatiblity with Calculator::PriceSack
210
211
212
|
# File 'app/models/spree/order.rb', line 210
def amount
line_items.sum(&:amount)
end
|
511
512
513
514
|
# File 'app/models/spree/order.rb', line 511
def apply_shipping_promotions
Spree::Config.shipping_promotion_handler_class.new(self).activate
recalculate
end
|
#approved? ⇒ Boolean
558
559
560
|
# File 'app/models/spree/order.rb', line 558
def approved?
!!approved_at
end
|
#assign_billing_to_shipping_address ⇒ Object
275
276
277
278
|
# File 'app/models/spree/order.rb', line 275
def assign_billing_to_shipping_address
self.ship_address = bill_address if bill_address
true
end
|
#assign_default_user_addresses ⇒ Object
Note:
This doesn’t persist the change bill_address or ship_address
Assigns a default bill_address and ship_address to the order based on the associated user’s bill_address and ship_address.
667
668
669
670
671
672
673
674
675
676
677
|
# File 'app/models/spree/order.rb', line 667
def assign_default_user_addresses
if user
bill_address = user.bill_address
ship_address = user.ship_address
self.bill_address ||= bill_address if bill_address.try!(:valid?)
self.ship_address ||= ship_address if ship_address.try!(:valid?) && checkout_steps.include?("delivery")
end
end
|
#assign_shipping_to_billing_address ⇒ Object
280
281
282
283
|
# File 'app/models/spree/order.rb', line 280
def assign_shipping_to_billing_address
self.bill_address = ship_address if ship_address
true
end
|
#associate_user!(user, override_email = true) ⇒ Object
Associates the specified user with the order.
311
312
313
314
315
316
317
318
319
320
321
322
323
|
# File 'app/models/spree/order.rb', line 311
def associate_user!(user, override_email = true)
self.user = user
attrs_to_set = { user_id: user.try(:id) }
attrs_to_set[:email] = user.try(:email) if override_email
attrs_to_set[:created_by_id] = user.try(:id) if created_by.blank?
if persisted?
self.class.unscoped.where(id: id).update_all(attrs_to_set)
end
assign_attributes(attrs_to_set)
end
|
#available_payment_methods ⇒ Object
420
421
422
423
424
425
426
|
# File 'app/models/spree/order.rb', line 420
def available_payment_methods
@available_payment_methods ||= Spree::PaymentMethod
.active
.available_to_store(store)
.available_to_users
.order(:position)
end
|
#backordered? ⇒ Boolean
256
257
258
|
# File 'app/models/spree/order.rb', line 256
def backordered?
shipments.any?(&:backordered?)
end
|
#bill_address_attributes=(attributes) ⇒ Object
656
657
658
|
# File 'app/models/spree/order.rb', line 656
def bill_address_attributes=(attributes)
self.bill_address = Spree::Address.immutable_merge(bill_address, attributes)
end
|
#billing_address_required? ⇒ Boolean
488
489
490
|
# File 'app/models/spree/order.rb', line 488
def billing_address_required?
Spree::Config.billing_address_required
end
|
#can_add_coupon? ⇒ Boolean
#can_approve? ⇒ Boolean
562
563
564
|
# File 'app/models/spree/order.rb', line 562
def can_approve?
!approved?
end
|
#can_ship? ⇒ Boolean
395
396
397
|
# File 'app/models/spree/order.rb', line 395
def can_ship?
complete? || resumed? || awaiting_return? || returned?
end
|
#canceled_by(user) ⇒ Object
551
552
553
554
555
556
|
# File 'app/models/spree/order.rb', line 551
def canceled_by(user)
transaction do
cancel!
update_column(:canceler_id, user.id)
end
end
|
#cancellations ⇒ Object
306
307
308
|
# File 'app/models/spree/order.rb', line 306
def cancellations
@cancellations ||= Spree::Config.order_cancellations_class.new(self)
end
|
#check_shipments_and_restart_checkout ⇒ Object
Also known as:
ensure_updated_shipments
Clean shipments and make order back to address state (or to whatever state is set by restart_checkout_flow in case of state machine modifications)
518
519
520
521
522
523
524
|
# File 'app/models/spree/order.rb', line 518
def check_shipments_and_restart_checkout
if !completed? && shipments.all?(&:pending?)
shipments.destroy_all
update_column(:shipment_total, 0)
restart_checkout_flow
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.
247
248
249
|
# File 'app/models/spree/order.rb', line 247
def checkout_allowed?
line_items.count > 0
end
|
#completed? ⇒ Boolean
239
240
241
|
# File 'app/models/spree/order.rb', line 239
def completed?
completed_at.present?
end
|
#contains?(variant, options = {}) ⇒ Boolean
333
334
335
|
# File 'app/models/spree/order.rb', line 333
def contains?(variant, options = {})
find_line_item_by_variant(variant, options).present?
end
|
#contents ⇒ Object
298
299
300
|
# File 'app/models/spree/order.rb', line 298
def contents
@contents ||= Spree::Config.order_contents_class.new(self)
end
|
#covered_by_store_credit? ⇒ Boolean
Also known as:
covered_by_store_credit
625
626
627
628
|
# File 'app/models/spree/order.rb', line 625
def covered_by_store_credit?
return false unless user
user.available_store_credit_total(currency: currency) >= total
end
|
#create_proposed_shipments ⇒ Object
492
493
494
495
496
497
498
499
500
501
|
# File 'app/models/spree/order.rb', line 492
def create_proposed_shipments
if completed?
raise CannotRebuildShipments.new(I18n.t('spree.cannot_rebuild_shipments_order_completed'))
elsif shipments.any? { |shipment| !shipment.pending? }
raise CannotRebuildShipments.new(I18n.t('spree.cannot_rebuild_shipments_shipments_not_pending'))
else
shipments.destroy_all
shipments.push(*Spree::Config.stock.coordinator_class.new(self).shipments)
end
end
|
#create_shipments_for_line_item(line_item) ⇒ Object
503
504
505
506
507
508
509
|
# File 'app/models/spree/order.rb', line 503
def create_shipments_for_line_item(line_item)
units = Spree::Config.stock.inventory_unit_builder_class.new(self).missing_units_for_line_item(line_item)
Spree::Config.stock.coordinator_class.new(self, units).shipments.each do |shipment|
shipments << shipment
end
end
|
#credit_cards ⇒ Object
399
400
401
402
|
# File 'app/models/spree/order.rb', line 399
def credit_cards
credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq
Spree::CreditCard.where(id: credit_card_ids)
end
|
#currency ⇒ Object
227
228
229
|
# File 'app/models/spree/order.rb', line 227
def currency
self[:currency] || Spree::Config[:currency]
end
|
#display_store_credit_remaining_after_capture ⇒ Object
652
653
654
|
# File 'app/models/spree/order.rb', line 652
def display_store_credit_remaining_after_capture
Spree::Money.new(total_available_store_credit - total_applicable_store_credit, { currency: currency })
end
|
#display_total_applicable_store_credit ⇒ Object
648
649
650
|
# File 'app/models/spree/order.rb', line 648
def display_total_applicable_store_credit
Spree::Money.new(-total_applicable_store_credit, { currency: currency })
end
|
#empty! ⇒ Object
449
450
451
452
453
454
455
456
|
# File 'app/models/spree/order.rb', line 449
def empty!
line_items.destroy_all
adjustments.destroy_all
shipments.destroy_all
order_promotions.destroy_all
recalculate
end
|
#ensure_billing_address ⇒ Object
480
481
482
483
484
485
486
|
# File 'app/models/spree/order.rb', line 480
def ensure_billing_address
return unless billing_address_required?
return if bill_address&.valid?
errors.add(:base, I18n.t('spree.bill_address_required'))
false
end
|
#ensure_line_item_variants_are_not_deleted ⇒ Object
Check to see if any line item variants are soft, deleted. If so add error and restart checkout.
435
436
437
438
439
440
441
442
443
|
# File 'app/models/spree/order.rb', line 435
def ensure_line_item_variants_are_not_deleted
if line_items.any? { |li| li.variant.discarded? }
errors.add(:base, I18n.t('spree.deleted_variants_present'))
restart_checkout_flow
false
else
true
end
end
|
#ensure_shipping_address ⇒ Object
474
475
476
477
478
|
# File 'app/models/spree/order.rb', line 474
def ensure_shipping_address
unless ship_address && ship_address.valid?
errors.add(:base, I18n.t('spree.ship_address_required')) && (return false)
end
end
|
#find_line_item_by_variant(variant, options = {}) ⇒ Object
342
343
344
345
346
347
|
# File 'app/models/spree/order.rb', line 342
def find_line_item_by_variant(variant, options = {})
line_items.detect { |line_item|
line_item.variant_id == variant.id &&
line_item_options_match(line_item, options)
}
end
|
#fulfill! ⇒ Object
409
410
411
412
413
|
# File 'app/models/spree/order.rb', line 409
def fulfill!
shipments.each { |shipment| shipment.update_state if shipment.persisted? }
recalculator.update_shipment_state
save!
end
|
#generate_order_number ⇒ Object
325
326
327
|
# File 'app/models/spree/order.rb', line 325
def generate_order_number
self.number ||= Spree::Config.order_number_generator.generate
end
|
570
571
572
|
# File 'app/models/spree/order.rb', line 570
def has_non_reimbursement_related_refunds?
refunds.non_reimbursement.exists?
end
|
#insufficient_stock_lines ⇒ Object
428
429
430
|
# File 'app/models/spree/order.rb', line 428
def insufficient_stock_lines
line_items.select(&:insufficient_stock?)
end
|
#is_risky? ⇒ Boolean
547
548
549
|
# File 'app/models/spree/order.rb', line 547
def is_risky?
payments.risky.count > 0
end
|
#item_total_before_tax ⇒ Object
214
215
216
|
# File 'app/models/spree/order.rb', line 214
def item_total_before_tax
line_items.to_a.sum(&:total_before_tax)
end
|
#item_total_excluding_vat ⇒ Object
Sum of all line item amounts pre-tax
223
224
225
|
# File 'app/models/spree/order.rb', line 223
def item_total_excluding_vat
line_items.to_a.sum(&:total_excluding_vat)
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
358
359
360
361
362
363
364
|
# File 'app/models/spree/order.rb', line 358
def line_item_options_match(line_item, options)
return true unless options
line_item_comparison_hooks.all? { |hook|
send(hook, line_item, options)
}
end
|
#merge!(*args) ⇒ Object
445
446
447
|
# File 'app/models/spree/order.rb', line 445
def merge!(*args)
Spree::Config.order_merger_class.new(self).merge!(*args)
end
|
#name ⇒ Object
389
390
391
392
393
|
# File 'app/models/spree/order.rb', line 389
def name
if (address = bill_address || ship_address)
address.name
end
end
|
#order_total_after_store_credit ⇒ Object
636
637
638
|
# File 'app/models/spree/order.rb', line 636
def order_total_after_store_credit
total - total_applicable_store_credit
end
|
#outstanding_balance ⇒ Object
370
371
372
373
374
375
376
377
378
379
|
# File 'app/models/spree/order.rb', line 370
def outstanding_balance
if state == 'canceled'
-1 * payment_total
else
total - reimbursement_total - payment_total
end
end
|
#outstanding_balance? ⇒ Boolean
381
382
383
|
# File 'app/models/spree/order.rb', line 381
def outstanding_balance?
outstanding_balance != 0
end
|
#paid? ⇒ Boolean
Helper methods for checkout steps
416
417
418
|
# File 'app/models/spree/order.rb', line 416
def paid?
%w(paid credit_owed).include?(payment_state)
end
|
#payment_required? ⇒ Boolean
Is this a free order in which case the payment step should be skipped
252
253
254
|
# File 'app/models/spree/order.rb', line 252
def payment_required?
total > 0
end
|
#payments_attributes=(attributes) ⇒ Object
713
714
715
716
|
# File 'app/models/spree/order.rb', line 713
def payments_attributes=(attributes)
validate_payments_attributes(attributes)
super(attributes)
end
|
#persist_user_address! ⇒ Object
679
680
681
682
683
|
# File 'app/models/spree/order.rb', line 679
def persist_user_address!
if !temporary_address && user && user.respond_to?(:persist_order_address) && bill_address_id
user.persist_order_address(self)
end
end
|
#quantity ⇒ Object
566
567
568
|
# File 'app/models/spree/order.rb', line 566
def quantity
line_items.sum(:quantity)
end
|
#quantity_of(variant, options = {}) ⇒ Object
337
338
339
340
|
# File 'app/models/spree/order.rb', line 337
def quantity_of(variant, options = {})
line_item = find_line_item_by_variant(variant, options)
line_item ? line_item.quantity : 0
end
|
#recalculator ⇒ Object
Also known as:
updater
269
270
271
|
# File 'app/models/spree/order.rb', line 269
def recalculator
@recalculator ||= Spree::Config.order_recalculator_class.new(self)
end
|
#record_ip_address(ip_address) ⇒ Object
705
706
707
708
709
710
711
|
# File 'app/models/spree/order.rb', line 705
def record_ip_address(ip_address)
if new_record?
self.last_ip_address = ip_address
elsif last_ip_address != ip_address
update_column(:last_ip_address, ip_address)
end
end
|
#refresh_shipment_rates ⇒ Object
539
540
541
|
# File 'app/models/spree/order.rb', line 539
def refresh_shipment_rates
shipments.map(&:refresh_rates)
end
|
#refund_total ⇒ Object
385
386
387
|
# File 'app/models/spree/order.rb', line 385
def refund_total
refunds.sum(&:amount)
end
|
#reimbursement_total ⇒ Object
366
367
368
|
# File 'app/models/spree/order.rb', line 366
def reimbursement_total
reimbursements.sum(:total)
end
|
#restart_checkout_flow ⇒ Object
529
530
531
532
533
534
535
536
537
|
# File 'app/models/spree/order.rb', line 529
def restart_checkout_flow
return if state == 'cart'
update_columns(
state: 'cart',
updated_at: Time.current
)
self.next if line_items.any?
end
|
#ship_address_attributes=(attributes) ⇒ Object
660
661
662
|
# File 'app/models/spree/order.rb', line 660
def ship_address_attributes=(attributes)
self.ship_address = Spree::Address.immutable_merge(ship_address, attributes)
end
|
#shipment_total_before_tax ⇒ Object
218
219
220
|
# File 'app/models/spree/order.rb', line 218
def shipment_total_before_tax
shipments.to_a.sum(&:total_before_tax)
end
|
#shipped? ⇒ Boolean
470
471
472
|
# File 'app/models/spree/order.rb', line 470
def shipped?
%w(partial shipped).include?(shipment_state)
end
|
#shipped_shipments ⇒ Object
329
330
331
|
# File 'app/models/spree/order.rb', line 329
def shipped_shipments
shipments.shipped
end
|
#shipping ⇒ Object
302
303
304
|
# File 'app/models/spree/order.rb', line 302
def shipping
@shipping ||= Spree::Config.order_shipping_class.new(self)
end
|
#shipping_discount ⇒ Object
231
232
233
|
# File 'app/models/spree/order.rb', line 231
def shipping_discount
shipment_adjustments.credit.eligible.sum(:amount) * - 1
end
|
#shipping_eq_billing_address? ⇒ Boolean
543
544
545
|
# File 'app/models/spree/order.rb', line 543
def shipping_eq_billing_address?
bill_address == ship_address
end
|
#tax_address ⇒ Object
Returns the address for taxation based on configuration
261
262
263
264
265
266
267
|
# File 'app/models/spree/order.rb', line 261
def tax_address
if Spree::Config[:tax_using_ship_address]
ship_address
else
bill_address
end || store&.default_cart_tax_location
end
|
#tax_total ⇒ Object
574
575
576
|
# File 'app/models/spree/order.rb', line 574
def tax_total
additional_tax_total + included_tax_total
end
|
#to_param ⇒ Object
235
236
237
|
# File 'app/models/spree/order.rb', line 235
def to_param
number
end
|
#total_applicable_store_credit ⇒ Object
640
641
642
643
644
645
646
|
# File 'app/models/spree/order.rb', line 640
def total_applicable_store_credit
if can_complete? || complete?
valid_store_credit_payments.to_a.sum(&:amount)
else
[total, user.try(:available_store_credit_total, currency: currency) || 0.0].min
end
end
|
#total_available_store_credit ⇒ Object
631
632
633
634
|
# File 'app/models/spree/order.rb', line 631
def total_available_store_credit
return 0.0 unless user
user.available_store_credit_total(currency: currency)
end
|
#valid_credit_cards ⇒ Object
404
405
406
407
|
# File 'app/models/spree/order.rb', line 404
def valid_credit_cards
credit_card_ids = payments.from_credit_card.valid.pluck(:source_id).uniq
Spree::CreditCard.where(id: credit_card_ids)
end
|
#validate_payments_attributes(attributes) ⇒ Object
718
719
720
721
722
723
724
725
726
727
|
# File 'app/models/spree/order.rb', line 718
def validate_payments_attributes(attributes)
attributes = Array(attributes)
attributes.each do |payment_attributes|
payment_method_id = payment_attributes[:payment_method_id]
available_payment_methods.find(payment_method_id) if payment_method_id
end
end
|