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



338
339
340
# File 'app/models/shoppe/order.rb', line 338

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

.ransackable_attributes(auth_object = nil) ⇒ Object

Specify which attributes can be searched



333
334
335
# File 'app/models/shoppe/order.rb', line 333

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).



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

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

#accepted?Boolean

Has this order been accepted?

Returns:

  • (Boolean)


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

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)



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

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.



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

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)


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

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.



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

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)


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

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

#courier_tracking_urlObject

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



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

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



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

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



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

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



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

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



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

def delivery_service
  super || available_delivery_services.first
end

#delivery_service_priceObject

Return the delivery price for this order in its current state



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

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.



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

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



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

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

#delivery_tax_rateObject

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



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

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

#empty?Boolean

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

Returns:

  • (Boolean)


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

def empty?
  order_items.empty?
end

#has_items?Boolean

Does this order have items?

Returns:

  • (Boolean)


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

def has_items?
  total_items > 0
end

#numberObject

The order number



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

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

#paid?Boolean

Has this order been fully paid for?

Returns:

  • (Boolean)


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

def paid?
  !paid_at.blank?
end

#pay!(reference, method) ⇒ Object

This method will mark an order as paid.



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

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.



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

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

#profitObject

Return the price for the order



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

def profit
  total_before_tax - total_cost
end

#received?Boolean

Has the order been received?

Returns:

  • (Boolean)


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

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).



309
310
311
312
313
314
315
316
317
# File 'app/models/shoppe/order.rb', line 309

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

#rejected?Boolean

Has this order been rejected?

Returns:

  • (Boolean)


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

def rejected?
  !!self.rejected_at
end

#remove_delivery_service_if_invalidObject

Remove the associated delivery service if it’s invalid



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

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.



321
322
323
324
325
326
327
328
329
330
# File 'app/models/shoppe/order.rb', line 321

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)


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

def shipped?
  !!self.shipped_at?
end

#taxObject

The total amount of tax due on this order



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

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



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

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



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

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



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

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



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

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

#total_itemsObject

Return the number of items in the order?



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

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

#total_weightObject

The total weight of the order



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

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)


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

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