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.
141
142
143
|
# File 'app/models/spree/order.rb', line 141
def use_billing
@use_billing
end
|
#use_shipping ⇒ Object
Returns the value of attribute use_shipping.
142
143
144
|
# File 'app/models/spree/order.rb', line 142
def use_shipping
@use_shipping
end
|
Class Method Details
.by_customer(customer) ⇒ Object
177
178
179
|
# File 'app/models/spree/order.rb', line 177
def self.by_customer(customer)
joins(:user).where("#{Spree.user_class.table_name}.email" => customer)
end
|
.by_state(state) ⇒ Object
181
182
183
|
# File 'app/models/spree/order.rb', line 181
def self.by_state(state)
where(state:)
end
|
.canceled ⇒ Object
193
194
195
|
# File 'app/models/spree/order.rb', line 193
def self.canceled
where(state: 'canceled')
end
|
.complete ⇒ Object
185
186
187
|
# File 'app/models/spree/order.rb', line 185
def self.complete
where.not(completed_at: nil)
end
|
.find_by_param(value) ⇒ Object
153
154
155
|
# File 'app/models/spree/order.rb', line 153
def self.find_by_param(value)
find_by number: value
end
|
.find_by_param!(value) ⇒ Object
157
158
159
|
# File 'app/models/spree/order.rb', line 157
def self.find_by_param!(value)
find_by! number: value
end
|
.incomplete ⇒ Object
189
190
191
|
# File 'app/models/spree/order.rb', line 189
def self.incomplete
where(completed_at: nil)
end
|
.not_canceled ⇒ Object
197
198
199
|
# File 'app/models/spree/order.rb', line 197
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.
203
204
205
|
# File 'app/models/spree/order.rb', line 203
def self.register_line_item_comparison_hook(hook)
line_item_comparison_hooks.add(hook)
end
|
Instance Method Details
#add_default_payment_from_wallet ⇒ Object
684
685
686
687
688
689
690
691
692
693
694
695
696
|
# File 'app/models/spree/order.rb', line 684
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
678
679
680
681
682
|
# File 'app/models/spree/order.rb', line 678
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
571
572
573
574
575
576
577
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
|
# File 'app/models/spree/order.rb', line 571
def add_store_credit_payments
return if user.nil?
return if payments.store_credits.checkout.empty? && user.available_store_credit_total(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:)
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:,
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
288
289
290
291
292
293
294
|
# File 'app/models/spree/order.rb', line 288
def all_inventory_units_returned?
inventory_units.reload.all?(&:returned?)
end
|
#allow_cancel? ⇒ Boolean
283
284
285
286
|
# File 'app/models/spree/order.rb', line 283
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
208
209
210
|
# File 'app/models/spree/order.rb', line 208
def amount
line_items.sum(&:amount)
end
|
#approved? ⇒ Boolean
551
552
553
|
# File 'app/models/spree/order.rb', line 551
def approved?
!!approved_at
end
|
#assign_billing_to_shipping_address ⇒ Object
273
274
275
276
|
# File 'app/models/spree/order.rb', line 273
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.
660
661
662
663
664
665
666
667
668
669
670
|
# File 'app/models/spree/order.rb', line 660
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
278
279
280
281
|
# File 'app/models/spree/order.rb', line 278
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.
309
310
311
312
313
314
315
316
317
318
319
320
321
|
# File 'app/models/spree/order.rb', line 309
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:).update_all(attrs_to_set)
end
assign_attributes(attrs_to_set)
end
|
#available_payment_methods ⇒ Object
418
419
420
421
422
423
424
|
# File 'app/models/spree/order.rb', line 418
def available_payment_methods
@available_payment_methods ||= Spree::PaymentMethod
.active
.available_to_store(store)
.available_to_users
.order(:position)
end
|
#backordered? ⇒ Boolean
254
255
256
|
# File 'app/models/spree/order.rb', line 254
def backordered?
shipments.any?(&:backordered?)
end
|
#bill_address_attributes=(attributes) ⇒ Object
649
650
651
|
# File 'app/models/spree/order.rb', line 649
def bill_address_attributes=(attributes)
self.bill_address = Spree::Address.immutable_merge(bill_address, attributes)
end
|
#billing_address_required? ⇒ Boolean
486
487
488
|
# File 'app/models/spree/order.rb', line 486
def billing_address_required?
Spree::Config.billing_address_required
end
|
#can_add_coupon? ⇒ Boolean
464
465
466
|
# File 'app/models/spree/order.rb', line 464
def can_add_coupon?
Spree::Config.promotions.coupon_code_handler_class.new(self).can_apply?
end
|
#can_approve? ⇒ Boolean
555
556
557
|
# File 'app/models/spree/order.rb', line 555
def can_approve?
!approved?
end
|
#can_ship? ⇒ Boolean
393
394
395
|
# File 'app/models/spree/order.rb', line 393
def can_ship?
complete? || resumed? || awaiting_return? || returned?
end
|
#canceled_by(user) ⇒ Object
544
545
546
547
548
549
|
# File 'app/models/spree/order.rb', line 544
def canceled_by(user)
transaction do
cancel!
update_column(:canceler_id, user.id)
end
end
|
#cancellations ⇒ Object
304
305
306
|
# File 'app/models/spree/order.rb', line 304
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)
511
512
513
514
515
516
517
|
# File 'app/models/spree/order.rb', line 511
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.
245
246
247
|
# File 'app/models/spree/order.rb', line 245
def checkout_allowed?
line_items.count > 0
end
|
#completed? ⇒ Boolean
237
238
239
|
# File 'app/models/spree/order.rb', line 237
def completed?
completed_at.present?
end
|
#contains?(variant, options = {}) ⇒ Boolean
331
332
333
|
# File 'app/models/spree/order.rb', line 331
def contains?(variant, options = {})
find_line_item_by_variant(variant, options).present?
end
|
#contents ⇒ Object
296
297
298
|
# File 'app/models/spree/order.rb', line 296
def contents
@contents ||= Spree::Config.order_contents_class.new(self)
end
|
#covered_by_store_credit? ⇒ Boolean
Also known as:
covered_by_store_credit
618
619
620
621
|
# File 'app/models/spree/order.rb', line 618
def covered_by_store_credit?
return false unless user
user.available_store_credit_total(currency:) >= total
end
|
#create_proposed_shipments ⇒ Object
490
491
492
493
494
495
496
497
498
499
|
# File 'app/models/spree/order.rb', line 490
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
501
502
503
504
505
506
507
|
# File 'app/models/spree/order.rb', line 501
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
397
398
399
400
|
# File 'app/models/spree/order.rb', line 397
def credit_cards
credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq
Spree::CreditCard.where(id: credit_card_ids)
end
|
#currency ⇒ Object
225
226
227
|
# File 'app/models/spree/order.rb', line 225
def currency
self[:currency] || Spree::Config[:currency]
end
|
#display_store_credit_remaining_after_capture ⇒ Object
645
646
647
|
# File 'app/models/spree/order.rb', line 645
def display_store_credit_remaining_after_capture
Spree::Money.new(total_available_store_credit - total_applicable_store_credit, { currency: })
end
|
#display_total_applicable_store_credit ⇒ Object
641
642
643
|
# File 'app/models/spree/order.rb', line 641
def display_total_applicable_store_credit
Spree::Money.new(-total_applicable_store_credit, { currency: })
end
|
#empty! ⇒ Object
447
448
449
450
451
452
453
454
|
# File 'app/models/spree/order.rb', line 447
def empty!
line_items.destroy_all
adjustments.destroy_all
shipments.destroy_all
Spree::Bus.publish :order_emptied, order: self
recalculate
end
|
#ensure_billing_address ⇒ Object
478
479
480
481
482
483
484
|
# File 'app/models/spree/order.rb', line 478
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.
433
434
435
436
437
438
439
440
441
|
# File 'app/models/spree/order.rb', line 433
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
472
473
474
475
476
|
# File 'app/models/spree/order.rb', line 472
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
340
341
342
343
344
345
|
# File 'app/models/spree/order.rb', line 340
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
407
408
409
410
411
|
# File 'app/models/spree/order.rb', line 407
def fulfill!
shipments.each { |shipment| shipment.update_state if shipment.persisted? }
recalculator.update_shipment_state
save!
end
|
#generate_order_number ⇒ Object
323
324
325
|
# File 'app/models/spree/order.rb', line 323
def generate_order_number
self.number ||= Spree::Config.order_number_generator.generate
end
|
563
564
565
|
# File 'app/models/spree/order.rb', line 563
def has_non_reimbursement_related_refunds?
refunds.non_reimbursement.exists?
end
|
#insufficient_stock_lines ⇒ Object
426
427
428
|
# File 'app/models/spree/order.rb', line 426
def insufficient_stock_lines
line_items.select(&:insufficient_stock?)
end
|
#is_risky? ⇒ Boolean
540
541
542
|
# File 'app/models/spree/order.rb', line 540
def is_risky?
payments.risky.count > 0
end
|
#item_total_before_tax ⇒ Object
212
213
214
|
# File 'app/models/spree/order.rb', line 212
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
221
222
223
|
# File 'app/models/spree/order.rb', line 221
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
356
357
358
359
360
361
362
|
# File 'app/models/spree/order.rb', line 356
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
443
444
445
|
# File 'app/models/spree/order.rb', line 443
def merge!(*args)
Spree::Config.order_merger_class.new(self).merge!(*args)
end
|
#name ⇒ Object
387
388
389
390
391
|
# File 'app/models/spree/order.rb', line 387
def name
if (address = bill_address || ship_address)
address.name
end
end
|
#order_total_after_store_credit ⇒ Object
629
630
631
|
# File 'app/models/spree/order.rb', line 629
def order_total_after_store_credit
total - total_applicable_store_credit
end
|
#outstanding_balance ⇒ Object
368
369
370
371
372
373
374
375
376
377
|
# File 'app/models/spree/order.rb', line 368
def outstanding_balance
if state == 'canceled'
-1 * payment_total
else
total - reimbursement_total - payment_total
end
end
|
#outstanding_balance? ⇒ Boolean
379
380
381
|
# File 'app/models/spree/order.rb', line 379
def outstanding_balance?
outstanding_balance != 0
end
|
#paid? ⇒ Boolean
Helper methods for checkout steps
414
415
416
|
# File 'app/models/spree/order.rb', line 414
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
250
251
252
|
# File 'app/models/spree/order.rb', line 250
def payment_required?
total > 0
end
|
#payments_attributes=(attributes) ⇒ Object
706
707
708
709
|
# File 'app/models/spree/order.rb', line 706
def payments_attributes=(attributes)
validate_payments_attributes(attributes)
super(attributes)
end
|
#persist_user_address! ⇒ Object
672
673
674
675
676
|
# File 'app/models/spree/order.rb', line 672
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
559
560
561
|
# File 'app/models/spree/order.rb', line 559
def quantity
line_items.sum(:quantity)
end
|
#quantity_of(variant, options = {}) ⇒ Object
335
336
337
338
|
# File 'app/models/spree/order.rb', line 335
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
267
268
269
|
# File 'app/models/spree/order.rb', line 267
def recalculator
@recalculator ||= Spree::Config.order_recalculator_class.new(self)
end
|
#record_ip_address(ip_address) ⇒ Object
698
699
700
701
702
703
704
|
# File 'app/models/spree/order.rb', line 698
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
532
533
534
|
# File 'app/models/spree/order.rb', line 532
def refresh_shipment_rates
shipments.map(&:refresh_rates)
end
|
#refund_total ⇒ Object
383
384
385
|
# File 'app/models/spree/order.rb', line 383
def refund_total
refunds.sum(&:amount)
end
|
#reimbursement_total ⇒ Object
364
365
366
|
# File 'app/models/spree/order.rb', line 364
def reimbursement_total
reimbursements.sum(:total)
end
|
#restart_checkout_flow ⇒ Object
522
523
524
525
526
527
528
529
530
|
# File 'app/models/spree/order.rb', line 522
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
653
654
655
|
# File 'app/models/spree/order.rb', line 653
def ship_address_attributes=(attributes)
self.ship_address = Spree::Address.immutable_merge(ship_address, attributes)
end
|
#shipment_total_before_tax ⇒ Object
216
217
218
|
# File 'app/models/spree/order.rb', line 216
def shipment_total_before_tax
shipments.to_a.sum(&:total_before_tax)
end
|
#shipped? ⇒ Boolean
468
469
470
|
# File 'app/models/spree/order.rb', line 468
def shipped?
%w(partial shipped).include?(shipment_state)
end
|
#shipped_shipments ⇒ Object
327
328
329
|
# File 'app/models/spree/order.rb', line 327
def shipped_shipments
shipments.shipped
end
|
#shipping ⇒ Object
300
301
302
|
# File 'app/models/spree/order.rb', line 300
def shipping
@shipping ||= Spree::Config.order_shipping_class.new(self)
end
|
#shipping_discount ⇒ Object
229
230
231
|
# File 'app/models/spree/order.rb', line 229
def shipping_discount
shipment_adjustments.credit.sum(:amount) * - 1
end
|
#shipping_eq_billing_address? ⇒ Boolean
536
537
538
|
# File 'app/models/spree/order.rb', line 536
def shipping_eq_billing_address?
bill_address == ship_address
end
|
#tax_address ⇒ Object
Returns the address for taxation based on configuration
259
260
261
262
263
264
265
|
# File 'app/models/spree/order.rb', line 259
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
567
568
569
|
# File 'app/models/spree/order.rb', line 567
def tax_total
additional_tax_total + included_tax_total
end
|
#to_param ⇒ Object
233
234
235
|
# File 'app/models/spree/order.rb', line 233
def to_param
number
end
|
#total_applicable_store_credit ⇒ Object
633
634
635
636
637
638
639
|
# File 'app/models/spree/order.rb', line 633
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:) || 0.0].min
end
end
|
#total_available_store_credit ⇒ Object
624
625
626
627
|
# File 'app/models/spree/order.rb', line 624
def total_available_store_credit
return 0.0 unless user
user.available_store_credit_total(currency:)
end
|
#valid_credit_cards ⇒ Object
402
403
404
405
|
# File 'app/models/spree/order.rb', line 402
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
711
712
713
714
715
716
717
718
719
720
|
# File 'app/models/spree/order.rb', line 711
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
|