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
144
# 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.shipping_address_same_as_billing = 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

#shipping_address_same_as_billingObject

save these addresses to the user if selected



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

def shipping_address_same_as_billing
  @shipping_address_same_as_billing
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



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 148

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



301
302
303
304
305
306
307
308
309
# File 'app/models/effective/order.rb', line 301

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



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

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



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

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)


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

def declined?
  purchase_state == EffectiveOrders::DECLINED
end

#num_itemsObject



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

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”}}



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

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)


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

def pending?
  purchase_state == EffectiveOrders::PENDING
end

#purchasablesObject



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

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)



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

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'

      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)


376
377
378
379
380
381
382
383
# File 'app/models/effective/order.rb', line 376

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)


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

def save_billing_address?
  ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(self.save_billing_address)
end

#save_shipping_address?Boolean

Returns:

  • (Boolean)


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

def save_shipping_address?
  ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(self.save_shipping_address)
end

#send_mark_as_paid_email_to_buyer?Boolean

Returns:

  • (Boolean)


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

def send_mark_as_paid_email_to_buyer?
  ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(self.send_mark_as_paid_email_to_buyer)
end

#send_order_receipt_to_admin!Object



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

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

#send_order_receipt_to_buyer!Object



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

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

#send_order_receipt_to_seller!Object



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

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



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

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



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

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)


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

def send_payment_request_to_buyer?
  ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(self.send_payment_request_to_buyer)
end

#send_pending_order_invoice_to_buyer!Object



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

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

#shipping_address_same_as_billing?Boolean

Returns:

  • (Boolean)


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

def shipping_address_same_as_billing?
  ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(self.shipping_address_same_as_billing)
end

#skip_buyer_validations?Boolean

Returns:

  • (Boolean)


297
298
299
# File 'app/models/effective/order.rb', line 297

def skip_buyer_validations?
  ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(self.skip_buyer_validations)
end

#subtotalObject



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

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

#taxObject



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

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

#tax_rateObject



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

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

#totalObject



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

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

#user=(user) ⇒ Object



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

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