Class: Shoppe::Order

Inherits:
ActiveRecord::Base
  • Object
show all
Extended by:
ActiveModel::Callbacks
Defined in:
app/models/shoppe/order.rb,
app/models/shoppe/order/states.rb,
app/models/shoppe/order/actions.rb,
app/models/shoppe/order/billing.rb,
app/models/shoppe/order/delivery.rb

Constant Summary collapse

STATUSES =

An array of all the available statuses for an order

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#billing_address_idObject

Some methods for setting the billing & delivery addresses


37
38
39
# File 'app/models/shoppe/order.rb', line 37

def billing_address_id
  @billing_address_id
end

#delivery_address_idObject

Some methods for setting the billing & delivery addresses


37
38
39
# File 'app/models/shoppe/order.rb', line 37

def delivery_address_id
  @delivery_address_id
end

#save_addressesObject

Some methods for setting the billing & delivery addresses


37
38
39
# File 'app/models/shoppe/order.rb', line 37

def save_addresses
  @save_addresses
end

Class Method Details

.ransackable_associations(auth_object = nil) ⇒ Object


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

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

.ransackable_attributes(auth_object = nil) ⇒ Object


91
92
93
# File 'app/models/shoppe/order.rb', line 91

def self.ransackable_attributes(auth_object = nil)
  ["id", "billing_postcode", "billing_address1", "billing_address2", "billing_address3", "billing_address4", "first_name", "last_name", "company", "email_address", "phone_number", "consignment_number", "status", "received_at"] + _ransackers.keys
end

Instance Method Details

#accept!(user = nil) ⇒ Object

Mark order as accepted

Parameters:

  • user (Shoppe::User) (defaults to: nil)

    the user who carried out this action


56
57
58
59
60
61
62
63
64
65
# File 'app/models/shoppe/order/actions.rb', line 56

def accept!(user = nil)
  run_callbacks :acceptance do
    self.accepted_at = Time.now
    self.accepter = user if user
    self.status = 'accepted'
    self.save!
    self.order_items.each(&:accept!)
    deliver_accepted_order_email
  end
end

#accepted?Boolean

Has this order been accepted?

Returns:

  • (Boolean)

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

def accepted?
  !!self.accepted_at
end

#accepterShoppe::User

The Shoppe::User who accepted the order

Returns:


10
# File 'app/models/shoppe/order/states.rb', line 10

belongs_to :accepter, :class_name => 'Shoppe::User', :foreign_key => 'accepted_by'

#available_delivery_servicesArray

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

Returns:

  • (Array)

    an array of Shoppe::DeliveryService objects


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

def available_delivery_services
  delivery_service_prices.map(&:delivery_service).uniq
end

#balanceBigDecimal

The total amount due on the order

Returns:

  • (BigDecimal)

79
80
81
# File 'app/models/shoppe/order/billing.rb', line 79

def balance
  @balance ||= total - amount_paid
end

#billing_countryShoppe::Country

The country which this order should be billed to

Returns:


7
# File 'app/models/shoppe/order/billing.rb', line 7

belongs_to :billing_country, :class_name => 'Shoppe::Country', :foreign_key => 'billing_country_id'

#billing_nameString

The name for billing purposes

Returns:

  • (String)

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

def billing_name
  company.blank? ? full_name : "#{full_name} (#{company})"
end

#build_timeFloat

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

Returns:

  • (Float)
    • the length of time


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

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)

35
36
37
# File 'app/models/shoppe/order/states.rb', line 35

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

#cache_delivery_pricingObject

Cache delivery prices for the order


85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'app/models/shoppe/order/delivery.rb', line 85

def cache_delivery_pricing
  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_rate, self.delivery_tax_rate
  else
    write_attribute :delivery_service_id, nil
    write_attribute :delivery_price, nil
    write_attribute :delivery_cost_price, nil
    write_attribute :delivery_tax_rate, nil
    write_attribute :delivery_tax_amount, nil
  end
end

#cache_delivery_pricing!Object

Cache prices and save the order


101
102
103
104
# File 'app/models/shoppe/order/delivery.rb', line 101

def cache_delivery_pricing!
  cache_delivery_pricing
  save!
end

#confirm!Boolean

This method should be executed by the application when the order should be completed by the customer. It will raise exceptions if anything goes wrong or return true if the order has been confirmed successfully

Returns:

  • (Boolean)

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'app/models/shoppe/order/actions.rb', line 30

def confirm!
  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

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

  # We're all good.
  true
end

#confirming?Boolean

Is this order in the user confirmation step?

Returns:

  • (Boolean)

42
43
44
# File 'app/models/shoppe/order/states.rb', line 42

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

#courier_tracking_urlString

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

Returns:

  • (String)

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

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

#customer_nameString

The name of the customer in the format of “Company (First Last)” or if they don't have company specified, just “First Last”.

Returns:

  • (String)

59
60
61
# File 'app/models/shoppe/order.rb', line 59

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

#deliver_accepted_order_emailObject


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

def deliver_accepted_order_email
  Shoppe::OrderMailer.accepted(self).deliver_now
end

#deliver_received_order_emailObject


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

def deliver_received_order_email
  Shoppe::OrderMailer.received(self).deliver_now
end

#deliver_rejected_order_emailObject


85
86
87
# File 'app/models/shoppe/order/actions.rb', line 85

def deliver_rejected_order_email
  Shoppe::OrderMailer.rejected(self).deliver_now
end

#delivery_cost_priceBigDecimal

The cost of delivering this order in its current state

Returns:

  • (BigDecimal)

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

def delivery_cost_price
  read_attribute(:delivery_cost_price) || delivery_service_price.try(:cost_price) || BigDecimal(0)
end

#delivery_countryShoppe::Country

The country where this order is being delivered to (if one has been provided)

Returns:


12
# File 'app/models/shoppe/order/delivery.rb', line 12

belongs_to :delivery_country, :class_name => 'Shoppe::Country', :foreign_key => 'delivery_country_id'

#delivery_priceBigDecimal

The price for delivering this order in its current state

Returns:

  • (BigDecimal)

165
166
167
# File 'app/models/shoppe/order/delivery.rb', line 165

def delivery_price
  read_attribute(:delivery_price) || delivery_service_price.try(:price) || BigDecimal(0)
end

#delivery_required?Boolean

Is delivery required for this order?

Returns:

  • (Boolean)

123
124
125
# File 'app/models/shoppe/order/delivery.rb', line 123

def delivery_required?
  total_weight > BigDecimal(0)
end

#delivery_serviceShoppe::DeliveryService

The recommended delivery service for this order


7
# File 'app/models/shoppe/order/delivery.rb', line 7

belongs_to :delivery_service, :class_name => 'Shoppe::DeliveryService'

#delivery_service_priceBigDecimal

Return the delivery price for this order in its current state

Returns:

  • (BigDecimal)

158
159
160
# File 'app/models/shoppe/order/delivery.rb', line 158

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

#delivery_service_pricesArray

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

Returns:

  • (Array)

    an array of Shoppe:DeliveryServicePrice objects


138
139
140
141
142
143
144
145
146
# File 'app/models/shoppe/order/delivery.rb', line 138

def delivery_service_prices
  if delivery_required?
    prices = Shoppe::DeliveryServicePrice.joins(:delivery_service).where(:shoppe_delivery_services => {:active => true}).order(:price).for_weight(total_weight)
    prices = prices.select { |p| p.countries.empty? || p.country?(self.delivery_country) }
    prices.sort{ |x,y| (y.delivery_service.default? ? 1 : 0) <=> (x.delivery_service.default? ? 1 : 0) } # Order by truthiness
  else
    []
  end
end

#delivery_tax_amountBigDecimal

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

Returns:

  • (BigDecimal)

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

def delivery_tax_amount
  read_attribute(:delivery_tax_amount) ||
  delivery_price / BigDecimal(100) * delivery_tax_rate
end

#delivery_tax_rateBigDecimal

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

Returns:

  • (BigDecimal)

187
188
189
190
191
# File 'app/models/shoppe/order/delivery.rb', line 187

def delivery_tax_rate
  read_attribute(:delivery_tax_rate) ||
  delivery_service_price.try(:tax_rate).try(:rate_for, self) ||
  BigDecimal(0)
end

#empty?Boolean

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

Returns:

  • (Boolean)

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

def empty?
  order_items.empty?
end

#full_nameString

The full name of the customer created by concatinting the first & last name

Returns:

  • (String)

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

def full_name
  "#{first_name} #{last_name}"
end

#has_items?Boolean

Does this order have items?

Returns:

  • (Boolean)

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

def has_items?
  total_items > 0
end

#invoiced?Boolean

Is the order invoiced?

Returns:

  • (Boolean)

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

def invoiced?
  !invoice_number.blank?
end

#items_sub_totalBigDecimal

The total price of all items in the basket excluding delivery

Returns:

  • (BigDecimal)

48
49
50
# File 'app/models/shoppe/order/billing.rb', line 48

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

#numberString

The order number

Returns:

  • (String)
    • the order number padded with at least 5 zeros


42
43
44
# File 'app/models/shoppe/order.rb', line 42

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

Has this order been paid in full?

Returns:

  • (Boolean)

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

def paid_in_full?
  !payment_outstanding?
end

#payment_outstanding?Boolean

Is there a payment still outstanding on this order?

Returns:

  • (Boolean)

86
87
88
# File 'app/models/shoppe/order/billing.rb', line 86

def payment_outstanding?
  balance > BigDecimal(0)
end

#proceed_to_confirm(params = {}) ⇒ Boolean

This method should be called by the base application when the user has completed their first round of entering details. This will mark the order as “confirming” which means the customer now just must confirm.

Parameters:

  • params (Hash) (defaults to: {})

    a hash of order attributes

Returns:

  • (Boolean)

16
17
18
19
20
21
22
23
# File 'app/models/shoppe/order/actions.rb', line 16

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

#profitBigDecimal

Return the price for the order

Returns:

  • (BigDecimal)

41
42
43
# File 'app/models/shoppe/order/billing.rb', line 41

def profit
  total_before_tax - total_cost
end

#received?Boolean

Has the order been received?

Returns:

  • (Boolean)

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

def received?
  !!self.received_at?
end

#reject!(user = nil) ⇒ Object

Mark order as rejected

Parameters:

  • user (Shoppe::User) (defaults to: nil)

    the user who carried out the action


70
71
72
73
74
75
76
77
78
79
# File 'app/models/shoppe/order/actions.rb', line 70

def reject!(user = nil)
  run_callbacks :rejection do
    self.rejected_at = Time.now
    self.rejecter = user if user
    self.status = 'rejected'
    self.save!
    self.order_items.each(&:reject!)
    deliver_rejected_order_email
  end
end

#rejected?Boolean

Has this order been rejected?

Returns:

  • (Boolean)

49
50
51
# File 'app/models/shoppe/order/states.rb', line 49

def rejected?
  !!self.rejected_at
end

#rejecterShoppe::User

The Shoppe::User who rejected the order

Returns:


15
# File 'app/models/shoppe/order/states.rb', line 15

belongs_to :rejecter, :class_name => 'Shoppe::User', :foreign_key => 'rejected_by'

#remove_delivery_service_if_invalidObject

Remove the associated delivery service if it's invalid


201
202
203
204
205
206
# File 'app/models/shoppe/order/delivery.rb', line 201

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

#ship!(consignment_number, user = nil) ⇒ Object

Mark this order as shipped


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

def ship!(consignment_number, user = nil)
  run_callbacks :ship do
    self.shipped_at = Time.now
    self.shipper = user if user
    self.status = 'shipped'
    self.consignment_number = consignment_number
    self.save!
    Shoppe::OrderMailer.shipped(self).deliver_now
  end
end

#shipped?Boolean

Has this order been shipped?

Returns:

  • (Boolean)

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

def shipped?
  !!self.shipped_at?
end

#shipperShoppe::User

The user who marked the order has shipped

Returns:


17
# File 'app/models/shoppe/order/delivery.rb', line 17

belongs_to :shipper, :class_name => 'Shoppe::User', :foreign_key => 'shipped_by'

#taxBigDecimal

The total amount of tax due on this order

Returns:

  • (BigDecimal)

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

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

#totalBigDecimal

The total of the order including tax

Returns:

  • (BigDecimal)

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

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

#total_before_taxBigDecimal

The total price of the order before tax

Returns:

  • (BigDecimal)

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

def total_before_tax
  self.delivery_price + self.items_sub_total
end

#total_costBigDecimal

The total cost of the order

Returns:

  • (BigDecimal)

33
34
35
36
# File 'app/models/shoppe/order/billing.rb', line 33

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

#total_itemsInteger

Return the number of items in the order?

Returns:

  • (Integer)

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

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

#total_weightBigDecimal

The total weight of the order

Returns:

  • (BigDecimal)

116
117
118
# File 'app/models/shoppe/order/delivery.rb', line 116

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

#valid_delivery_service?Boolean

Is the currently assigned delivery service appropriate for this order?

Returns:

  • (Boolean)

196
197
198
# File 'app/models/shoppe/order/delivery.rb', line 196

def valid_delivery_service?
  self.delivery_service ? self.available_delivery_services.include?(self.delivery_service) : !self.delivery_required?
end