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)



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/models/effective/order.rb', line 124

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

  return unless atts.present?

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

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

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

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

    atts.each { |key, value| self.send("#{key}=", value) }

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

Instance Attribute Details

#send_mark_as_paid_email_to_buyerObject

Set by Admin::Orders#mark_as_paid



18
19
20
# File 'app/models/effective/order.rb', line 18

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



17
18
19
# File 'app/models/effective/order.rb', line 17

def send_payment_request_to_buyer
  @send_payment_request_to_buyer
end

#skip_buyer_validationsObject

Set by Admin::Orders#create



19
20
21
# File 'app/models/effective/order.rb', line 19

def skip_buyer_validations
  @skip_buyer_validations
end

#skip_minimum_charge_validationObject

Set by Admin::Orders#show



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

def skip_minimum_charge_validation
  @skip_minimum_charge_validation
end

#terms_and_conditionsObject

Yes, I agree to the terms and conditions



14
15
16
# File 'app/models/effective/order.rb', line 14

def terms_and_conditions
  @terms_and_conditions
end

Instance Method Details

#abandoned?Boolean

Returns:

  • (Boolean)


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

def abandoned?
  purchase_state == EffectiveOrders::ABANDONED
end

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

add(Product.first) => returns an Effective::OrderItem add(Product.first, current_cart) => returns an array of Effective::OrderItems



155
156
157
158
159
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
192
193
# File 'app/models/effective/order.rb', line 155

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

      item.order_items.select { |oi| oi.purchasable.kind_of?(Effective::Product) }.map do |oi|
        product = Effective::Product.new(title: oi.purchasable.title, price: oi.purchasable.price, tax_exempt: oi.purchasable.tax_exempt)
        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(
      title: item.title,
      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

#billing_nameObject



290
291
292
293
294
295
296
297
298
# File 'app/models/effective/order.rb', line 290

def billing_name
  name ||= billing_address.try(:full_name).presence
  name ||= user.try(:full_name).presence
  name ||= (user.try(:first_name).to_s + ' ' + user.try(:last_name).to_s).presence
  name ||= user.try(:email).presence
  name ||= user.to_s
  name ||= "User #{user.try(:id)}"
  name
end

#create_as_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



215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'app/models/effective/order.rb', line 215

def create_as_pending!
  return false unless new_record?

  self.purchase_state = EffectiveOrders::PENDING

  self.skip_buyer_validations = true
  self.addresses.clear if addresses.any? { |address| address.valid? == false }

  save!

  send_payment_request_to_buyer! if send_payment_request_to_buyer?

  true
end

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



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'app/models/effective/order.rb', line 351

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

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

  success = false
  error = nil

  Effective::Order.transaction do
    begin
      self.purchase_state = EffectiveOrders::DECLINED
      self.purchased_at = nil

      self.payment = details.kind_of?(Hash) ? details : { details: details.to_s }
      self.payment_provider = provider.to_s
      self.payment_card = card.to_s.presence || 'none'

      save!(validate: validate)

      success = true
    rescue => e
      self.purchase_state = purchase_state_was
      self.purchased_at = purchased_at_was

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

  raise "Failed to decline! Effective::Order: #{error || errors.full_messages.to_sentence}" unless success

  run_purchasable_callbacks(:after_decline)

  true
end

#declined?Boolean

Returns:

  • (Boolean)


400
401
402
# File 'app/models/effective/order.rb', line 400

def declined?
  purchase_state == EffectiveOrders::DECLINED
end

#free?Boolean

Returns:

  • (Boolean)


242
243
244
# File 'app/models/effective/order.rb', line 242

def free?
  total == 0
end

#num_itemsObject



270
271
272
# File 'app/models/effective/order.rb', line 270

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

#pending?Boolean

Returns:

  • (Boolean)


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

def pending?
  purchase_state == EffectiveOrders::PENDING
end

#purchasablesObject



250
251
252
# File 'app/models/effective/order.rb', line 250

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

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

Effective::Order.new(Product.first, user: User.first).purchase!(details: ‘manual purchase’) order.purchase!(details: value)



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'app/models/effective/order.rb', line 302

def purchase!(details: 'none', provider: 'none', card: 'none', validate: true, email: true, skip_buyer_validations: false)
  return false if purchased?

  success = false
  error = nil

  Effective::Order.transaction do
    begin
      self.purchase_state = EffectiveOrders::PURCHASED
      self.purchased_at ||= Time.zone.now

      self.payment = (
        if details.kind_of?(Hash)
          details
        elsif details.respond_to?(:to_unsafe_h)
          details.to_unsafe_h
        else
          { details: details.to_s }
        end
      )

      self.payment_provider = provider.to_s
      self.payment_card = card.to_s.presence || 'none'

      self.skip_buyer_validations = skip_buyer_validations

      run_purchasable_callbacks(:before_purchase)

      save!(validate: validate)

      success = true
    rescue => e
      self.purchase_state = purchase_state_was
      self.purchased_at = purchased_at_was

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

  raise "Failed to purchase Effective::Order: #{error || errors.full_messages.to_sentence}" unless success

  send_order_receipts! if email

  run_purchasable_callbacks(:after_purchase)

  true
end

#purchased?(provider = nil) ⇒ Boolean

Returns:

  • (Boolean)


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

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

  unless EffectiveOrder.payment_providers.include?(provider.to_s)
    raise "Unknown provider #{provider}. Known providers are #{EffectiveOrders.payment_providers}"
  end
end

#refund?Boolean

Returns:

  • (Boolean)


246
247
248
# File 'app/models/effective/order.rb', line 246

def refund?
  total.to_i < 0
end

#send_mark_as_paid_email_to_buyer?Boolean

Returns:

  • (Boolean)


278
279
280
# File 'app/models/effective/order.rb', line 278

def send_mark_as_paid_email_to_buyer?
  truthy?(send_mark_as_paid_email_to_buyer)
end

#send_order_receipt_to_admin!Object



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

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

#send_order_receipt_to_buyer!Object



418
419
420
# File 'app/models/effective/order.rb', line 418

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

#send_order_receipt_to_seller!Object



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

def send_order_receipt_to_seller!
  return false unless (EffectiveOrders.stripe_connect_enabled && purchased?(:stripe_connect))

  order_items.group_by { |oi| oi.seller }.each do |seller, order_items|
    send_email(:order_receipt_to_seller, to_param, seller, order_items)
  end
end

#send_order_receipts!Object



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

def send_order_receipts!
  send_order_receipt_to_admin! if EffectiveOrders.mailer[:send_order_receipt_to_admin]
  send_order_receipt_to_buyer! if EffectiveOrders.mailer[:send_order_receipt_to_buyer]
  send_order_receipt_to_seller! if EffectiveOrders.mailer[:send_order_receipt_to_seller]
end

#send_payment_request_to_buyer!Object



430
431
432
# File 'app/models/effective/order.rb', line 430

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)


274
275
276
# File 'app/models/effective/order.rb', line 274

def send_payment_request_to_buyer?
  truthy?(send_payment_request_to_buyer)
end

#send_pending_order_invoice_to_buyer!Object



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

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

#skip_buyer_validations?Boolean

Returns:

  • (Boolean)


282
283
284
# File 'app/models/effective/order.rb', line 282

def skip_buyer_validations?
  truthy?(skip_buyer_validations)
end

#skip_minimum_charge_validation?Boolean

Returns:

  • (Boolean)


286
287
288
# File 'app/models/effective/order.rb', line 286

def skip_minimum_charge_validation?
  truthy?(skip_minimum_charge_validation) || skip_buyer_validations?
end

#subtotalObject



262
263
264
# File 'app/models/effective/order.rb', line 262

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

#taxObject



258
259
260
# File 'app/models/effective/order.rb', line 258

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

#tax_rateObject



254
255
256
# File 'app/models/effective/order.rb', line 254

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

#to_sObject



230
231
232
233
234
235
236
237
238
239
240
# File 'app/models/effective/order.rb', line 230

def to_s
  if refund?
    "Refund ##{to_param}"
  elsif purchased?
    "Receipt ##{to_param}"
  elsif pending?
    "Pending Order ##{to_param}"
  else
    "Order ##{to_param}"
  end
end

#totalObject



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

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

#user=(user) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'app/models/effective/order.rb', line 195

def user=(user)
  super

  return unless user.present?

  # Copy user addresses into this order if they are present
  if user.respond_to?(:billing_address) && user.billing_address.present?
    self.billing_address = user.billing_address
  end

  if user.respond_to?(:shipping_address) && user.shipping_address.present?
    self.shipping_address = user.shipping_address
  end

  user
end