Class: Spree::Order
- Inherits:
-
Object
show all
- Extended by:
- DisplayMoney
- Includes:
- MemoizedData, Metadata, Metafields, NumberIdentifier, AddressBook, Checkout, CurrencyUpdater, Digital, GiftCard, Payments, StoreCredit, Webhooks, Searchable, Security::Orders, SingleStoreResource, VendorConcern
- Defined in:
- app/models/spree/order.rb,
app/models/spree/order/digital.rb,
app/models/spree/order/checkout.rb,
app/models/spree/order/payments.rb,
app/models/spree/order/webhooks.rb,
app/models/spree/order/gift_card.rb,
app/models/spree/order/address_book.rb,
app/models/spree/order/store_credit.rb,
app/models/spree/order/currency_updater.rb
Defined Under Namespace
Modules: AddressBook, Checkout, CurrencyUpdater, Digital, GiftCard, Payments, StoreCredit, Webhooks
Constant Summary
collapse
- PAYMENT_STATES =
%w(balance_due credit_owed failed paid void)
- SHIPMENT_STATES =
%w(backorder canceled partial pending ready shipped)
- LINE_ITEM_REMOVABLE_STATES =
%w(cart address delivery payment confirm resumed)
- MEMOIZED_METHODS =
%w(tax_zone)
- 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
- STATUSES =
%w[draft placed canceled].freeze
- ASSOCIATED_USER_ATTRIBUTES =
[:user_id, :email, :bill_address_id, :ship_address_id]
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
money_methods
Methods included from Metadata
#metadata, #metadata=, #public_metadata=
Methods included from GiftCard
#apply_gift_card, #gift_card_total, #recalculate_gift_card, #redeem_gift_card, #remove_gift_card
Methods included from Webhooks
#send_order_canceled_webhook, #send_order_placed_webhook, #send_order_resumed_webhook
#bill_address_attributes=, #bill_address_id=, #clone_billing_address, #clone_shipping_address, #ship_address_attributes=, #ship_address_id=
#add_store_credit_payments, #available_store_credits, #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, #display_total_minus_store_credits, #order_total_after_store_credit, #remove_store_credit_payments, #total_applicable_store_credit, #total_applied_store_credit, #total_available_store_credit, #total_minus_store_credits, #using_store_credit?
Methods included from Digital
#create_digital_links, #digital?, #digital_line_items, #digital_links, #some_digital?, #with_digital_assets?
#price_from_line_item, #update_line_item_currencies!, #update_line_item_price!
Methods included from Checkout
included
Instance Attribute Details
#coupon_code ⇒ Object
Returns the value of attribute coupon_code.
122
123
124
|
# File 'app/models/spree/order.rb', line 122
def coupon_code
@coupon_code
end
|
#notify_customer ⇒ Object
Set to false on admin-initiated flows to suppress customer-facing emails.
126
127
128
|
# File 'app/models/spree/order.rb', line 126
def notify_customer
@notify_customer
end
|
#skip_market_resolution ⇒ Object
Returns the value of attribute skip_market_resolution.
235
236
237
|
# File 'app/models/spree/order.rb', line 235
def skip_market_resolution
@skip_market_resolution
end
|
#temporary_address ⇒ Object
Returns the value of attribute temporary_address.
123
124
125
|
# File 'app/models/spree/order.rb', line 123
def temporary_address
@temporary_address
end
|
#use_billing ⇒ Object
Returns the value of attribute use_billing.
235
236
237
|
# File 'app/models/spree/order.rb', line 235
def use_billing
@use_billing
end
|
#use_shipping ⇒ Object
Returns the value of attribute use_shipping.
235
236
237
|
# File 'app/models/spree/order.rb', line 235
def use_shipping
@use_shipping
end
|
Class Method Details
.find_by_param(param) ⇒ Spree::Order?
Find an order by prefixed ID first, falling back to number, then integer id for backwards compatibility
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
|
# File 'app/models/spree/order.rb', line 320
def self.find_by_param(param)
return nil if param.blank?
if param.to_s.include?('_')
decoded = decode_prefixed_id(param)
order = find_by(id: decoded) if decoded
return order if order
end
order = find_by(number: param)
return order if order
find_by(id: param) if param.to_s.match?(/\A\d+\z/)
end
|
.find_by_param!(param) ⇒ Spree::Order
Find an order by prefixed ID first, falling back to number, then integer id for backwards compatibility
Raises ActiveRecord::RecordNotFound if not found
343
344
345
|
# File 'app/models/spree/order.rb', line 343
def self.find_by_param!(param)
find_by_param(param) || raise(ActiveRecord::RecordNotFound.new("Couldn't find Order with param=#{param}"))
end
|
.multi_search(query) ⇒ Object
Backward compatibility alias — remove in Spree 6.0
315
|
# File 'app/models/spree/order.rb', line 315
def self.multi_search(query) = search(query)
|
.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
349
350
351
|
# File 'app/models/spree/order.rb', line 349
def self.register_update_hook(hook)
update_hooks.add(hook)
end
|
.search(query) ⇒ Object
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
|
# File 'app/models/spree/order.rb', line 292
def self.search(query)
sanitized_query = sanitize_query_for_search(query)
return none if query.blank?
query_pattern = "%#{sanitized_query}%"
conditions = []
conditions << arel_table[:number].lower.matches(query_pattern)
conditions << search_condition(Spree::Address, :firstname, sanitized_query)
conditions << search_condition(Spree::Address, :lastname, sanitized_query)
full_name = NameOfPerson::PersonName.full(sanitized_query)
if full_name.first.present? && full_name.last.present?
conditions << search_condition(Spree::Address, :firstname, full_name.first)
conditions << search_condition(Spree::Address, :lastname, full_name.last)
end
left_joins(:bill_address).where(arel_table[:email].lower.eq(query.downcase)).or(where(conditions.reduce(:or)))
end
|
Instance Method Details
#all_inventory_units_returned? ⇒ Boolean
539
540
541
|
# File 'app/models/spree/order.rb', line 539
def all_inventory_units_returned?
inventory_units.all?(&:returned?)
end
|
#all_line_items ⇒ Object
1057
1058
1059
|
# File 'app/models/spree/order.rb', line 1057
def all_line_items
line_items
end
|
#all_line_items_invalid? ⇒ Boolean
Checks if all line items cannot be shipped
844
845
846
|
# File 'app/models/spree/order.rb', line 844
def all_line_items_invalid?
line_items_without_shipping_rates.size == line_items.count
end
|
#allow_cancel? ⇒ Boolean
533
534
535
536
537
|
# File 'app/models/spree/order.rb', line 533
def allow_cancel?
return false if !completed? || canceled?
shipment_state.nil? || %w{ready backorder pending canceled}.include?(shipment_state)
end
|
#amount ⇒ Object
For compatibility with Calculator::PriceSack
354
355
356
|
# File 'app/models/spree/order.rb', line 354
def amount
line_items.inject(0.0) { |sum, li| sum + li.amount }
end
|
#amount_due ⇒ Object
61
62
63
|
# File 'app/models/spree/order.rb', line 61
def amount_due
[outstanding_balance - total_applied_store_credit, 0].max
end
|
#analytics_subtotal ⇒ Float
Returns the subtotal used for analytics integrations
It's a sum of the item total and the promo total
371
372
373
|
# File 'app/models/spree/order.rb', line 371
def analytics_subtotal
(item_total + line_items.sum(:promo_total)).to_f
end
|
Applies user promotions when login after filling the cart
968
969
970
|
# File 'app/models/spree/order.rb', line 968
def approve!
Spree.order_approve_service.call(order: self)
end
|
936
937
938
|
# File 'app/models/spree/order.rb', line 936
def approved?
!!approved_at
end
|
932
933
934
|
# File 'app/models/spree/order.rb', line 932
def approved_by(user = nil)
Spree.order_approve_service.call(order: self, approver: user)
end
|
Associates the specified user with the order.
Delegates to Cart::Associate service.
549
550
551
|
# File 'app/models/spree/order.rb', line 549
def associate_user!(user, override_email = true)
Spree.cart_associate_service.call(guest_order: self, user: user, override_email: override_email)
end
|
#available_payment_methods(store = nil) ⇒ Object
693
694
695
696
697
|
# File 'app/models/spree/order.rb', line 693
def available_payment_methods(store = nil)
Spree::Deprecation.warn('`Order#available_payment_methods` is deprecated and will be removed in Spree 5.5. Use `collect_frontend_payment_methods` instead.')
@available_payment_methods ||= collect_payment_methods(store)
end
|
#backordered? ⇒ Boolean
452
453
454
|
# File 'app/models/spree/order.rb', line 452
def backordered?
shipments.any?(&:backordered?)
end
|
#backordered_variants ⇒ Array<Spree::Variant>
Returns the backordered variants for the order
636
637
638
639
640
641
|
# File 'app/models/spree/order.rb', line 636
def backordered_variants
variants.
where(track_inventory: true).
joins(:stock_items, :product).
where(Spree::StockItem.table_name => { count_on_hand: ..0, backorderable: true })
end
|
#can_approve? ⇒ Boolean
940
941
942
|
# File 'app/models/spree/order.rb', line 940
def can_approve?
!approved?
end
|
#can_be_deleted? ⇒ Boolean
949
950
951
|
# File 'app/models/spree/order.rb', line 949
def can_be_deleted?
!completed? && payments.completed.empty?
end
|
#can_be_destroyed? ⇒ Boolean
944
945
946
947
|
# File 'app/models/spree/order.rb', line 944
def can_be_destroyed?
Spree::Deprecation.warn('Spree::Order#can_be_destroyed? is deprecated and will be removed in the next major version. Use Spree::Order#can_be_deleted? instead.')
can_be_deleted?
end
|
643
644
645
|
# File 'app/models/spree/order.rb', line 643
def can_ship?
complete? || resumed? || awaiting_return? || returned?
end
|
923
924
925
|
# File 'app/models/spree/order.rb', line 923
def canceled_by(user, canceled_at = nil)
Spree.order_cancel_service.call(order: self, canceler: user, canceled_at: canceled_at)
end
|
Returns item and whole order discount amount for Order
without Shipment discounts (eg. Free Shipping)
1032
1033
1034
1035
1036
|
# File 'app/models/spree/order.rb', line 1032
def cart_promo_total
all_adjustments.eligible.nonzero.promotion.
where.not(adjustable_type: 'Spree::Shipment').
sum(:amount)
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.
424
425
426
|
# File 'app/models/spree/order.rb', line 424
def checkout_allowed?
line_items.exists?
end
|
#collect_backend_payment_methods ⇒ Object
985
986
987
|
# File 'app/models/spree/order.rb', line 985
def collect_backend_payment_methods
store.payment_methods.active.available_on_back_end.select { |pm| pm.available_for_order?(self) }
end
|
#collect_frontend_payment_methods ⇒ Object
989
990
991
|
# File 'app/models/spree/order.rb', line 989
def collect_frontend_payment_methods
store.payment_methods.active.available_on_front_end.select { |pm| pm.available_for_order?(self) }
end
|
379
380
381
|
# File 'app/models/spree/order.rb', line 379
def completed?
completed_at.present?
end
|
#confirmation_required? ⇒ Boolean
If true, causes the confirmation step to happen during the checkout process
439
440
441
442
443
444
445
446
|
# File 'app/models/spree/order.rb', line 439
def confirmation_required?
Spree::Config[:always_include_confirm_step] ||
payments.valid.map(&:payment_method).compact.any?(&:confirmation_required?) ||
confirm?
end
|
#consider_risk ⇒ Object
953
954
955
|
# File 'app/models/spree/order.rb', line 953
def consider_risk
considered_risky! if is_risky? && !approved?
end
|
#considered_risky! ⇒ Object
957
958
959
960
961
962
|
# File 'app/models/spree/order.rb', line 957
def considered_risky!
update_column(:considered_risky, true)
publish_event('order.updated')
end
|
#create_proposed_shipments ⇒ Object
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
|
# File 'app/models/spree/order.rb', line 781
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 = order_routing_strategy.for_allocation.map do |package|
package.to_shipment.tap { |s| s.address_id = ship_address_id }
end
end
|
#create_shipment_tax_charge! ⇒ Object
578
579
580
|
# File 'app/models/spree/order.rb', line 578
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.
573
574
575
576
|
# File 'app/models/spree/order.rb', line 573
def create_tax_charge!
Spree::TaxRate.adjust(self, line_items)
Spree::TaxRate.adjust(self, shipments) if shipments.any?
end
|
#delivery_required? ⇒ Boolean
Does this order require a delivery (physical or digital).
429
430
431
|
# File 'app/models/spree/order.rb', line 429
def delivery_required?
true end
|
#disassociate_user! ⇒ Object
553
554
555
556
557
|
# File 'app/models/spree/order.rb', line 553
def disassociate_user!
nullified_attributes = ASSOCIATED_USER_ATTRIBUTES.index_with(nil)
update!(nullified_attributes)
end
|
390
391
392
|
# File 'app/models/spree/order.rb', line 390
def draft?
status == 'draft'
end
|
#email_required? ⇒ Boolean
448
449
450
|
# File 'app/models/spree/order.rb', line 448
def email_required?
require_email
end
|
#empty! ⇒ Object
726
727
728
729
730
731
|
# File 'app/models/spree/order.rb', line 726
def empty!
raise Spree.t(:cannot_empty_completed_order) if completed?
result = Spree.cart_empty_service.call(order: self)
result.value
end
|
#ensure_channel_presence ⇒ Object
510
511
512
513
514
|
# File 'app/models/spree/order.rb', line 510
def ensure_channel_presence
return if channel_id.present?
self.channel = store&.default_channel
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.
706
707
708
709
710
711
712
713
714
|
# File 'app/models/spree/order.rb', line 706
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
716
717
718
719
720
721
722
723
724
|
# File 'app/models/spree/order.rb', line 716
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_market_presence ⇒ Object
506
507
508
|
# File 'app/models/spree/order.rb', line 506
def ensure_market_presence
self.market ||= Spree::Current.market || store&.default_market
end
|
#ensure_store_presence ⇒ Object
501
502
503
504
|
# File 'app/models/spree/order.rb', line 501
def ensure_store_presence
Spree::Deprecation.warn('Spree::Order#ensure_store_presence is deprecated and will be removed in Spree 6.0. ensure_store instead.')
ensure_store
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
865
866
867
868
869
870
871
872
873
874
875
|
# File 'app/models/spree/order.rb', line 865
def ensure_updated_shipments
if shipments.any? && !completed?
shipments.destroy_all
update_column(:shipment_total, 0)
publish_event('order.updated')
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
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
|
# File 'app/models/spree/order.rb', line 653
def finalize!
all_adjustments.each(&:close)
updater.update_payment_state
shipments.each do |shipment|
shipment.update!(self)
shipment.finalize!
end
updater.update_shipment_state
self.status = 'placed'
save!
updater.run_hooks
touch :completed_at
send_order_placed_webhook
consider_risk
publish_order_completed_event
end
|
#find_line_item_by_variant(variant, options = {}) ⇒ Object
564
565
566
567
568
569
|
# File 'app/models/spree/order.rb', line 564
def find_line_item_by_variant(variant, options = {})
line_items.detect do |line_item|
line_item.variant_id == variant.id &&
Spree.cart_compare_line_items_service.new.call(order: self, line_item: line_item, options: options).value
end
end
|
#fulfill! ⇒ Object
678
679
680
681
682
|
# File 'app/models/spree/order.rb', line 678
def fulfill!
shipments.each { |shipment| shipment.update!(self) if shipment.persisted? }
updater.update_shipment_state
save!
end
|
#full_name ⇒ Object
611
612
613
614
615
616
617
|
# File 'app/models/spree/order.rb', line 611
def full_name
@full_name ||= if user.present? && user.name.present?
user.full_name
else
billing_address&.full_name || email
end
end
|
#fully_discounted? ⇒ Boolean
Also known as:
fully_discounted
determines whether the inventory is fully discounted
Returns
- true if inventory amount is the exact negative of inventory related adjustments
- false otherwise
998
999
1000
|
# File 'app/models/spree/order.rb', line 998
def fully_discounted?
adjustment_total + line_items.map(&:final_amount).sum == 0.0
end
|
#fully_shipped? ⇒ Boolean
777
778
779
|
# File 'app/models/spree/order.rb', line 777
def fully_shipped?
shipments.shipped.size == shipments.size
end
|
#guest_checkout_disallowed? ⇒ Boolean
Returns true when this order has no registered user and its
channel forbids guest checkout (see Spree::Channel::Gating). Enforced by
the checkout completion service and the v3 Store API so every completion
path (controller, payment webhook) is covered.
A prices_hidden channel also disallows guest completion regardless of
the guest_checkout flag — prices are withheld from guests, and a buyer
who cannot see prices cannot meaningfully place an order. This dissolves
the otherwise contradictory "prices hidden but guests may buy" config.
525
526
527
528
529
530
531
|
# File 'app/models/spree/order.rb', line 525
def guest_checkout_disallowed?
return false if user_id.present?
return false if channel.blank?
return true if channel.storefront_prices_hidden?
!channel.resolved_guest_checkout
end
|
#has_free_shipping? ⇒ Boolean
1038
1039
1040
1041
1042
1043
|
# File 'app/models/spree/order.rb', line 1038
def has_free_shipping?
shipment_adjustments.
joins(:promotion_action).
where(spree_adjustments: { eligible: true, source_type: 'Spree::PromotionAction' },
spree_promotion_actions: { type: 'Spree::Promotion::Actions::FreeShipping' }).exists?
end
|
980
981
982
983
|
# File 'app/models/spree/order.rb', line 980
def has_non_reimbursement_related_refunds?
refunds.non_reimbursement.exists? ||
payments.offset_payment.exists? end
|
#has_step?(step) ⇒ Boolean
737
738
739
|
# File 'app/models/spree/order.rb', line 737
def has_step?(step)
checkout_steps.include?(step)
end
|
#in_checkout? ⇒ Boolean
True when the order is mid-checkout: past the cart state but not yet
completed or canceled. Used by stock reservation hooks and any flow
that should only run during the active checkout phase.
386
387
388
|
# File 'app/models/spree/order.rb', line 386
def in_checkout?
!cart? && !complete? && !canceled?
end
|
#inferred_preferred_stock_location_id ⇒ Integer?
Cascade for the preferred_location rule kind. Channel and B2B sources
are layered in by their respective plans.
819
820
821
822
|
# File 'app/models/spree/order.rb', line 819
def inferred_preferred_stock_location_id
preferred_stock_location_id.presence ||
created_by&.try(:preferred_stock_location_id)
end
|
#insufficient_stock_lines ⇒ Object
699
700
701
|
# File 'app/models/spree/order.rb', line 699
def insufficient_stock_lines
line_items.select(&:insufficient_stock?)
end
|
913
914
915
|
# File 'app/models/spree/order.rb', line 913
def is_risky?
!payments.risky.empty?
end
|
#line_items_without_shipping_rates ⇒ Array<Spree::LineItem>
Returns line items that have no shipping rates
835
836
837
838
839
|
# File 'app/models/spree/order.rb', line 835
def line_items_without_shipping_rates
@line_items_without_shipping_rates ||= shipments.map do |shipment|
shipment.manifest.map(&:line_item) if shipment.shipping_rates.blank?
end.flatten.compact
end
|
#log_state_changes(state_name:, old_state:, new_state:) ⇒ Object
752
753
754
755
756
757
758
759
|
# File 'app/models/spree/order.rb', line 752
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
497
498
499
|
# File 'app/models/spree/order.rb', line 497
def merger
@merger ||= Spree::OrderMerger.new(self)
end
|
#name ⇒ Object
605
606
607
608
609
|
# File 'app/models/spree/order.rb', line 605
def name
if (address = bill_address || ship_address)
address.full_name
end
end
|
#order_refunded? ⇒ Boolean
Checks if the order is fully refunded
400
401
402
403
404
405
|
# File 'app/models/spree/order.rb', line 400
def order_refunded?
return false if item_count.zero?
return false if refunds_total.zero?
payment_state.in?(%w[void failed]) || refunds_total == total_minus_store_credits - additional_tax_total.abs
end
|
Resolves the routing strategy from the channel override first, then the
store default. Only a registered Spree::OrderRouting::Strategy::Base
subclass is used; any other value (an unregistered/typo'd class, or a
strategy that was unregistered after being persisted) is logged and
skipped rather than raised, falling back to the default Rules strategy so
a misconfiguration can't take down cart display or checkout.
807
808
809
810
811
812
813
|
# File 'app/models/spree/order.rb', line 807
def order_routing_strategy
klass = valid_order_routing_strategy_class(channel&.preferred_order_routing_strategy) ||
valid_order_routing_strategy_class(store.preferred_order_routing_strategy) ||
Spree::OrderRouting::Strategy::Rules
klass.new(order: self)
end
|
#outstanding_balance ⇒ Object
589
590
591
592
593
594
595
|
# File 'app/models/spree/order.rb', line 589
def outstanding_balance
if canceled?
-1 * payment_total
else
total - (payment_total + reimbursement_paid_total)
end
end
|
#outstanding_balance? ⇒ Boolean
601
602
603
|
# File 'app/models/spree/order.rb', line 601
def outstanding_balance?
outstanding_balance != 0
end
|
Helper methods for checkout steps
685
686
687
|
# File 'app/models/spree/order.rb', line 685
def paid?
payments.valid.completed.size == payments.valid.size && payments.valid.sum(:amount) >= total
end
|
#partially_refunded? ⇒ Boolean
Checks if the order is partially refunded
413
414
415
416
417
418
|
# File 'app/models/spree/order.rb', line 413
def partially_refunded?
return false if item_count.zero?
return false if payment_state.in?(%w[void failed]) || refunds.empty?
refunds_total < total_minus_store_credits - additional_tax_total.abs
end
|
Returns the payment method for the order
622
623
624
|
# File 'app/models/spree/order.rb', line 622
def payment_method
payments.valid.not_store_credits.first&.payment_method
end
|
#payment_methods ⇒ Object
689
690
691
|
# File 'app/models/spree/order.rb', line 689
def payment_methods
@payment_methods ||= store.payment_methods.active.available_on_front_end.select { |pm| pm.available_for_order?(self) }
end
|
#payment_required? ⇒ Boolean
Is this a free order in which case the payment step should be skipped
434
435
436
|
# File 'app/models/spree/order.rb', line 434
def payment_required?
total.to_f > 0.0
end
|
Returns the payment source for the order
629
630
631
|
# File 'app/models/spree/order.rb', line 629
def payment_source
payments.valid.not_store_credits.first&.source
end
|
394
395
396
|
# File 'app/models/spree/order.rb', line 394
def placed?
status == 'placed'
end
|
#pre_tax_item_amount ⇒ Object
Sum of all line item amounts pre-tax
359
360
361
|
# File 'app/models/spree/order.rb', line 359
def pre_tax_item_amount
line_items.sum(:pre_tax_amount)
end
|
#pre_tax_total ⇒ Object
Sum of all line item and shipment pre-tax
364
365
366
|
# File 'app/models/spree/order.rb', line 364
def pre_tax_total
pre_tax_item_amount + shipments.sum(:pre_tax_amount)
end
|
1003
1004
1005
|
# File 'app/models/spree/order.rb', line 1003
def promo_code
Spree::CouponCode.find_by(order: self, promotion: promotions).try(:code) || promotions.pluck(:code).compact.first
end
|
#quantity ⇒ Object
976
977
978
|
# File 'app/models/spree/order.rb', line 976
def quantity
line_items.sum(:quantity)
end
|
#quantity_of(variant, options = {}) ⇒ Object
559
560
561
562
|
# File 'app/models/spree/order.rb', line 559
def quantity_of(variant, options = {})
line_item = find_line_item_by_variant(variant, options)
line_item ? line_item.quantity : 0
end
|
#quick_checkout? ⇒ Boolean
Check if the shipping address is a quick checkout address
quick checkout addresses are incomplete as wallet providers like Apple Pay and Google Pay
do not provide all the address fields until the checkout is completed (confirmed) on their side
460
461
462
|
# File 'app/models/spree/order.rb', line 460
def quick_checkout?
shipping_address.present? && shipping_address.quick_checkout?
end
|
#quick_checkout_available? ⇒ Boolean
Check if quick checkout is available for this order
Either fully digital or not digital at all
467
468
469
|
# File 'app/models/spree/order.rb', line 467
def quick_checkout_available?
payment_required? && shipments.count <= 1 && (digital? || !some_digital? || !delivery_required?)
end
|
#quick_checkout_require_address? ⇒ Boolean
Check if quick checkout requires an address collection
If the order is digital or not delivery required, then we don't need to collect an address
474
475
476
|
# File 'app/models/spree/order.rb', line 474
def quick_checkout_require_address?
!digital? && delivery_required?
end
|
#refresh_shipment_rates(shipping_method_filter = ShippingMethod::DISPLAY_ON_FRONT_END) ⇒ Object
889
890
891
|
# File 'app/models/spree/order.rb', line 889
def refresh_shipment_rates(shipping_method_filter = ShippingMethod::DISPLAY_ON_FRONT_END)
shipments.map { |s| s.refresh_rates(shipping_method_filter) }
end
|
#refunds_total ⇒ Object
407
408
409
|
# File 'app/models/spree/order.rb', line 407
def refunds_total
refunds.loaded? ? refunds.sum(&:amount) : refunds.sum(:amount)
end
|
#reimbursement_paid_total ⇒ Object
597
598
599
|
# File 'app/models/spree/order.rb', line 597
def reimbursement_paid_total
reimbursements.sum(&:paid_amount)
end
|
#remove_out_of_stock_items! ⇒ Object
Removes out-of-stock/discontinued items and populates warnings.
Returns self (reloaded if items were removed) with warnings set.
Captured before the call because removing items reloads the order, which
would drop warnings already recorded upstream.
72
73
74
75
76
77
78
79
80
|
# File 'app/models/spree/order.rb', line 72
def remove_out_of_stock_items!
existing_warnings = warnings
result = Spree::Cart::RemoveOutOfStockItems.call(order: self)
return self unless result.success?
order, _messages, new_warnings = result.value
order.warnings = existing_warnings | (new_warnings || [])
order
end
|
#requires_ship_address? ⇒ Boolean
1061
1062
1063
|
# File 'app/models/spree/order.rb', line 1061
def requires_ship_address?
!digital?
end
|
#restart_checkout_flow ⇒ Object
877
878
879
880
881
882
883
884
885
886
887
|
# File 'app/models/spree/order.rb', line 877
def restart_checkout_flow
update_columns(
state: 'cart',
updated_at: Time.current
)
publish_event('order.updated')
next! unless line_items.empty?
end
|
#set_shipments_cost ⇒ Object
897
898
899
900
901
902
|
# File 'app/models/spree/order.rb', line 897
def set_shipments_cost
shipments.each(&:update_amounts)
updater.update_shipment_total
updater.update_adjustment_total
persist_totals
end
|
773
774
775
|
# File 'app/models/spree/order.rb', line 773
def shipped?
%w(partial shipped).include?(shipment_state)
end
|
#shipping_discount ⇒ Object
375
376
377
|
# File 'app/models/spree/order.rb', line 375
def shipping_discount
shipment_adjustments.non_tax.eligible.sum(:amount) * - 1
end
|
#shipping_eq_billing_address? ⇒ Boolean
893
894
895
|
# File 'app/models/spree/order.rb', line 893
def shipping_eq_billing_address?
bill_address == ship_address
end
|
#shipping_method ⇒ Object
904
905
906
907
908
909
910
911
|
# File 'app/models/spree/order.rb', line 904
def shipping_method
Spree::ShippingMethod.
where(id: shipments.with_selected_shipping_method.limit(1).pluck(:shipping_method_id)).
limit(1).
first
end
|
#state_changed(name) ⇒ Object
741
742
743
744
745
746
747
748
749
750
|
# File 'app/models/spree/order.rb', line 741
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
|
142
143
144
|
# File 'app/models/spree/order.rb', line 142
def tags=(tags)
self.tag_list = tags
end
|
#tax_address ⇒ Object
Returns the address for taxation based on configuration
485
486
487
|
# File 'app/models/spree/order.rb', line 485
def tax_address
Spree::Config[:tax_using_ship_address] ? ship_address : bill_address
end
|
#tax_total ⇒ Object
972
973
974
|
# File 'app/models/spree/order.rb', line 972
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
480
481
482
|
# File 'app/models/spree/order.rb', line 480
def tax_zone
@tax_zone ||= Zone.match(tax_address) || Zone.default_tax
end
|
#to_csv(_store = nil) ⇒ Object
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
|
# File 'app/models/spree/order.rb', line 1045
def to_csv(_store = nil)
metafields_for_csv ||= Spree::MetafieldDefinition.for_resource_type('Spree::Order').order(:namespace, :key).map do |mf_def|
metafields.find { |mf| mf.metafield_definition_id == mf_def.id }&.csv_value
end
csv_lines = []
all_line_items.each_with_index do |line_item, index|
csv_lines << Spree::CSV::OrderLineItemPresenter.new(self, line_item, index, metafields_for_csv).call
end
csv_lines
end
|
#total_weight ⇒ BigDecimal
Returns the total weight of the inventory units in the order
This is used to calculate the shipping rates for the order
828
829
830
|
# File 'app/models/spree/order.rb', line 828
def total_weight
@total_weight ||= line_items.joins(:variant).includes(:variant).map(&:item_weight).sum
end
|
#uneditable? ⇒ Boolean
647
648
649
|
# File 'app/models/spree/order.rb', line 647
def uneditable?
complete? || canceled? || returned?
end
|
#update_line_item_prices! ⇒ Object
582
583
584
585
586
587
|
# File 'app/models/spree/order.rb', line 582
def update_line_item_prices!
transaction do
line_items.reload.each(&:update_price)
save!
end
end
|
#update_with_updater! ⇒ Object
493
494
495
|
# File 'app/models/spree/order.rb', line 493
def update_with_updater!
updater.update
end
|
#updater ⇒ Object
489
490
491
|
# File 'app/models/spree/order.rb', line 489
def updater
@updater ||= Spree.order_updater.new(self)
end
|
#use_all_coupon_codes ⇒ Object
Returns the valid coupon promotions for the order
1023
1024
1025
1026
1027
|
# File 'app/models/spree/order.rb', line 1023
def valid_coupon_promotions
promotions.
where(id: valid_promotion_ids).
coupons
end
|
Returns the IDs of the valid promotions for the order
1015
1016
1017
1018
1019
|
# File 'app/models/spree/order.rb', line 1015
def valid_promotion_ids
all_adjustments.eligible.nonzero.promotion.promotion.eligible.nonzero.promotion.
joins("INNER JOIN #{Spree::PromotionAction.table_name} ON #{Spree::PromotionAction.table_name}.id = #{Spree::Adjustment.table_name}.source_id").
pluck("#{Spree::PromotionAction.table_name}.promotion_id").compact.uniq
end
|
Returns the valid promotions for the order
1009
1010
1011
|
# File 'app/models/spree/order.rb', line 1009
def valid_promotions
order_promotions.includes(:promotion).where(promotion_id: valid_promotion_ids).uniq(&:promotion_id)
end
|