Class: Shoppe::Order

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/shoppe/order.rb

Constant Summary collapse

STATUSES =

An array of all the available statuses for an order

['building', 'confirming', 'received', 'accepted', 'rejected', 'shipped']

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.ransackable_associations(auth_object = nil) ⇒ Object

Specify which associations can be searched



341
342
343
# File 'app/models/shoppe/order.rb', line 341

def self.ransackable_associations(auth_object = nil)
  ['products']
end

.ransackable_attributes(auth_object = nil) ⇒ Object

Specify which attributes can be searched



336
337
338
# File 'app/models/shoppe/order.rb', line 336

def self.ransackable_attributes(auth_object = nil) 
  ["id", "postcode", "address1", "address2", "address3", "address4", "first_name", "last_name", "company", "email_address", "phone_number", "consignment_number", "status", "received_at"] + _ransackers.keys
end

Instance Method Details

#accept!(user) ⇒ Object

This method will accept the this order. It is called by a user (which is the only parameter).



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

def accept!(user)
  run_callbacks :acceptance do
    self.accepted_at = Time.now
    self.accepted_by = user.id
    self.status = 'accepted'
    self.save!
    self.order_items.each(&:accept!)
    Shoppe::OrderMailer.accepted(self).deliver
  end
end

#accepted?Boolean

Has this order been accepted?

Returns:

  • (Boolean)


67
68
69
# File 'app/models/shoppe/order.rb', line 67

def accepted?
  !!self.accepted_at
end

#available_delivery_servicesObject

An array of all the delivery services which are suitable for this order in it’s current state (based on its current weight)



162
163
164
165
166
# File 'app/models/shoppe/order.rb', line 162

def available_delivery_services
  @available_delivery_services ||= begin
    delivery_service_prices.map(&:delivery_service).uniq
  end
end

#build_timeObject

The length of time the customer spent building the order before submitting it to us. The time from first item in basket to received.



88
89
90
91
# File 'app/models/shoppe/order.rb', line 88

def build_time
  return nil if self.received_at.blank?
  self.created_at - self.received_at
end

#building?Boolean

Is this order still being built by the user?

Returns:

  • (Boolean)


52
53
54
# File 'app/models/shoppe/order.rb', line 52

def building?
  self.status == 'building'
end

#confirm!Object

This method will confirm the order If there are any issues with the order an exception should be raised.



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'app/models/shoppe/order.rb', line 245

def confirm!
  
  # Ensure that we have the stock to fulfil this order at the current time. We may have had it when
  # it was placed int he basket and if we don't now, we should let the user know so they can
  # rethink.
  no_stock_of = self.order_items.select(&:validate_stock_levels)
  unless no_stock_of.empty?
    raise Shoppe::Errors::InsufficientStockToFulfil, :order => self, :out_of_stock_items => no_stock_of
  end
  
  # Ensure that before we confirm the order that the delivery service which has been selected
  # is appropritae for the contents of the order.
  unless self.valid_delivery_service?
    raise Shoppe::Errors::InappropriateDeliveryService, :order => self
  end
  
  # Store the delivery prices with the order
  if self.delivery_service
    write_attribute :delivery_service_id, self.delivery_service.id
    write_attribute :delivery_price, self.delivery_price
    write_attribute :delivery_cost_price, self.delivery_cost_price
    write_attribute :delivery_tax_amount, self.delivery_tax_amount
    write_attribute :delivery_tax_rate, self.delivery_tax_rate
  end
    
  run_callbacks :confirmation do
    # If we have successfully charged the card (i.e. no exception) we can go ahead and mark this
    # order as 'received' which means it can be accepted by staff.
    self.status = 'received'
    self.received_at = Time.now
    self.save!

    self.order_items.each(&:confirm!)

    # Send an email to the customer
    Shoppe::OrderMailer.received(self).deliver
  end
  
  # We're all good.
  true
end

#confirming?Boolean

Is this order in the user confirmation step?

Returns:

  • (Boolean)


57
58
59
# File 'app/models/shoppe/order.rb', line 57

def confirming?
  self.status == 'confirming'
end

#courier_tracking_urlObject

The URL which can be used to track the delivery of this order



220
221
222
223
# File 'app/models/shoppe/order.rb', line 220

def courier_tracking_url
  return nil if self.shipped_at.blank? || self.consignment_number.blank?
  @courier_tracking_url ||= self.delivery_service.tracking_url_for(self.consignment_number)
end

#customer_nameObject

The name of the customer



94
95
96
# File 'app/models/shoppe/order.rb', line 94

def customer_name
  company.blank? ? "#{first_name} #{last_name}" : "#{company} (#{first_name} #{last_name})"
end

#delivery_cost_priceObject

The cost of delivering this order in its current state



184
185
186
# File 'app/models/shoppe/order.rb', line 184

def delivery_cost_price
  @delivery_cost_price ||= read_attribute(:delivery_cost_price) || delivery_service_price.try(:cost_price) || 0.0
end

#delivery_priceObject

The price for delivering this order in its current state



179
180
181
# File 'app/models/shoppe/order.rb', line 179

def delivery_price
  @delivery_price ||= read_attribute(:delivery_price) || delivery_service_price.try(:price) || 0.0
end

#delivery_serviceObject

The recommended delivery service for this order



169
170
171
# File 'app/models/shoppe/order.rb', line 169

def delivery_service
  super || available_delivery_services.first
end

#delivery_service_priceObject

Return the delivery price for this order in its current state



174
175
176
# File 'app/models/shoppe/order.rb', line 174

def delivery_service_price
  @delivery_service_price ||= self.delivery_service && self.delivery_service.delivery_service_prices.for_weight(self.total_weight).first
end

#delivery_service_pricesObject

An array of all the delivery service prices which can be applied to this order.



154
155
156
157
158
# File 'app/models/shoppe/order.rb', line 154

def delivery_service_prices
  @delivery_service_prices ||= begin
    Shoppe::DeliveryServicePrice.joins(:delivery_service).where(:shoppe_delivery_services => {:active => true}).order("`default` desc, price asc").for_weight(total_weight)
  end
end

#delivery_tax_amountObject

The tax amount due for the delivery of this order in its current state



189
190
191
192
193
194
195
# File 'app/models/shoppe/order.rb', line 189

def delivery_tax_amount
  @delivery_tax_amount ||= begin
    read_attribute(:delivery_tax_amount) ||
    (delivery_service_price && delivery_service_price.tax_rate ? delivery_price / BigDecimal(100) * delivery_service_price.tax_rate.rate : 0.0) ||
    0.0
  end
end

#delivery_tax_rateObject

The tax rate for the delivery of this order in its current state



198
199
200
201
202
203
204
# File 'app/models/shoppe/order.rb', line 198

def delivery_tax_rate
  @delivery_tax_rate ||= begin
    read_attribute(:delivery_tax_rate) ||
    delivery_service_price.try(:tax_rate).try(:rate) ||
    0.0
  end
end

#empty?Boolean

Is this order empty? (i.e. doesn’t have any items associated with it)

Returns:

  • (Boolean)


99
100
101
# File 'app/models/shoppe/order.rb', line 99

def empty?
  order_items.empty?
end

#has_items?Boolean

Does this order have items?

Returns:

  • (Boolean)


104
105
106
# File 'app/models/shoppe/order.rb', line 104

def has_items?
  total_items > 0
end

#numberObject

The order number



82
83
84
# File 'app/models/shoppe/order.rb', line 82

def number
  id.to_s.rjust(6, '0')
end

#paid?Boolean

Has this order been fully paid for?

Returns:

  • (Boolean)


226
227
228
# File 'app/models/shoppe/order.rb', line 226

def paid?
  !paid_at.blank?
end

#pay!(reference, method) ⇒ Object

This method will mark an order as paid.



288
289
290
291
292
293
294
295
# File 'app/models/shoppe/order.rb', line 288

def pay!(reference, method)
  run_callbacks :payment do
    self.paid_at = Time.now.utc
    self.payment_reference = reference
    self.payment_method = method
    self.save!
  end
end

#proceed_to_confirm(params = {}) ⇒ Object

This method is called by the customer when they submit their details in the first step of the checkout process. It will update the status to ‘confirmed’ as well as updating their details. Any issues with validation will cause false to be returned otherwise true. Any more serious issues will be raised as exceptions.



234
235
236
237
238
239
240
241
# File 'app/models/shoppe/order.rb', line 234

def proceed_to_confirm(params = {})
  self.status = 'confirming'
  if self.update(params)
    true
  else
    false
  end
end

#profitObject

Return the price for the order



120
121
122
# File 'app/models/shoppe/order.rb', line 120

def profit
  total_before_tax - total_cost
end

#received?Boolean

Has the order been received?

Returns:

  • (Boolean)


77
78
79
# File 'app/models/shoppe/order.rb', line 77

def received?
  !!self.received_at?
end

#reject!(user) ⇒ Object

This method will reject the order. It is called by a user (which is the only parameter).



311
312
313
314
315
316
317
318
319
320
# File 'app/models/shoppe/order.rb', line 311

def reject!(user)
  run_callbacks :rejection do
    self.rejected_at = Time.now
    self.rejected_by = user.id
    self.status = 'rejected'
    self.save!
    self.order_items.each(&:reject!)
    Shoppe::OrderMailer.rejected(self).deliver
  end
end

#rejected?Boolean

Has this order been rejected?

Returns:

  • (Boolean)


62
63
64
# File 'app/models/shoppe/order.rb', line 62

def rejected?
  !!self.rejected_at
end

#remove_delivery_service_if_invalidObject

Remove the associated delivery service if it’s invalid



212
213
214
215
216
217
# File 'app/models/shoppe/order.rb', line 212

def remove_delivery_service_if_invalid
  unless self.valid_delivery_service?
    self.delivery_service = nil
    self.save
  end
end

#ship!(user, consignment_number) ⇒ Object

This method will mark an order as shipped and store the given consignment number with the order for use later in tracking.



324
325
326
327
328
329
330
331
332
333
# File 'app/models/shoppe/order.rb', line 324

def ship!(user, consignment_number)
  run_callbacks :ship do
    self.shipped_at = Time.now
    self.shipped_by = user.id
    self.status = 'shipped'
    self.consignment_number = consignment_number
    self.save!
    Shoppe::OrderMailer.shipped(self).deliver
  end
end

#shipped?Boolean

Has this order been shipped?

Returns:

  • (Boolean)


72
73
74
# File 'app/models/shoppe/order.rb', line 72

def shipped?
  !!self.shipped_at?
end

#taxObject

The total amount of tax due on this order



131
132
133
134
# File 'app/models/shoppe/order.rb', line 131

def tax
  self.delivery_tax_amount +
  order_items.inject(BigDecimal(0)) { |t, i| t + i.tax_amount }
end

#totalObject

The total of the order including tax



137
138
139
140
141
# File 'app/models/shoppe/order.rb', line 137

def total
  self.delivery_price + 
  self.delivery_tax_amount + 
  order_items.inject(BigDecimal(0)) { |t, i| t + i.total }
end

#total_before_taxObject

The total price of the order before tax



125
126
127
128
# File 'app/models/shoppe/order.rb', line 125

def total_before_tax
  self.delivery_price +
  order_items.inject(BigDecimal(0)) { |t, i| t + i.sub_total }
end

#total_costObject

The total cost of the order



114
115
116
117
# File 'app/models/shoppe/order.rb', line 114

def total_cost
  self.delivery_cost_price + 
  order_items.inject(BigDecimal(0)) { |t, i| t + i.total_cost }
end

#total_in_penceObject

The total of the order including tax in pence



144
145
146
# File 'app/models/shoppe/order.rb', line 144

def total_in_pence
  (total * BigDecimal(100)).to_i
end

#total_itemsObject

Return the number of items in the order?



109
110
111
# File 'app/models/shoppe/order.rb', line 109

def total_items
  @total_items ||= order_items.inject(0) { |t,i| t + i.quantity }
end

#total_weightObject

The total weight of the order



149
150
151
# File 'app/models/shoppe/order.rb', line 149

def total_weight
  order_items.inject(BigDecimal(0)) { |t,i| t + i.weight}
end

#valid_delivery_service?Boolean

Is the currently assigned delivery service appropriate for this order?

Returns:

  • (Boolean)


207
208
209
# File 'app/models/shoppe/order.rb', line 207

def valid_delivery_service?
  self.delivery_service && self.available_delivery_services.include?(self.delivery_service)
end