Class: Spree::Order
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Spree::Order
show all
- Includes:
- Checkout, CurrencyUpdater
- Defined in:
- app/models/spree/order.rb,
app/models/spree/order/checkout.rb,
app/models/spree/order/currency_updater.rb
Defined Under Namespace
Modules: Checkout, CurrencyUpdater
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#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.
20
21
22
|
# File 'app/models/spree/order.rb', line 20
def coupon_code
@coupon_code
end
|
#use_billing ⇒ Object
Returns the value of attribute use_billing.
69
70
71
|
# File 'app/models/spree/order.rb', line 69
def use_billing
@use_billing
end
|
Class Method Details
.between(start_date, end_date) ⇒ Object
94
95
96
97
|
# File 'app/models/spree/order.rb', line 94
def self.between(start_date, end_date)
ActiveSupport::Deprecation.warn("Order#between will be deprecated in Spree 2.3, please use either Order#created_between or Order#completed_between instead.")
self.created_between(start_date, end_date)
end
|
.by_customer(customer) ⇒ Object
99
100
101
|
# File 'app/models/spree/order.rb', line 99
def self.by_customer(customer)
joins(:user).where("#{Spree.user_class.table_name}.email" => customer)
end
|
.by_number(number) ⇒ Object
86
87
88
|
# File 'app/models/spree/order.rb', line 86
def self.by_number(number)
where(number: number)
end
|
.by_state(state) ⇒ Object
103
104
105
|
# File 'app/models/spree/order.rb', line 103
def self.by_state(state)
where(state: state)
end
|
.complete ⇒ Object
107
108
109
|
# File 'app/models/spree/order.rb', line 107
def self.complete
where.not(completed_at: nil)
end
|
.incomplete ⇒ Object
111
112
113
|
# File 'app/models/spree/order.rb', line 111
def self.incomplete
where(completed_at: nil)
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
117
118
119
|
# File 'app/models/spree/order.rb', line 117
def self.register_update_hook(hook)
self.update_hooks.add(hook)
end
|
Instance Method Details
#all_adjustments ⇒ Object
121
122
123
124
|
# File 'app/models/spree/order.rb', line 121
def all_adjustments
Adjustment.where("order_id = :order_id OR (adjustable_id = :order_id AND adjustable_type = 'Spree::Order')",
order_id: self.id)
end
|
#allow_cancel? ⇒ Boolean
243
244
245
246
|
# File 'app/models/spree/order.rb', line 243
def allow_cancel?
return false unless completed? and state != 'canceled'
shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state)
end
|
#amount ⇒ Object
For compatiblity with Calculator::PriceSack
127
128
129
|
# File 'app/models/spree/order.rb', line 127
def amount
line_items.inject(0.0) { |sum, li| sum + li.amount }
end
|
#approve! ⇒ Object
576
577
578
|
# File 'app/models/spree/order.rb', line 576
def approve!
update_column(:considered_risky, false)
end
|
#approved? ⇒ Boolean
558
559
560
|
# File 'app/models/spree/order.rb', line 558
def approved?
!!self.approved_at
end
|
#approved_by(user) ⇒ Object
547
548
549
550
551
552
553
554
555
556
|
# File 'app/models/spree/order.rb', line 547
def approved_by(user)
self.transaction do
approve!
self.update_columns(
approver_id: user.id,
approved_at: Time.now,
considered_risky: false,
)
end
end
|
#associate_user!(user, override_email = true) ⇒ Object
Associates the specified user with the order.
257
258
259
260
261
262
263
264
265
266
267
268
|
# File 'app/models/spree/order.rb', line 257
def associate_user!(user, override_email = true)
self.user = user
attrs_to_set = { user_id: user.id }
attrs_to_set[:email] = user.email if override_email
attrs_to_set[:created_by_id] = user.id if self.created_by.blank?
assign_attributes(attrs_to_set)
if persisted?
self.class.unscoped.where(id: id).update_all(attrs_to_set)
end
end
|
#available_payment_methods ⇒ Object
#awaiting_returns? ⇒ Boolean
248
249
250
|
# File 'app/models/spree/order.rb', line 248
def awaiting_returns?
return_authorizations.any? { |return_authorization| return_authorization.authorized? }
end
|
#backordered? ⇒ Boolean
203
204
205
|
# File 'app/models/spree/order.rb', line 203
def backordered?
shipments.any?(&:backordered?)
end
|
#billing_firstname ⇒ Object
405
406
407
|
# File 'app/models/spree/order.rb', line 405
def billing_firstname
bill_address.try(:firstname)
end
|
#billing_lastname ⇒ Object
409
410
411
|
# File 'app/models/spree/order.rb', line 409
def billing_lastname
bill_address.try(:lastname)
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
322
323
324
|
# File 'app/models/spree/order.rb', line 322
def can_ship?
self.complete? || self.resumed? || self.awaiting_return? || self.returned?
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.
184
185
186
|
# File 'app/models/spree/order.rb', line 184
def checkout_allowed?
line_items.count > 0
end
|
#clone_billing_address ⇒ Object
234
235
236
237
238
239
240
241
|
# File 'app/models/spree/order.rb', line 234
def clone_billing_address
if bill_address and self.ship_address.nil?
self.ship_address = bill_address.clone
else
self.ship_address.attributes = bill_address.attributes.except('id', 'updated_at', 'created_at')
end
true
end
|
#completed? ⇒ Boolean
176
177
178
|
# File 'app/models/spree/order.rb', line 176
def completed?
completed_at.present?
end
|
#confirmation_required? ⇒ Boolean
If true, causes the confirmation step to happen during the checkout process
194
195
196
197
198
199
200
201
|
# File 'app/models/spree/order.rb', line 194
def confirmation_required?
Spree::Config[:always_include_confirm_step] ||
payments.valid.map(&:payment_method).compact.any?(&:payment_profiles_supported?) ||
state == 'confirm'
end
|
#consider_risk ⇒ Object
566
567
568
569
570
|
# File 'app/models/spree/order.rb', line 566
def consider_risk
if is_risky? && !approved?
considered_risky!
end
end
|
#considered_risky! ⇒ Object
572
573
574
|
# File 'app/models/spree/order.rb', line 572
def considered_risky!
update_column(:considered_risky, true)
end
|
#contains?(variant) ⇒ Boolean
288
289
290
|
# File 'app/models/spree/order.rb', line 288
def contains?(variant)
find_line_item_by_variant(variant).present?
end
|
#contents ⇒ Object
252
253
254
|
# File 'app/models/spree/order.rb', line 252
def contents
@contents ||= Spree::OrderContents.new(self)
end
|
#create_proposed_shipments ⇒ Object
490
491
492
493
494
495
496
497
498
499
500
|
# File 'app/models/spree/order.rb', line 490
def create_proposed_shipments
adjustments.shipping.delete_all
shipments.destroy_all
packages = Spree::Stock::Coordinator.new(self).packages
packages.each do |package|
shipments << package.to_shipment
end
shipments
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.
303
304
305
306
|
# File 'app/models/spree/order.rb', line 303
def create_tax_charge!
Spree::TaxRate.adjust(self, line_items)
Spree::TaxRate.adjust(self, shipments) if shipments.any?
end
|
#credit_cards ⇒ Object
326
327
328
329
|
# File 'app/models/spree/order.rb', line 326
def credit_cards
credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq
CreditCard.where(id: credit_card_ids)
end
|
#currency ⇒ Object
131
132
133
|
# File 'app/models/spree/order.rb', line 131
def currency
self[:currency] || Spree::Config[:currency]
end
|
#deliver_order_confirmation_email ⇒ Object
355
356
357
358
|
# File 'app/models/spree/order.rb', line 355
def deliver_order_confirmation_email
OrderMailer.confirm_email(self.id).deliver
update_column(:confirmation_delivered, true)
end
|
#display_additional_tax_total ⇒ Object
151
152
153
|
# File 'app/models/spree/order.rb', line 151
def display_additional_tax_total
Spree::Money.new(additional_tax_total, { currency: currency })
end
|
#display_adjustment_total ⇒ Object
143
144
145
|
# File 'app/models/spree/order.rb', line 143
def display_adjustment_total
Spree::Money.new(adjustment_total, { currency: currency })
end
|
#display_included_tax_total ⇒ Object
147
148
149
|
# File 'app/models/spree/order.rb', line 147
def display_included_tax_total
Spree::Money.new(included_tax_total, { currency: currency })
end
|
#display_item_total ⇒ Object
139
140
141
|
# File 'app/models/spree/order.rb', line 139
def display_item_total
Spree::Money.new(item_total, { currency: currency })
end
|
#display_outstanding_balance ⇒ Object
135
136
137
|
# File 'app/models/spree/order.rb', line 135
def display_outstanding_balance
Spree::Money.new(outstanding_balance, { currency: currency })
end
|
#display_shipment_total ⇒ Object
Also known as:
display_ship_total
159
160
161
|
# File 'app/models/spree/order.rb', line 159
def display_shipment_total
Spree::Money.new(shipment_total, { currency: currency })
end
|
#display_tax_total ⇒ Object
155
156
157
|
# File 'app/models/spree/order.rb', line 155
def display_tax_total
Spree::Money.new(included_tax_total + additional_tax_total, { currency: currency })
end
|
#display_total ⇒ Object
164
165
166
|
# File 'app/models/spree/order.rb', line 164
def display_total
Spree::Money.new(total, { currency: currency })
end
|
#empty! ⇒ Object
447
448
449
450
451
452
453
454
455
|
# File 'app/models/spree/order.rb', line 447
def empty!
line_items.destroy_all
updater.update_item_count
adjustments.destroy_all
shipments.destroy_all
update_totals
persist_totals
end
|
#ensure_line_items_are_in_stock ⇒ Object
417
418
419
420
421
|
# File 'app/models/spree/order.rb', line 417
def ensure_line_items_are_in_stock
if insufficient_stock_lines.present?
errors.add(:base, Spree.t(:insufficient_stock_lines_present)) and return false
end
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
514
515
516
517
518
519
520
|
# File 'app/models/spree/order.rb', line 514
def ensure_updated_shipments
if shipments.any? && !self.completed?
self.shipments.destroy_all
self.update_column(:shipment_total, 0)
restart_checkout_flow
end
end
|
#exclude_tax? ⇒ Boolean
Indicates whether tax should be backed out of the price calcualtions in cases where prices include tax but the customer is not required to pay taxes in that case.
216
217
218
219
|
# File 'app/models/spree/order.rb', line 216
def exclude_tax?
return false unless Spree::Config[:prices_inc_tax]
tax_zone != Zone.default_tax
end
|
#finalize! ⇒ Object
Finalizes an in progress order after checkout is complete. Called after transition to complete state when payments will have been processed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
|
# File 'app/models/spree/order.rb', line 333
def finalize!
all_adjustments.each{|a| a.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) ⇒ Object
297
298
299
|
# File 'app/models/spree/order.rb', line 297
def find_line_item_by_variant(variant)
line_items.detect { |line_item| line_item.variant_id == variant.id }
end
|
#generate_order_number(digits = 9) ⇒ Object
270
271
272
273
274
275
276
277
278
279
280
281
282
|
# File 'app/models/spree/order.rb', line 270
def generate_order_number(digits = 9)
self.number ||= loop do
random = "R#{Array.new(digits){rand(10)}.join}"
if self.class.exists?(number: random)
digits += 1 if self.class.count > (10 ** digits / 2)
else
break random
end
end
end
|
#has_step?(step) ⇒ Boolean
457
458
459
|
# File 'app/models/spree/order.rb', line 457
def has_step?(step)
checkout_steps.include?(step)
end
|
#insufficient_stock_lines ⇒ Object
413
414
415
|
# File 'app/models/spree/order.rb', line 413
def insufficient_stock_lines
line_items.select(&:insufficient_stock?)
end
|
#is_risky? ⇒ Boolean
543
544
545
|
# File 'app/models/spree/order.rb', line 543
def is_risky?
self.payments.risky.count > 0
end
|
#merge!(order, user = nil) ⇒ Object
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
|
# File 'app/models/spree/order.rb', line 423
def merge!(order, user = nil)
order.line_items.each do |line_item|
next unless line_item.currency == currency
current_line_item = self.line_items.find_by(variant: line_item.variant)
if current_line_item
current_line_item.quantity += line_item.quantity
current_line_item.save
else
line_item.order_id = self.id
line_item.save
end
end
self.associate_user!(user) if !self.user && !user.blank?
updater.update_item_count
updater.update_item_total
updater.persist_totals
order.line_items.reload
order.destroy
end
|
#name ⇒ Object
316
317
318
319
320
|
# File 'app/models/spree/order.rb', line 316
def name
if (address = bill_address || ship_address)
"#{address.firstname} #{address.lastname}"
end
end
|
#outstanding_balance ⇒ Object
308
309
310
|
# File 'app/models/spree/order.rb', line 308
def outstanding_balance
total - payment_total
end
|
#outstanding_balance? ⇒ Boolean
312
313
314
|
# File 'app/models/spree/order.rb', line 312
def outstanding_balance?
self.outstanding_balance != 0
end
|
#paid? ⇒ Boolean
Helper methods for checkout steps
361
362
363
|
# File 'app/models/spree/order.rb', line 361
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
189
190
191
|
# File 'app/models/spree/order.rb', line 189
def payment_required?
total.to_f > 0.0
end
|
#pending_payments ⇒ Object
369
370
371
|
# File 'app/models/spree/order.rb', line 369
def pending_payments
payments.select { |payment| payment.checkout? || payment.pending? }
end
|
#process_payments! ⇒ Object
processes any pending payments and must return a boolean as it’s return value is used by the checkout state_machine to determine success or failure of the ‘complete’ event for the order
Returns:
-
true if all pending_payments processed successfully
-
true if a payment failed, ie. raised a GatewayError which gets rescued and converted to TRUE when :allow_checkout_gateway_error is set to true
-
false if a payment failed, ie. raised a GatewayError which gets rescued and converted to FALSE when :allow_checkout_on_gateway_error is set to false
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
|
# File 'app/models/spree/order.rb', line 386
def process_payments!
if pending_payments.empty?
raise Core::GatewayError.new Spree.t(:no_pending_payments)
else
pending_payments.each do |payment|
break if payment_total >= total
payment.process!
if payment.completed?
self.payment_total += payment.amount
end
end
end
rescue Core::GatewayError => e
result = !!Spree::Config[:allow_checkout_on_gateway_error]
errors.add(:base, e.message) and return result
end
|
#quantity_of(variant) ⇒ Object
292
293
294
295
|
# File 'app/models/spree/order.rb', line 292
def quantity_of(variant)
line_item = find_line_item_by_variant(variant)
line_item ? line_item.quantity : 0
end
|
#refresh_shipment_rates ⇒ Object
529
530
531
|
# File 'app/models/spree/order.rb', line 529
def refresh_shipment_rates
shipments.map &:refresh_rates
end
|
#reload(options = nil) ⇒ Object
593
594
595
596
|
# File 'app/models/spree/order.rb', line 593
def reload(options=nil)
remove_instance_variable(:@tax_zone) if defined?(@tax_zone)
super
end
|
#restart_checkout_flow ⇒ Object
522
523
524
525
526
527
|
# File 'app/models/spree/order.rb', line 522
def restart_checkout_flow
self.update_columns(
state: checkout_steps.first,
updated_at: Time.now,
)
end
|
#set_shipments_cost ⇒ Object
537
538
539
540
541
|
# File 'app/models/spree/order.rb', line 537
def set_shipments_cost
shipments.each(&:update_amounts)
updater.update_shipment_total
persist_totals
end
|
#shipped? ⇒ Boolean
486
487
488
|
# File 'app/models/spree/order.rb', line 486
def shipped?
%w(partial shipped).include?(shipment_state)
end
|
#shipped_shipments ⇒ Object
284
285
286
|
# File 'app/models/spree/order.rb', line 284
def shipped_shipments
shipments.shipped
end
|
#shipping_discount ⇒ Object
168
169
170
|
# File 'app/models/spree/order.rb', line 168
def shipping_discount
shipment_adjustments.eligible.sum(:amount) * - 1
end
|
#shipping_eq_billing_address? ⇒ Boolean
533
534
535
|
# File 'app/models/spree/order.rb', line 533
def shipping_eq_billing_address?
(bill_address.empty? && ship_address.empty?) || bill_address.same_as?(ship_address)
end
|
#state_changed(name) ⇒ Object
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
|
# File 'app/models/spree/order.rb', line 461
def state_changed(name)
state = "#{name}_state"
if persisted?
old_state = self.send("#{state}_was")
new_state = self.send(state)
unless old_state == new_state
self.state_changes.create(
previous_state: old_state,
next_state: new_state,
name: name,
user_id: self.user_id
)
end
end
end
|
#tax_address ⇒ Object
Returns the address for taxation based on configuration
222
223
224
|
# File 'app/models/spree/order.rb', line 222
def tax_address
Spree::Config[:tax_using_ship_address] ? ship_address : bill_address
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
209
210
211
|
# File 'app/models/spree/order.rb', line 209
def tax_zone
@tax_zone ||= Zone.match(tax_address) || Zone.default_tax
end
|
#to_param ⇒ Object
172
173
174
|
# File 'app/models/spree/order.rb', line 172
def to_param
number.to_s.to_url.upcase
end
|
#update! ⇒ Object
230
231
232
|
# File 'app/models/spree/order.rb', line 230
def update!
updater.update
end
|
#update_line_items(line_item_params) ⇒ Object
moved from api order_decorator. This is a better place for it.
581
582
583
584
585
586
587
588
589
590
591
|
# File 'app/models/spree/order.rb', line 581
def update_line_items(line_item_params)
return if line_item_params.blank?
line_item_params.each_value do |attributes|
if attributes[:id].present?
self.line_items.find(attributes[:id]).update_attributes!(attributes)
else
self.line_items.create!(attributes)
end
end
self.ensure_updated_shipments
end
|
#updater ⇒ Object
226
227
228
|
# File 'app/models/spree/order.rb', line 226
def updater
@updater ||= OrderUpdater.new(self)
end
|