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)



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'app/models/effective/order.rb', line 135

def initialize(atts = nil, &block)
  super(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



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

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



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

def send_payment_request_to_buyer
  @send_payment_request_to_buyer
end

#skip_buyer_validationsObject

Set by Admin::Orders#create



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

def skip_buyer_validations
  @skip_buyer_validations
end

#terms_and_conditionsObject

Yes, I agree to the terms and conditions



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

def terms_and_conditions
  @terms_and_conditions
end

Instance Method Details

#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



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
194
195
196
197
198
199
200
201
202
203
204
205
# File 'app/models/effective/order.rb', line 167

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(name: oi.purchasable.purchasable_name, 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(
      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…



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

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

#confirm!Object



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

def confirm!
  self.state = EffectiveOrders::CONFIRMED
  save!
end

#confirmed?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'app/models/effective/order.rb', line 223

def confirmed?
  state == EffectiveOrders::CONFIRMED
end

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



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
375
376
377
378
379
380
# File 'app/models/effective/order.rb', line 348

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
  )

  Effective::Order.transaction do
    begin
      save!(validate: validate)
    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?

  run_purchasable_callbacks(:after_decline)

  true
end

#declined?Boolean

Returns:

  • (Boolean)


233
234
235
# File 'app/models/effective/order.rb', line 233

def declined?
  state == EffectiveOrders::DECLINED
end

#free?Boolean

Returns:

  • (Boolean)


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

def free?
  total == 0
end

#num_itemsObject



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

def num_items
  order_items.map { |oi| oi.quantity }.sum
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



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

def pending!
  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)


219
220
221
# File 'app/models/effective/order.rb', line 219

def pending?
  state == EffectiveOrders::PENDING
end

#purchasablesObject



237
238
239
# File 'app/models/effective/order.rb', line 237

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)



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

def purchase!(payment: 'none', provider: 'none', card: 'none', email: true, skip_buyer_validations: false)
  return false if purchased?
  error = nil

  assign_attributes(
    state: EffectiveOrders::PURCHASED,
    purchased_at: Time.zone.now,
    payment: payment_to_h(payment),
    payment_provider: provider,
    payment_card: (card.presence || 'none'),
    skip_buyer_validations: skip_buyer_validations
  )

  Effective::Order.transaction do
    begin
      run_purchasable_callbacks(:before_purchase)
      save!
      update_purchasables_purchased_order!
    rescue => e
      self.state = state_was
      self.purchased_at = nil

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

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

  send_order_receipts! if email

  run_purchasable_callbacks(:after_purchase)

  true
end

#purchased?(provider = nil) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

def refund?
  total.to_i < 0
end

#send_mark_as_paid_email_to_buyer?Boolean

Returns:

  • (Boolean)


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

def send_mark_as_paid_email_to_buyer?
  truthy?(send_mark_as_paid_email_to_buyer)
end

#send_order_receipt_to_admin!Object



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

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

#send_order_receipt_to_buyer!Object



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

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

#send_order_receipts!Object



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

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]
end

#send_payment_request_to_buyer!Object



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

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)


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

def send_payment_request_to_buyer?
  truthy?(send_payment_request_to_buyer) && !free? && !refund?
end

#send_pending_order_invoice_to_buyer!Object



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

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)


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

def skip_buyer_validations?
  truthy?(skip_buyer_validations)
end

#skip_qb_sync!Object



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

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

#subtotalObject



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

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

#taxObject



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

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

#tax_rateObject



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

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

#to_sObject



207
208
209
210
211
212
213
214
215
216
217
# File 'app/models/effective/order.rb', line 207

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



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

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