Class: Effective::Order

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/effective/order.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(atts = nil, &block) ⇒ Order

Effective::Order.new(items: Product.first) Effective::Order.new(items: [Product.first, Product.second], user: User.first) Effective::Order.new(items: Product.first, user: User.first, billing_address: Effective::Address.new, shipping_address: Effective::Address.new)



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'app/models/effective/order.rb', line 160

def initialize(atts = nil, &block)
  super(state: EffectiveOrders::PENDING) # Initialize with state: PENDING

  return self unless atts.present?

  if atts.kind_of?(Hash)
    items = Array(atts[:item]) + Array(atts[:items])

    self.user = atts[:user] || (items.first.user if items.first.respond_to?(:user))

    if (address = atts[:billing_address]).present?
      self.billing_address = address
      self.billing_address.full_name ||= user.to_s.presence
    end

    if (address = atts[:shipping_address]).present?
      self.shipping_address = address
      self.shipping_address.full_name ||= user.to_s.presence
    end

    atts.except(:item, :items, :user, :billing_address, :shipping_address).each do |key, value|
      self.send("#{key}=", value)
    end

    add(items) if items.present?
  else # Attributes are not a Hash
    self.user = atts.user if atts.respond_to?(:user)
    add(atts)
  end

  self
end

Instance Attribute Details

#confirmed_checkoutObject

Set on the Checkout Step 1



22
23
24
# File 'app/models/effective/order.rb', line 22

def confirmed_checkout
  @confirmed_checkout
end

#send_mark_as_paid_email_to_buyerObject

Set by Admin::Orders#mark_as_paid



26
27
28
# File 'app/models/effective/order.rb', line 26

def send_mark_as_paid_email_to_buyer
  @send_mark_as_paid_email_to_buyer
end

#send_payment_request_to_buyerObject

Settings in the /admin action forms



25
26
27
# File 'app/models/effective/order.rb', line 25

def send_payment_request_to_buyer
  @send_payment_request_to_buyer
end

#skip_buyer_validationsObject

Set by Admin::Orders#create



27
28
29
# File 'app/models/effective/order.rb', line 27

def skip_buyer_validations
  @skip_buyer_validations
end

#terms_and_conditionsObject

Yes, I agree to the terms and conditions



21
22
23
# File 'app/models/effective/order.rb', line 21

def terms_and_conditions
  @terms_and_conditions
end

Instance Method Details

#abandoned?Boolean

Returns:

  • (Boolean)


356
357
358
# File 'app/models/effective/order.rb', line 356

def abandoned?
  state == EffectiveOrders::ABANDONED
end

#add(*items, quantity: 1) ⇒ Object

Items can be an Effective::Cart, an Effective::order, a single acts_as_purchasable, or multiple acts_as_purchasables add(Product.first) => returns an Effective::OrderItem add(Product.first, current_cart) => returns an array of Effective::OrderItems



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'app/models/effective/order.rb', line 196

def add(*items, quantity: 1)
  raise 'unable to alter a purchased order' if purchased?
  raise 'unable to alter a declined order' if declined?

  cart_items = items.flatten.flat_map do |item|
    if item.kind_of?(Effective::Cart)
      item.cart_items.to_a
    elsif item.kind_of?(ActsAsPurchasable)
      Effective::CartItem.new(quantity: quantity, purchasable: item)
    elsif item.kind_of?(Effective::Order)
      # Duplicate an existing order
      self.note_to_buyer ||= item.note_to_buyer
      self.note_internal ||= item.note_internal
      self.cc ||= item.cc

      item.order_items.select { |oi| oi.purchasable.kind_of?(Effective::Product) }.map do |oi|
        purchasable = oi.purchasable

        product = Effective::Product.new(name: purchasable.purchasable_name, price: purchasable.price, tax_exempt: purchasable.tax_exempt)

        # Copy over any extended attributes that may have been created
        atts = purchasable.dup.attributes.except('name', 'price', 'tax_exempt', 'purchased_order_id').compact

        atts.each do |k, v|
          next unless product.respond_to?("#{k}=") && product.respond_to?(k)
          product.send("#{k}=", v) if product.send(k).blank?
        end

        Effective::CartItem.new(quantity: oi.quantity, purchasable: product)
      end
    else
      raise 'add() expects one or more acts_as_purchasable objects, or an Effective::Cart'
    end
  end.compact

  # Make sure to reset stored aggregates
  self.total = nil
  self.subtotal = nil
  self.tax = nil

  retval = cart_items.map do |item|
    order_items.build(
      name: item.name,
      quantity: item.quantity,
      price: item.price,
      tax_exempt: (item.tax_exempt || false),
    ).tap { |order_item| order_item.purchasable = item.purchasable }
  end

  retval.size == 1 ? retval.first : retval
end

#assign_confirmed_if_valid!Object

This lets us skip to the confirmed workflow for an admin…



439
440
441
442
443
444
445
446
447
448
# File 'app/models/effective/order.rb', line 439

def assign_confirmed_if_valid!
  return unless pending?

  self.state = EffectiveOrders::CONFIRMED
  return true if valid?

  self.errors.clear
  self.state = EffectiveOrders::PENDING
  false
end

#billing_first_nameObject



326
327
328
# File 'app/models/effective/order.rb', line 326

def billing_first_name
  billing_name.to_s.split(' ').first
end

#billing_last_nameObject



330
331
332
# File 'app/models/effective/order.rb', line 330

def billing_last_name
  Array(billing_name.to_s.split(' ')[1..-1]).join(' ')
end

#confirm!Object

Used by admin checkout only



433
434
435
436
# File 'app/models/effective/order.rb', line 433

def confirm!
  return false if purchased?
  update!(state: EffectiveOrders::CONFIRMED)
end

#confirmed?Boolean

Returns:

  • (Boolean)


338
339
340
# File 'app/models/effective/order.rb', line 338

def confirmed?
  state == EffectiveOrders::CONFIRMED
end

#decline!(payment: 'none', provider: 'none', card: 'none', validate: true) ⇒ Object



495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'app/models/effective/order.rb', line 495

def decline!(payment: 'none', provider: 'none', card: 'none', validate: true)
  return false if declined?

  raise EffectiveOrders::AlreadyPurchasedException.new('order already purchased') if purchased?

  error = nil

  assign_attributes(
    state: EffectiveOrders::DECLINED,
    purchased_at: nil,
    payment: payment_to_h(payment),
    payment_provider: provider,
    payment_card: (card.presence || 'none'),
    skip_buyer_validations: true
  )

  EffectiveResources.transaction do
    begin
      run_purchasable_callbacks(:before_decline)
      save!(validate: validate)
      run_purchasable_callbacks(:after_decline)
    rescue => e
      self.state = state_was

      error = e.message
      raise ::ActiveRecord::Rollback
    end
  end

  raise "Failed to decline order: #{error || errors.full_messages.to_sentence}" unless error.nil?

  true
end

#declined?Boolean

Returns:

  • (Boolean)


352
353
354
# File 'app/models/effective/order.rb', line 352

def declined?
  state == EffectiveOrders::DECLINED
end

#defer!(provider: 'none', email: true) ⇒ Object



484
485
486
487
488
489
490
491
492
493
# File 'app/models/effective/order.rb', line 484

def defer!(provider: 'none', email: true)
  return false if purchased?

  assign_attributes(state: EffectiveOrders::DEFERRED, payment_provider: provider)
  save!

  send_payment_request_to_buyer! if email

  true
end

#deferred?Boolean

Returns:

  • (Boolean)


342
343
344
# File 'app/models/effective/order.rb', line 342

def deferred?
  state == EffectiveOrders::DEFERRED
end

#duplicateObject



317
318
319
# File 'app/models/effective/order.rb', line 317

def duplicate
  Effective::Order.new(self)
end

#emails_send_toObject

Doesn’t control anything. Purely for the flash messaging



530
531
532
# File 'app/models/effective/order.rb', line 530

def emails_send_to
  [email, cc.presence].compact.to_sentence
end

#free?Boolean

Returns:

  • (Boolean)


380
381
382
# File 'app/models/effective/order.rb', line 380

def free?
  total == 0
end

#labelObject



269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'app/models/effective/order.rb', line 269

def label
  if refund? && purchased?
    'Refund'
  elsif purchased?
    'Receipt'
  elsif refund? && (pending? || confirmed?)
    'Pending Refund'
  elsif (pending? || confirmed?)
    'Pending Order'
  else
    'Order'
  end
end

#num_itemsObject



388
389
390
# File 'app/models/effective/order.rb', line 388

def num_items
  order_items.map { |oi| oi.quantity }.sum
end

#payment_methodObject

Visa - 1234



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'app/models/effective/order.rb', line 298

def payment_method
  return nil unless purchased?

  # Normalize payment card
  card = case payment_card.to_s.downcase.gsub(' ', '').strip
    when '' then nil
    when 'v', 'visa' then 'Visa'
    when 'm', 'mc', 'master', 'mastercard' then 'MasterCard'
    when 'a', 'ax', 'american', 'americanexpress' then 'American Express'
    when 'd', 'discover' then 'Discover'
    else payment_card.to_s
  end unless payment_provider == 'free'

  # stripe, moneris, moneris_checkout
  last4 = (payment[:active_card] || payment['f4l4'] || payment['first6last4']).to_s.last(4)

  [card, '-', last4].compact.join(' ')
end

#pending!Object

This is called from admin/orders#create This is intended for use as an admin action only It skips any address or bad user validations It’s basically the same as save! on a new order, except it might send the payment request to buyer



421
422
423
424
425
426
427
428
429
430
# File 'app/models/effective/order.rb', line 421

def pending!
  return false if purchased?

  self.state = EffectiveOrders::PENDING
  self.addresses.clear if addresses.any? { |address| address.valid? == false }
  save!

  send_payment_request_to_buyer! if send_payment_request_to_buyer?
  true
end

#pending?Boolean

Returns:

  • (Boolean)


334
335
336
# File 'app/models/effective/order.rb', line 334

def pending?
  state == EffectiveOrders::PENDING
end

#purchasablesObject



360
361
362
# File 'app/models/effective/order.rb', line 360

def purchasables
  order_items.map { |order_item| order_item.purchasable }
end

#purchase!(payment: 'none', provider: 'none', card: 'none', email: true, skip_buyer_validations: false) ⇒ Object

Effective::Order.new(items: Product.first, user: User.first).purchase!(email: false)



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'app/models/effective/order.rb', line 451

def purchase!(payment: 'none', provider: 'none', card: 'none', email: true, skip_buyer_validations: false)
  # Assign attributes
  self.state = EffectiveOrders::PURCHASED
  self.skip_buyer_validations = skip_buyer_validations

  self.payment_provider ||= provider
  self.payment_card ||= (card.presence || 'none')
  self.purchased_at ||= Time.zone.now
  self.payment = payment_to_h(payment) if self.payment.blank?

  begin
    EffectiveResources.transaction do
      run_purchasable_callbacks(:before_purchase)

      save!
      update_purchasables_purchased_order!

      run_purchasable_callbacks(:after_purchase)
    end
  rescue => e
    EffectiveResources.transaction do
      save!(validate: false)
      update_purchasables_purchased_order!
    end

    raise(e)
  end

  send_order_receipts! if email

  true
end

#purchased?(provider = nil) ⇒ Boolean

Returns:

  • (Boolean)


346
347
348
349
350
# File 'app/models/effective/order.rb', line 346

def purchased?(provider = nil)
  return false if (state != EffectiveOrders::PURCHASED)
  return true if provider.nil? || payment_provider == provider.to_s
  false
end

#refund?Boolean

Returns:

  • (Boolean)


384
385
386
# File 'app/models/effective/order.rb', line 384

def refund?
  total.to_i < 0
end

#send_mark_as_paid_email_to_buyer?Boolean

Returns:

  • (Boolean)


409
410
411
# File 'app/models/effective/order.rb', line 409

def send_mark_as_paid_email_to_buyer?
  EffectiveResources.truthy?(send_mark_as_paid_email_to_buyer)
end

#send_order_receipt_to_admin!Object



540
541
542
# File 'app/models/effective/order.rb', line 540

def send_order_receipt_to_admin!
  send_email(:order_receipt_to_admin, to_param) if purchased?
end

#send_order_receipt_to_admin?Boolean

Returns:

  • (Boolean)


392
393
394
395
# File 'app/models/effective/order.rb', line 392

def send_order_receipt_to_admin?
  return false if free? && !EffectiveOrders.mailer[:send_order_receipts_when_free]
  EffectiveOrders.mailer[:send_order_receipt_to_admin]
end

#send_order_receipt_to_buyer!Object



544
545
546
# File 'app/models/effective/order.rb', line 544

def send_order_receipt_to_buyer!
  send_email(:order_receipt_to_buyer, to_param) if purchased?
end

#send_order_receipt_to_buyer?Boolean

Returns:

  • (Boolean)


397
398
399
400
# File 'app/models/effective/order.rb', line 397

def send_order_receipt_to_buyer?
  return false if free? && !EffectiveOrders.mailer[:send_order_receipts_when_free]
  EffectiveOrders.mailer[:send_order_receipt_to_buyer]
end

#send_order_receipts!Object



534
535
536
537
538
# File 'app/models/effective/order.rb', line 534

def send_order_receipts!
  send_order_receipt_to_admin! if send_order_receipt_to_admin?
  send_order_receipt_to_buyer! if send_order_receipt_to_buyer?
  send_refund_notification! if refund?
end

#send_payment_request_to_buyer!Object



548
549
550
# File 'app/models/effective/order.rb', line 548

def send_payment_request_to_buyer!
  send_email(:payment_request_to_buyer, to_param) unless purchased?
end

#send_payment_request_to_buyer?Boolean

Returns:

  • (Boolean)


402
403
404
405
406
407
# File 'app/models/effective/order.rb', line 402

def send_payment_request_to_buyer?
  return false if free? && !EffectiveOrders.mailer[:send_order_receipts_when_free]
  return false if refund?

  EffectiveResources.truthy?(send_payment_request_to_buyer)
end

#send_pending_order_invoice_to_buyer!Object



552
553
554
# File 'app/models/effective/order.rb', line 552

def send_pending_order_invoice_to_buyer!
  send_email(:pending_order_invoice_to_buyer, to_param) unless purchased?
end

#send_refund_notification!Object



556
557
558
# File 'app/models/effective/order.rb', line 556

def send_refund_notification!
  send_email(:refund_notification_to_admin, to_param) if purchased? && refund?
end

#skip_buyer_validations?Boolean

Returns:

  • (Boolean)


413
414
415
# File 'app/models/effective/order.rb', line 413

def skip_buyer_validations?
  EffectiveResources.truthy?(skip_buyer_validations)
end

#skip_qb_sync!Object



560
561
562
# File 'app/models/effective/order.rb', line 560

def skip_qb_sync!
  EffectiveOrders.use_effective_qb_sync ? EffectiveQbSync.skip_order!(self) : true
end

#subtotalObject



364
365
366
# File 'app/models/effective/order.rb', line 364

def subtotal
  self[:subtotal] || order_items.map { |oi| oi.subtotal }.sum
end

#taxObject



372
373
374
# File 'app/models/effective/order.rb', line 372

def tax
  self[:tax] || get_tax()
end

#tax_rateObject



368
369
370
# File 'app/models/effective/order.rb', line 368

def tax_rate
  self[:tax_rate] || get_tax_rate()
end

#to_sObject



265
266
267
# File 'app/models/effective/order.rb', line 265

def to_s
  [label, ' #', to_param].join
end

#totalObject



376
377
378
# File 'app/models/effective/order.rb', line 376

def total
  (self[:total] || (subtotal + tax.to_i)).to_i
end

#total_labelObject



283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'app/models/effective/order.rb', line 283

def total_label
  if refund? && purchased?
    'Total Paid'
  elsif purchased?
    'Total Paid'
  elsif refund? && (pending? || confirmed?)
    'Total Due'
  elsif (pending? || confirmed?)
    'Total Due'
  else
    'Total'
  end
end

#transaction_idObject

For moneris and moneris_checkout. Just a unique value.



322
323
324
# File 'app/models/effective/order.rb', line 322

def transaction_id
  [to_param, billing_name.to_s.parameterize.presence, Time.zone.now.to_i].compact.join('-')
end

#update_prices!Object



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'app/models/effective/order.rb', line 248

def update_prices!
  raise('already purchased') if purchased?
  raise('must be pending or confirmed') unless pending? || confirmed?

  order_items.each do |item|
    purchasable = item.purchasable

    if purchasable.blank? || purchasable.marked_for_destruction?
      item.mark_for_destruction
    else
      item.price = purchasable.price
    end
  end

  save!
end