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(*items, user: nil, billing_address: nil, shipping_address: nil) ⇒ Order

items can be an Effective::Cart, a single acts_as_purchasable, or an array of acts_as_purchasables



124
125
126
127
128
129
130
131
132
133
134
135
# File 'app/models/effective/order.rb', line 124

def initialize(*items, user: nil, billing_address: nil, shipping_address: nil)
  super() # Call super with no arguments

  # Assign user
  self.user = user || (items.first.user if items.first.kind_of?(Effective::Cart))

  # Assign addresses
  self.billing_address = billing_address if billing_address
  self.shipping_address = shipping_address if shipping_address

  add(items) if items.present?
end

Instance Attribute Details

#send_mark_as_paid_email_to_buyer ⇒ Object

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_buyer ⇒ Object

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_validations ⇒ Object

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_validation ⇒ Object

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_conditions ⇒ Object

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)


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

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



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'app/models/effective/order.rb', line 139

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,
      seller_id: (item.purchasable.try(:seller).try(:id) rescue nil)
    ).tap { |order_item| order_item.purchasable = item.purchasable }
  end

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

#billing_name ⇒ Object



275
276
277
278
279
280
281
282
283
# File 'app/models/effective/order.rb', line 275

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



200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'app/models/effective/order.rb', line 200

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



327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'app/models/effective/order.rb', line 327

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)


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

def declined?
  purchase_state == EffectiveOrders::DECLINED
end

#free? ⇒ Boolean

Returns:

  • (Boolean)


227
228
229
# File 'app/models/effective/order.rb', line 227

def free?
  total == 0
end

#num_items ⇒ Object



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

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

#pending? ⇒ Boolean

Returns:

  • (Boolean)


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

def pending?
  purchase_state == EffectiveOrders::PENDING
end

#purchasables ⇒ Object



235
236
237
# File 'app/models/effective/order.rb', line 235

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)



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'app/models/effective/order.rb', line 287

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 = details.kind_of?(Hash) ? details : { details: details.to_s }
      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)


367
368
369
370
371
372
373
374
# File 'app/models/effective/order.rb', line 367

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)


231
232
233
# File 'app/models/effective/order.rb', line 231

def refund?
  total.to_i < 0
end

#send_mark_as_paid_email_to_buyer? ⇒ Boolean

Returns:

  • (Boolean)


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

def send_mark_as_paid_email_to_buyer?
  truthy?(send_mark_as_paid_email_to_buyer)
end

#send_order_receipt_to_admin! ⇒ Object



390
391
392
# File 'app/models/effective/order.rb', line 390

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

#send_order_receipt_to_buyer! ⇒ Object



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

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

#send_order_receipt_to_seller! ⇒ Object



398
399
400
401
402
403
404
# File 'app/models/effective/order.rb', line 398

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



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

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



406
407
408
# File 'app/models/effective/order.rb', line 406

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)


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

def send_payment_request_to_buyer?
  truthy?(send_payment_request_to_buyer)
end

#send_pending_order_invoice_to_buyer! ⇒ Object



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

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)


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

def skip_buyer_validations?
  truthy?(skip_buyer_validations)
end

#skip_minimum_charge_validation? ⇒ Boolean

Returns:

  • (Boolean)


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

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

#subtotal ⇒ Object



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

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

#tax ⇒ Object



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

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

#tax_rate ⇒ Object



239
240
241
# File 'app/models/effective/order.rb', line 239

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

#to_s ⇒ Object



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

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

#total ⇒ Object



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

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

#user=(user) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'app/models/effective/order.rb', line 180

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