Class: Shoppe::Order

Inherits:
ActiveRecord::Base
  • Object
show all
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']

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.ransackable_associations(auth_object = nil) ⇒ Object



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

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

.ransackable_attributes(auth_object = nil) ⇒ Object



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

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) ⇒ Object

Mark order as accepted

Parameters:

  • user (Shoppe::User)

    the user who carried out this action



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

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)


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



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

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

#balanceBigDecimal

The total amount due on the order

Returns:

  • (BigDecimal)


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

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



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

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

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


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

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
    Shoppe::OrderMailer.received(self).deliver
  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)


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

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)


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

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

#delivery_cost_priceBigDecimal

The cost of delivering this order in its current state

Returns:

  • (BigDecimal)


134
135
136
# File 'app/models/shoppe/order/delivery.rb', line 134

def delivery_cost_price
  @delivery_cost_price ||= read_attribute(:delivery_cost_price) || delivery_service_price.try(:cost_price) || 0.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)


127
128
129
# File 'app/models/shoppe/order/delivery.rb', line 127

def delivery_price
  @delivery_price ||= read_attribute(:delivery_price) || delivery_service_price.try(:price) || 0.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)


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

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_pricesArray

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

Returns:

  • (Array)

    an array of Shoppe:DeliveryServicePrice objects



102
103
104
105
106
107
108
# File 'app/models/shoppe/order/delivery.rb', line 102

def delivery_service_prices
  @delivery_service_prices ||= begin
    prices = Shoppe::DeliveryServicePrice.joins(:delivery_service).where(:shoppe_delivery_services => {:active => true}).order("`default` desc, price asc").for_weight(total_weight)
    prices = prices.select { |p| p.countries.empty? || p.country?(self.delivery_country) }
    prices
  end
end

#delivery_tax_amountBigDecimal

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

Returns:

  • (BigDecimal)


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

def delivery_tax_amount
  @delivery_tax_amount ||= begin
    read_attribute(:delivery_tax_amount) ||
    delivery_price / BigDecimal(100) * delivery_tax_rate ||
    0.0
  end
end

#delivery_tax_rateBigDecimal

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

Returns:

  • (BigDecimal)


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

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

#empty?Boolean

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

Returns:

  • (Boolean)


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

def empty?
  order_items.empty?
end

#full_nameString

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

Returns:

  • (String)


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

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

#has_items?Boolean

Does this order have items?

Returns:

  • (Boolean)


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

def has_items?
  total_items > 0
end

#invoiced?Boolean

Is the order invoiced?

Returns:

  • (Boolean)


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

def invoiced?
  !invoice_number.blank?
end

#numberString

The order number

Returns:

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



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

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

Has this order been paid in full?

Returns:

  • (Boolean)


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

def paid_in_full?
  !payment_outstanding?
end

#payment_outstanding?Boolean

Is there a payment still outstanding on this order?

Returns:

  • (Boolean)


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

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)


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

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) ⇒ Object

Mark order as rejected

Parameters:

  • user (Shoppe::User)

    the user who carried out the action



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

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)


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



168
169
170
171
172
173
# File 'app/models/shoppe/order/delivery.rb', line 168

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

#ship!(user, consignment_number) ⇒ Object

Mark this order as shipped



184
185
186
187
188
189
190
191
192
193
# File 'app/models/shoppe/order/delivery.rb', line 184

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)


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

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)


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

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)


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

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)


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

def total_before_tax
  self.delivery_price +
  order_items.inject(BigDecimal(0)) { |t, i| t + i.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)


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

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

#total_weightBigDecimal

The total weight of the order

Returns:

  • (BigDecimal)


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

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)


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

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