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



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'app/models/effective/order.rb', line 123

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

  # Set up defaults
  self.save_billing_address = true
  self.save_shipping_address = true

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

  if billing_address
    self.billing_address = billing_address
    self.billing_address.full_name ||= billing_name
  end

  if shipping_address
    self.shipping_address = shipping_address
    self.shipping_address.full_name ||= billing_name
  end

  add(items) if items.present?
end

Instance Attribute Details

#save_billing_addressObject

save these addresses to the user if selected



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

def save_billing_address
  @save_billing_address
end

#save_shipping_addressObject

save these addresses to the user if selected



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

def save_shipping_address
  @save_shipping_address
end

#send_mark_as_paid_email_to_buyerObject

Used by the /admin/orders/mark_as_paid action



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

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



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

def send_payment_request_to_buyer
  @send_payment_request_to_buyer
end

#skip_buyer_validationsObject

Enabled by the /admin/orders/create action



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

def skip_buyer_validations
  @skip_buyer_validations
end

#terms_and_conditionsObject

Yes, I agree to the terms and conditions



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

def terms_and_conditions
  @terms_and_conditions
end

Instance Method Details

#add(*items, quantity: 1) ⇒ Object Also known as: add_to_order

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



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
# File 'app/models/effective/order.rb', line 147

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.to_i).tap { |cart_item| cart_item.purchasable = item }
    else
      raise ArgumentError.new('Effective::Order.add() expects one or more acts_as_purchasable objects, or an Effective::Cart')
    end
  end

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



296
297
298
299
300
301
302
303
304
# File 'app/models/effective/order.rb', line 296

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_pendingObject

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



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

def create_as_pending
  self.purchase_state = EffectiveOrders::PENDING

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

  return false unless save

  send_payment_request_to_buyer! if send_payment_request_to_buyer?
  true
end

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



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'app/models/effective/order.rb', line 343

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

  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)

      order_items.each { |item| (item.purchasable.declined!(self, item) rescue nil) }

      success = true
    rescue => e
      raise ::ActiveRecord::Rollback
    end
  end

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

#declined?Boolean

Returns:

  • (Boolean)


382
383
384
# File 'app/models/effective/order.rb', line 382

def declined?
  purchase_state == EffectiveOrders::DECLINED
end

#num_itemsObject



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

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

#order_items_attributes=(order_item_attributes) ⇒ Object

This is used for updating Subscription codes. We want to update the underlying purchasable object of an OrderItem Passing the order_item_attributes using rails default acts_as_nested creates a new object instead of updating the temporary one. So we override this method to do the updates on the non-persisted OrderItem objects Right now strong_paramaters only lets through stripe_coupon_id “stripe_coupon_id”=>“50OFF”, “id”=>“2”}}



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'app/models/effective/order.rb', line 234

def order_items_attributes=(order_item_attributes)
  if self.persisted? == false
    (order_item_attributes || {}).each do |_, atts|
      order_item = self.order_items.find { |oi| oi.purchasable.class.name == atts[:class] && oi.purchasable.id == atts[:id].to_i }

      if order_item
        order_item.purchasable.attributes = atts.except(:id, :class)

        # Recalculate the OrderItem based on the updated purchasable object
        order_item.title = order_item.purchasable.title
        order_item.price = order_item.purchasable.price
        order_item.tax_exempt = order_item.purchasable.tax_exempt
        order_item.seller_id = (order_item.purchasable.try(:seller).try(:id) rescue nil)
      end
    end
  end
end

#pending?Boolean

Returns:

  • (Boolean)


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

def pending?
  purchase_state == EffectiveOrders::PENDING
end

#purchasablesObject



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

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)



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
# File 'app/models/effective/order.rb', line 308

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

  success = false

  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

      save!(validate: validate)

      order_items.each { |item| (item.purchasable.purchased!(self, item) rescue nil) }

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

      raise ::ActiveRecord::Rollback
    end
  end

  send_order_receipts! if (success && email)

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

#purchased?(provider = nil) ⇒ Boolean

Returns:

  • (Boolean)


373
374
375
376
377
378
379
380
# File 'app/models/effective/order.rb', line 373

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

#save_billing_address?Boolean

Returns:

  • (Boolean)


276
277
278
# File 'app/models/effective/order.rb', line 276

def save_billing_address?
  truthy?(self.save_billing_address)
end

#save_shipping_address?Boolean

Returns:

  • (Boolean)


280
281
282
# File 'app/models/effective/order.rb', line 280

def save_shipping_address?
  truthy?(self.save_shipping_address)
end

#send_mark_as_paid_email_to_buyer?Boolean

Returns:

  • (Boolean)


288
289
290
# File 'app/models/effective/order.rb', line 288

def send_mark_as_paid_email_to_buyer?
  truthy?(self.send_mark_as_paid_email_to_buyer)
end

#send_order_receipt_to_admin!Object



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

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

#send_order_receipt_to_buyer!Object



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

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

#send_order_receipt_to_seller!Object



404
405
406
407
408
409
410
# File 'app/models/effective/order.rb', line 404

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

  order_items.group_by(&:seller).each do |seller, order_items|
    send_email(:order_receipt_to_seller, to_param, seller, order_items)
  end
end

#send_order_receipts!Object



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

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



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

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

#send_payment_request_to_buyer?Boolean

Returns:

  • (Boolean)


284
285
286
# File 'app/models/effective/order.rb', line 284

def send_payment_request_to_buyer?
  truthy?(self.send_payment_request_to_buyer)
end

#send_pending_order_invoice_to_buyer!Object



416
417
418
# File 'app/models/effective/order.rb', line 416

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

#skip_buyer_validations?Boolean

Returns:

  • (Boolean)


292
293
294
# File 'app/models/effective/order.rb', line 292

def skip_buyer_validations?
  truthy?(self.skip_buyer_validations)
end

#subtotalObject



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

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

#taxObject



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

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

#tax_rateObject



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

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

#totalObject



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

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

#user=(user) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'app/models/effective/order.rb', line 180

def user=(user)
  return if user.nil?

  super

  # 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

  # If our addresses are required, make sure they exist
  if EffectiveOrders.require_billing_address
    self.billing_address ||= Effective::Address.new()
  end

  if EffectiveOrders.require_shipping_address
    self.shipping_address ||= Effective::Address.new()
  end

  # Ensure the Full Name is assigned when an address exists
  if billing_address.present? && billing_address.full_name.blank?
    self.billing_address.full_name = billing_name
  end

  if shipping_address.present? && shipping_address.full_name.blank?
    self.shipping_address.full_name = billing_name
  end
end