shoppe 0.0.14 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/app/assets/javascripts/shoppe/application.coffee +57 -1
  2. data/app/assets/javascripts/shoppe/mousetrap.js +9 -0
  3. data/app/assets/stylesheets/shoppe/application.scss +70 -59
  4. data/app/assets/stylesheets/shoppe/dialog.scss +10 -0
  5. data/app/assets/stylesheets/shoppe/sub.scss +15 -0
  6. data/app/controllers/shoppe/application_controller.rb +13 -1
  7. data/app/controllers/shoppe/attachments_controller.rb +10 -8
  8. data/app/controllers/shoppe/dashboard_controller.rb +6 -4
  9. data/app/controllers/shoppe/delivery_service_prices_controller.rb +33 -31
  10. data/app/controllers/shoppe/delivery_services_controller.rb +34 -32
  11. data/app/controllers/shoppe/orders_controller.rb +40 -38
  12. data/app/controllers/shoppe/product_categories_controller.rb +34 -32
  13. data/app/controllers/shoppe/products_controller.rb +32 -44
  14. data/app/controllers/shoppe/sessions_controller.rb +24 -22
  15. data/app/controllers/shoppe/stock_level_adjustments_controller.rb +40 -0
  16. data/app/controllers/shoppe/tax_rates_controller.rb +35 -33
  17. data/app/controllers/shoppe/users_controller.rb +40 -33
  18. data/app/controllers/shoppe/variants_controller.rb +50 -0
  19. data/app/helpers/shoppe/shoppe_helper.rb +2 -2
  20. data/app/mailers/shoppe/order_mailer.rb +20 -18
  21. data/app/models/shoppe/country.rb +1 -1
  22. data/app/models/shoppe/delivery_service.rb +17 -15
  23. data/app/models/shoppe/delivery_service_price.rb +18 -16
  24. data/app/models/shoppe/order.rb +293 -290
  25. data/app/models/shoppe/order_item.rb +115 -113
  26. data/app/models/shoppe/product.rb +76 -54
  27. data/app/models/shoppe/product/product_attributes.rb +12 -10
  28. data/app/models/shoppe/product/variants.rb +26 -0
  29. data/app/models/shoppe/product_attribute.rb +40 -38
  30. data/app/models/shoppe/product_category.rb +16 -14
  31. data/app/models/shoppe/stock_level_adjustment.rb +1 -2
  32. data/app/models/shoppe/tax_rate.rb +2 -2
  33. data/app/models/shoppe/user.rb +34 -32
  34. data/app/views/shoppe/orders/index.html.haml +3 -4
  35. data/app/views/shoppe/orders/show.html.haml +5 -5
  36. data/app/views/shoppe/products/_form.html.haml +28 -27
  37. data/app/views/shoppe/products/_table.html.haml +42 -0
  38. data/app/views/shoppe/products/edit.html.haml +2 -1
  39. data/app/views/shoppe/products/index.html.haml +1 -24
  40. data/app/views/shoppe/shared/error.html.haml +4 -0
  41. data/app/views/shoppe/{products/stock_levels.html.haml → stock_level_adjustments/index.html.haml} +12 -6
  42. data/app/views/shoppe/variants/form.html.haml +64 -0
  43. data/app/views/shoppe/variants/index.html.haml +33 -0
  44. data/config/routes.rb +2 -1
  45. data/config/shoppe.example.yml +16 -2
  46. data/db/migrate/20131022090919_refactor_order_items_to_allow_any_product.rb +6 -0
  47. data/db/migrate/20131022092904_rename_product_title_to_name.rb +5 -0
  48. data/db/migrate/20131022093538_stock_level_adjustments_should_be_polymorphic.rb +6 -0
  49. data/db/migrate/20131022135331_add_parent_id_to_products.rb +5 -0
  50. data/db/migrate/20131022145653_cost_price_should_be_default_to_zero.rb +9 -0
  51. data/db/seeds.rb +20 -20
  52. data/lib/shoppe.rb +2 -0
  53. data/lib/shoppe/errors/not_enough_stock.rb +1 -1
  54. data/lib/shoppe/errors/unorderable_item.rb +11 -0
  55. data/lib/shoppe/orderable_item.rb +39 -0
  56. data/lib/shoppe/version.rb +1 -1
  57. data/test/dummy/db/schema.rb +14 -11
  58. data/test/dummy/log/development.log +75 -0
  59. metadata +37 -5
@@ -0,0 +1,50 @@
1
+ module Shoppe
2
+ class VariantsController < ApplicationController
3
+
4
+ before_filter { @active_nav = :products }
5
+ before_filter { @product = Shoppe::Product.find(params[:product_id]) }
6
+ before_filter { params[:id] && @variant = @product.variants.find(params[:id]) }
7
+
8
+ def index
9
+ @variants = @product.variants.ordered
10
+ end
11
+
12
+ def new
13
+ @variant = @product.variants.build
14
+ render :action => "form"
15
+ end
16
+
17
+ def create
18
+ @variant = @product.variants.build(safe_params)
19
+ if @variant.save
20
+ redirect_to [@product, :variants], :notice => "Varient has been added successfully"
21
+ else
22
+ render :action => "form"
23
+ end
24
+ end
25
+
26
+ def edit
27
+ render :action => "form"
28
+ end
29
+
30
+ def update
31
+ if @variant.update(safe_params)
32
+ redirect_to edit_product_variant_path(@product, @variant), :notice => "Varient has been updated successfully"
33
+ else
34
+ render :action => "form"
35
+ end
36
+ end
37
+
38
+ def destroy
39
+ @variant.destroy
40
+ redirect_to [@product, :variants], :notice => "Varient has been removed successfully"
41
+ end
42
+
43
+ private
44
+
45
+ def safe_params
46
+ params[:product].permit(:name, :permalink, :sku, :default_image_file, :price, :cost_price, :tax_rate_id, :weight, :stock_control, :active)
47
+ end
48
+
49
+ end
50
+ end
@@ -4,7 +4,7 @@ module Shoppe::ShoppeHelper
4
4
  content_tag :span, status, :class => "status-tag #{status}"
5
5
  end
6
6
 
7
- def attachment_preview(attachment)
7
+ def attachment_preview(attachment, options = {})
8
8
  if attachment
9
9
  String.new.tap do |s|
10
10
  if attachment.image?
@@ -22,7 +22,7 @@ module Shoppe::ShoppeHelper
22
22
  s << "</div>"
23
23
  s << "</div>"
24
24
  end.html_safe
25
- else
25
+ elsif !options[:hide_if_blank]
26
26
  "<div class='attachmentPreview'><div class='imgContainer'><div class='img none'></div></div><div class='desc none'>No attachment</div></div>".html_safe
27
27
  end
28
28
  end
@@ -1,25 +1,27 @@
1
- class Shoppe::OrderMailer < ActionMailer::Base
1
+ module Shoppe
2
+ class OrderMailer < ActionMailer::Base
2
3
 
3
- default :from => "#{Shoppe.config[:store_name]} <#{Shoppe.config[:email_address]}>"
4
+ default :from => "#{Shoppe.config[:store_name]} <#{Shoppe.config[:email_address]}>"
4
5
 
5
- def received(order)
6
- @order = order
7
- mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.subject', :default => "Order Confirmation")
8
- end
6
+ def received(order)
7
+ @order = order
8
+ mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.subject', :default => "Order Confirmation")
9
+ end
9
10
 
10
- def accepted(order)
11
- @order = order
12
- mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.accepted', :default => "Order Accepted")
13
- end
11
+ def accepted(order)
12
+ @order = order
13
+ mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.accepted', :default => "Order Accepted")
14
+ end
14
15
 
15
- def rejected(order)
16
- @order = order
17
- mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.rejected', :default => "Order Rejected")
18
- end
16
+ def rejected(order)
17
+ @order = order
18
+ mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.rejected', :default => "Order Rejected")
19
+ end
19
20
 
20
- def shipped(order)
21
- @order = order
22
- mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.shipped', :default => "Order Shipped")
23
- end
21
+ def shipped(order)
22
+ @order = order
23
+ mail :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.shipped', :default => "Order Shipped")
24
+ end
24
25
 
26
+ end
25
27
  end
@@ -5,7 +5,7 @@ module Shoppe
5
5
  self.table_name = 'shoppe_countries'
6
6
 
7
7
  # Relationships
8
- has_many :orders, :dependent => :restrict_with_exception
8
+ has_many :orders, :dependent => :restrict_with_exception, :class_name => 'Shoppe::Order'
9
9
 
10
10
  # Scopes
11
11
  scope :ordered, -> { order('shoppe_countries.name asc') }
@@ -1,22 +1,24 @@
1
- class Shoppe::DeliveryService < ActiveRecord::Base
1
+ module Shoppe
2
+ class DeliveryService < ActiveRecord::Base
2
3
 
3
- # Set the table name
4
- self.table_name = 'shoppe_delivery_services'
4
+ # Set the table name
5
+ self.table_name = 'shoppe_delivery_services'
5
6
 
6
- # Validations
7
- validates :name, :presence => true
8
- validates :courier, :presence => true
7
+ # Validations
8
+ validates :name, :presence => true
9
+ validates :courier, :presence => true
9
10
 
10
- # Relationships
11
- has_many :orders, :dependent => :restrict_with_exception, :class_name => 'Shoppe::Order'
12
- has_many :delivery_service_prices, :dependent => :destroy, :class_name => 'Shoppe::DeliveryServicePrice'
11
+ # Relationships
12
+ has_many :orders, :dependent => :restrict_with_exception, :class_name => 'Shoppe::Order'
13
+ has_many :delivery_service_prices, :dependent => :destroy, :class_name => 'Shoppe::DeliveryServicePrice'
13
14
 
14
- # Scopes
15
- scope :active, -> { where(:active => true)}
15
+ # Scopes
16
+ scope :active, -> { where(:active => true)}
16
17
 
17
- # Return the tracking URL for the given consignment number
18
- def tracking_url_for(consignment_number)
19
- tracking_url.gsub("{{consignment_number}}", consignment_number)
18
+ # Return the tracking URL for the given consignment number
19
+ def tracking_url_for(consignment_number)
20
+ tracking_url.gsub("{{consignment_number}}", consignment_number)
21
+ end
22
+
20
23
  end
21
-
22
24
  end
@@ -1,23 +1,25 @@
1
- class Shoppe::DeliveryServicePrice < ActiveRecord::Base
1
+ module Shoppe
2
+ class DeliveryServicePrice < ActiveRecord::Base
2
3
 
3
- # Set the table name
4
- self.table_name = 'shoppe_delivery_service_prices'
4
+ # Set the table name
5
+ self.table_name = 'shoppe_delivery_service_prices'
5
6
 
6
- # Tax rates are associated with countries
7
- include Shoppe::AssociatedCountries
7
+ # Tax rates are associated with countries
8
+ include Shoppe::AssociatedCountries
8
9
 
9
- # Relationships
10
- belongs_to :delivery_service, :class_name => 'Shoppe::DeliveryService'
11
- belongs_to :tax_rate, :class_name => "Shoppe::TaxRate"
10
+ # Relationships
11
+ belongs_to :delivery_service, :class_name => 'Shoppe::DeliveryService'
12
+ belongs_to :tax_rate, :class_name => "Shoppe::TaxRate"
12
13
 
13
- # Validations
14
- validates :price, :numericality => true
15
- validates :cost_price, :numericality => true, :allow_blank => true
16
- validates :min_weight, :numericality => true
17
- validates :max_weight, :numericality => true
14
+ # Validations
15
+ validates :price, :numericality => true
16
+ validates :cost_price, :numericality => true, :allow_blank => true
17
+ validates :min_weight, :numericality => true
18
+ validates :max_weight, :numericality => true
18
19
 
19
- # Scopes
20
- scope :ordered, -> { order('price asc')}
21
- scope :for_weight, -> weight { where("min_weight <= ? AND max_weight >= ?", weight, weight) }
20
+ # Scopes
21
+ scope :ordered, -> { order('price asc')}
22
+ scope :for_weight, -> weight { where("min_weight <= ? AND max_weight >= ?", weight, weight) }
22
23
 
24
+ end
23
25
  end
@@ -1,352 +1,355 @@
1
- class Shoppe::Order < ActiveRecord::Base
1
+ module Shoppe
2
+ class Order < ActiveRecord::Base
2
3
 
3
- # Set the table name
4
- self.table_name = 'shoppe_orders'
5
-
6
- # An array of all the available statuses for an order
7
- STATUSES = ['building', 'confirming', 'received', 'accepted', 'rejected', 'shipped']
8
-
9
- # Order's implement a key value store for storing arbitary properties which
10
- # may be useful (for example payment configuraiton)
11
- key_value_store :properties
12
-
13
- # These additional callbacks allow for applications to hook into other
14
- # parts of the order lifecycle.
15
- define_model_callbacks :confirmation, :payment, :acceptance, :rejection, :ship
16
-
17
- # Relationships
18
- belongs_to :delivery_service, :class_name => 'Shoppe::DeliveryService'
19
- belongs_to :country, :class_name => 'Shoppe::Country'
20
- belongs_to :accepter, :class_name => 'Shoppe::User', :foreign_key => 'accepted_by'
21
- belongs_to :rejecter, :class_name => 'Shoppe::User', :foreign_key => 'rejected_by'
22
- belongs_to :shipper, :class_name => 'Shoppe::User', :foreign_key => 'shipped_by'
23
- has_many :order_items, :dependent => :destroy, :class_name => 'Shoppe::OrderItem'
24
- has_many :products, :through => :order_items, :class_name => 'Shoppe::Product'
25
-
26
- # Validations
27
- validates :token, :presence => true
28
- validates :status, :inclusion => {:in => STATUSES}
29
- with_options :if => Proc.new { |o| !o.building? } do |order|
30
- order.validates :first_name, :presence => true
31
- order.validates :last_name, :presence => true
32
- order.validates :address1, :presence => true
33
- order.validates :address2, :presence => true
34
- order.validates :postcode, :presence => true
35
- order.validates :country, :presence => true
36
- order.validates :email_address, :format => {:with => /\A\b[A-Z0-9\.\_\%\-\+]+@(?:[A-Z0-9\-]+\.)+[A-Z]{2,6}\b\z/i}
37
- order.validates :phone_number, :format => {:with => /\A[\d\ ]{7,}\z/}
38
- end
39
- validate do
40
- unless available_delivery_services.include?(self.delivery_service)
41
- errors.add :delivery_service_id, "is not suitable for this order"
4
+ # Set the table name
5
+ self.table_name = 'shoppe_orders'
6
+
7
+ # An array of all the available statuses for an order
8
+ STATUSES = ['building', 'confirming', 'received', 'accepted', 'rejected', 'shipped']
9
+
10
+ # Order's implement a key value store for storing arbitary properties which
11
+ # may be useful (for example payment configuraiton)
12
+ key_value_store :properties
13
+
14
+ # These additional callbacks allow for applications to hook into other
15
+ # parts of the order lifecycle.
16
+ define_model_callbacks :confirmation, :payment, :acceptance, :rejection, :ship
17
+
18
+ # Relationships
19
+ belongs_to :delivery_service, :class_name => 'Shoppe::DeliveryService'
20
+ belongs_to :country, :class_name => 'Shoppe::Country'
21
+ belongs_to :accepter, :class_name => 'Shoppe::User', :foreign_key => 'accepted_by'
22
+ belongs_to :rejecter, :class_name => 'Shoppe::User', :foreign_key => 'rejected_by'
23
+ belongs_to :shipper, :class_name => 'Shoppe::User', :foreign_key => 'shipped_by'
24
+ has_many :order_items, :dependent => :destroy, :class_name => 'Shoppe::OrderItem'
25
+ has_many :products, :through => :order_items, :class_name => 'Shoppe::Product'
26
+
27
+ # Validations
28
+ validates :token, :presence => true
29
+ validates :status, :inclusion => {:in => STATUSES}
30
+ with_options :if => Proc.new { |o| !o.building? } do |order|
31
+ order.validates :first_name, :presence => true
32
+ order.validates :last_name, :presence => true
33
+ order.validates :address1, :presence => true
34
+ order.validates :address3, :presence => true
35
+ order.validates :address4, :presence => true
36
+ order.validates :postcode, :presence => true
37
+ order.validates :country, :presence => true
38
+ order.validates :email_address, :format => {:with => /\A\b[A-Z0-9\.\_\%\-\+]+@(?:[A-Z0-9\-]+\.)+[A-Z]{2,6}\b\z/i}
39
+ order.validates :phone_number, :format => {:with => /\A[\d\ \-x\(\)]{7,}\z/}
40
+ end
41
+ validate do
42
+ unless available_delivery_services.include?(self.delivery_service)
43
+ errors.add :delivery_service_id, "is not suitable for this order"
44
+ end
42
45
  end
43
- end
44
46
 
45
- # Scopes
46
- scope :received, -> {where("received_at is not null")}
47
- scope :pending, -> { where(:status => 'received') }
48
- scope :ordered, -> { order('id desc')}
47
+ # Scopes
48
+ scope :received, -> {where("received_at is not null")}
49
+ scope :pending, -> { where(:status => 'received') }
50
+ scope :ordered, -> { order('id desc')}
49
51
 
50
- # Set some defaults
51
- before_validation do
52
- self.status = 'building' if self.status.blank?
53
- self.token = SecureRandom.uuid if self.token.blank?
54
- end
52
+ # Set some defaults
53
+ before_validation do
54
+ self.status = 'building' if self.status.blank?
55
+ self.token = SecureRandom.uuid if self.token.blank?
56
+ end
55
57
 
56
- # Is this order still being built by the user?
57
- def building?
58
- self.status == 'building'
59
- end
58
+ # Is this order still being built by the user?
59
+ def building?
60
+ self.status == 'building'
61
+ end
60
62
 
61
- # Is this order in the user confirmation step?
62
- def confirming?
63
- self.status == 'confirming'
64
- end
63
+ # Is this order in the user confirmation step?
64
+ def confirming?
65
+ self.status == 'confirming'
66
+ end
65
67
 
66
- # Has this order been rejected?
67
- def rejected?
68
- !!self.rejected_at
69
- end
68
+ # Has this order been rejected?
69
+ def rejected?
70
+ !!self.rejected_at
71
+ end
70
72
 
71
- # Has this order been accepted?
72
- def accepted?
73
- !!self.accepted_at
74
- end
73
+ # Has this order been accepted?
74
+ def accepted?
75
+ !!self.accepted_at
76
+ end
75
77
 
76
- # Has this order been shipped?
77
- def shipped?
78
- !!self.shipped_at?
79
- end
78
+ # Has this order been shipped?
79
+ def shipped?
80
+ !!self.shipped_at?
81
+ end
80
82
 
81
- # Has the order been received?
82
- def received?
83
- !!self.received_at?
84
- end
83
+ # Has the order been received?
84
+ def received?
85
+ !!self.received_at?
86
+ end
85
87
 
86
- # The order number
87
- def number
88
- id.to_s.rjust(6, '0')
89
- end
88
+ # The order number
89
+ def number
90
+ id.to_s.rjust(6, '0')
91
+ end
90
92
 
91
- # The length of time the customer spent building the order before submitting it to us.
92
- # The time from first item in basket to received.
93
- def build_time
94
- return nil if self.received_at.blank?
95
- self.created_at - self.received_at
96
- end
93
+ # The length of time the customer spent building the order before submitting it to us.
94
+ # The time from first item in basket to received.
95
+ def build_time
96
+ return nil if self.received_at.blank?
97
+ self.created_at - self.received_at
98
+ end
97
99
 
98
- # The name of the customer
99
- def customer_name
100
- company.blank? ? "#{first_name} #{last_name}" : "#{company} (#{first_name} #{last_name})"
101
- end
100
+ # The name of the customer
101
+ def customer_name
102
+ company.blank? ? "#{first_name} #{last_name}" : "#{company} (#{first_name} #{last_name})"
103
+ end
102
104
 
103
- # Is this order empty? (i.e. doesn't have any items associated with it)
104
- def empty?
105
- order_items.empty?
106
- end
105
+ # Is this order empty? (i.e. doesn't have any items associated with it)
106
+ def empty?
107
+ order_items.empty?
108
+ end
107
109
 
108
- # Does this order have items?
109
- def has_items?
110
- total_items > 0
111
- end
110
+ # Does this order have items?
111
+ def has_items?
112
+ total_items > 0
113
+ end
112
114
 
113
- # Return the number of items in the order?
114
- def total_items
115
- @total_items ||= order_items.inject(0) { |t,i| t + i.quantity }
116
- end
115
+ # Return the number of items in the order?
116
+ def total_items
117
+ @total_items ||= order_items.inject(0) { |t,i| t + i.quantity }
118
+ end
117
119
 
118
- # The total cost of the order
119
- def total_cost
120
- self.delivery_cost_price +
121
- order_items.inject(BigDecimal(0)) { |t, i| t + i.total_cost }
122
- end
120
+ # The total cost of the order
121
+ def total_cost
122
+ self.delivery_cost_price +
123
+ order_items.inject(BigDecimal(0)) { |t, i| t + i.total_cost }
124
+ end
123
125
 
124
- # Return the price for the order
125
- def profit
126
- total_before_tax - total_cost
127
- end
126
+ # Return the price for the order
127
+ def profit
128
+ total_before_tax - total_cost
129
+ end
128
130
 
129
- # The total price of the order before tax
130
- def total_before_tax
131
- self.delivery_price +
132
- order_items.inject(BigDecimal(0)) { |t, i| t + i.sub_total }
133
- end
131
+ # The total price of the order before tax
132
+ def total_before_tax
133
+ self.delivery_price +
134
+ order_items.inject(BigDecimal(0)) { |t, i| t + i.sub_total }
135
+ end
134
136
 
135
- # The total amount of tax due on this order
136
- def tax
137
- self.delivery_tax_amount +
138
- order_items.inject(BigDecimal(0)) { |t, i| t + i.tax_amount }
139
- end
137
+ # The total amount of tax due on this order
138
+ def tax
139
+ self.delivery_tax_amount +
140
+ order_items.inject(BigDecimal(0)) { |t, i| t + i.tax_amount }
141
+ end
140
142
 
141
- # The total of the order including tax
142
- def total
143
- self.delivery_price +
144
- self.delivery_tax_amount +
145
- order_items.inject(BigDecimal(0)) { |t, i| t + i.total }
146
- end
143
+ # The total of the order including tax
144
+ def total
145
+ self.delivery_price +
146
+ self.delivery_tax_amount +
147
+ order_items.inject(BigDecimal(0)) { |t, i| t + i.total }
148
+ end
147
149
 
148
- # The total of the order including tax in pence
149
- def total_in_pence
150
- (total * BigDecimal(100)).to_i
151
- end
150
+ # The total of the order including tax in pence
151
+ def total_in_pence
152
+ (total * BigDecimal(100)).to_i
153
+ end
152
154
 
153
- # The total weight of the order
154
- def total_weight
155
- order_items.inject(BigDecimal(0)) { |t,i| t + i.weight}
156
- end
155
+ # The total weight of the order
156
+ def total_weight
157
+ order_items.inject(BigDecimal(0)) { |t,i| t + i.weight}
158
+ end
157
159
 
158
- # An array of all the delivery service prices which can be applied to this order.
159
- def delivery_service_prices
160
- @delivery_service_prices ||= begin
161
- prices = Shoppe::DeliveryServicePrice.joins(:delivery_service).where(:shoppe_delivery_services => {:active => true}).order("`default` desc, price asc").for_weight(total_weight)
162
- prices = prices.select { |p| p.countries.empty? || p.country?(self.country) }
163
- prices
160
+ # An array of all the delivery service prices which can be applied to this order.
161
+ def delivery_service_prices
162
+ @delivery_service_prices ||= begin
163
+ prices = Shoppe::DeliveryServicePrice.joins(:delivery_service).where(:shoppe_delivery_services => {:active => true}).order("`default` desc, price asc").for_weight(total_weight)
164
+ prices = prices.select { |p| p.countries.empty? || p.country?(self.country) }
165
+ prices
166
+ end
164
167
  end
165
- end
166
168
 
167
- # An array of all the delivery services which are suitable for this order in it's
168
- # current state (based on its current weight)
169
- def available_delivery_services
170
- @available_delivery_services ||= begin
171
- delivery_service_prices.map(&:delivery_service).uniq
169
+ # An array of all the delivery services which are suitable for this order in it's
170
+ # current state (based on its current weight)
171
+ def available_delivery_services
172
+ @available_delivery_services ||= begin
173
+ delivery_service_prices.map(&:delivery_service).uniq
174
+ end
172
175
  end
173
- end
174
176
 
175
- # The recommended delivery service for this order
176
- def delivery_service
177
- super || available_delivery_services.first
178
- end
177
+ # The recommended delivery service for this order
178
+ def delivery_service
179
+ super || available_delivery_services.first
180
+ end
179
181
 
180
- # Return the delivery price for this order in its current state
181
- def delivery_service_price
182
- @delivery_service_price ||= self.delivery_service && self.delivery_service.delivery_service_prices.for_weight(self.total_weight).first
183
- end
182
+ # Return the delivery price for this order in its current state
183
+ def delivery_service_price
184
+ @delivery_service_price ||= self.delivery_service && self.delivery_service.delivery_service_prices.for_weight(self.total_weight).first
185
+ end
184
186
 
185
- # The price for delivering this order in its current state
186
- def delivery_price
187
- @delivery_price ||= read_attribute(:delivery_price) || delivery_service_price.try(:price) || 0.0
188
- end
187
+ # The price for delivering this order in its current state
188
+ def delivery_price
189
+ @delivery_price ||= read_attribute(:delivery_price) || delivery_service_price.try(:price) || 0.0
190
+ end
189
191
 
190
- # The cost of delivering this order in its current state
191
- def delivery_cost_price
192
- @delivery_cost_price ||= read_attribute(:delivery_cost_price) || delivery_service_price.try(:cost_price) || 0.0
193
- end
192
+ # The cost of delivering this order in its current state
193
+ def delivery_cost_price
194
+ @delivery_cost_price ||= read_attribute(:delivery_cost_price) || delivery_service_price.try(:cost_price) || 0.0
195
+ end
194
196
 
195
- # The tax amount due for the delivery of this order in its current state
196
- def delivery_tax_amount
197
- @delivery_tax_amount ||= begin
198
- read_attribute(:delivery_tax_amount) ||
199
- delivery_price / BigDecimal(100) * delivery_tax_rate ||
200
- 0.0
197
+ # The tax amount due for the delivery of this order in its current state
198
+ def delivery_tax_amount
199
+ @delivery_tax_amount ||= begin
200
+ read_attribute(:delivery_tax_amount) ||
201
+ delivery_price / BigDecimal(100) * delivery_tax_rate ||
202
+ 0.0
203
+ end
201
204
  end
202
- end
203
205
 
204
- # The tax rate for the delivery of this order in its current state
205
- def delivery_tax_rate
206
- @delivery_tax_rate ||= begin
207
- read_attribute(:delivery_tax_rate) ||
208
- delivery_service_price.try(:tax_rate).try(:rate_for, self) ||
209
- 0.0
206
+ # The tax rate for the delivery of this order in its current state
207
+ def delivery_tax_rate
208
+ @delivery_tax_rate ||= begin
209
+ read_attribute(:delivery_tax_rate) ||
210
+ delivery_service_price.try(:tax_rate).try(:rate_for, self) ||
211
+ 0.0
212
+ end
210
213
  end
211
- end
212
214
 
213
- # Is the currently assigned delivery service appropriate for this order?
214
- def valid_delivery_service?
215
- self.delivery_service && self.available_delivery_services.include?(self.delivery_service)
216
- end
215
+ # Is the currently assigned delivery service appropriate for this order?
216
+ def valid_delivery_service?
217
+ self.delivery_service && self.available_delivery_services.include?(self.delivery_service)
218
+ end
217
219
 
218
- # Remove the associated delivery service if it's invalid
219
- def remove_delivery_service_if_invalid
220
- unless self.valid_delivery_service?
221
- self.delivery_service = nil
222
- self.save
220
+ # Remove the associated delivery service if it's invalid
221
+ def remove_delivery_service_if_invalid
222
+ unless self.valid_delivery_service?
223
+ self.delivery_service = nil
224
+ self.save
225
+ end
223
226
  end
224
- end
225
227
 
226
- # The URL which can be used to track the delivery of this order
227
- def courier_tracking_url
228
- return nil if self.shipped_at.blank? || self.consignment_number.blank?
229
- @courier_tracking_url ||= self.delivery_service.tracking_url_for(self.consignment_number)
230
- end
228
+ # The URL which can be used to track the delivery of this order
229
+ def courier_tracking_url
230
+ return nil if self.shipped_at.blank? || self.consignment_number.blank?
231
+ @courier_tracking_url ||= self.delivery_service.tracking_url_for(self.consignment_number)
232
+ end
231
233
 
232
- # Has this order been fully paid for?
233
- def paid?
234
- !paid_at.blank?
235
- end
234
+ # Has this order been fully paid for?
235
+ def paid?
236
+ !paid_at.blank?
237
+ end
236
238
 
237
- # This method is called by the customer when they submit their details in the first step of
238
- # the checkout process. It will update the status to 'confirmed' as well as updating their
239
- # details. Any issues with validation will cause false to be returned otherwise true. Any
240
- # more serious issues will be raised as exceptions.
241
- def proceed_to_confirm(params = {})
242
- self.status = 'confirming'
243
- if self.update(params)
244
- true
245
- else
246
- false
239
+ # This method is called by the customer when they submit their details in the first step of
240
+ # the checkout process. It will update the status to 'confirmed' as well as updating their
241
+ # details. Any issues with validation will cause false to be returned otherwise true. Any
242
+ # more serious issues will be raised as exceptions.
243
+ def proceed_to_confirm(params = {})
244
+ self.status = 'confirming'
245
+ if self.update(params)
246
+ true
247
+ else
248
+ false
249
+ end
247
250
  end
248
- end
249
251
 
250
- # This method will confirm the order If there are any issues with the order an exception
251
- # should be raised.
252
- def confirm!
252
+ # This method will confirm the order If there are any issues with the order an exception
253
+ # should be raised.
254
+ def confirm!
253
255
 
254
- # Ensure that we have the stock to fulfil this order at the current time. We may have had it when
255
- # it was placed int he basket and if we don't now, we should let the user know so they can
256
- # rethink.
257
- no_stock_of = self.order_items.select(&:validate_stock_levels)
258
- unless no_stock_of.empty?
259
- raise Shoppe::Errors::InsufficientStockToFulfil, :order => self, :out_of_stock_items => no_stock_of
260
- end
256
+ # Ensure that we have the stock to fulfil this order at the current time. We may have had it when
257
+ # it was placed int he basket and if we don't now, we should let the user know so they can
258
+ # rethink.
259
+ no_stock_of = self.order_items.select(&:validate_stock_levels)
260
+ unless no_stock_of.empty?
261
+ raise Shoppe::Errors::InsufficientStockToFulfil, :order => self, :out_of_stock_items => no_stock_of
262
+ end
261
263
 
262
- # Ensure that before we confirm the order that the delivery service which has been selected
263
- # is appropritae for the contents of the order.
264
- unless self.valid_delivery_service?
265
- raise Shoppe::Errors::InappropriateDeliveryService, :order => self
266
- end
264
+ # Ensure that before we confirm the order that the delivery service which has been selected
265
+ # is appropritae for the contents of the order.
266
+ unless self.valid_delivery_service?
267
+ raise Shoppe::Errors::InappropriateDeliveryService, :order => self
268
+ end
267
269
 
268
- # Store the delivery prices with the order
269
- if self.delivery_service
270
- write_attribute :delivery_service_id, self.delivery_service.id
271
- write_attribute :delivery_price, self.delivery_price
272
- write_attribute :delivery_cost_price, self.delivery_cost_price
273
- write_attribute :delivery_tax_amount, self.delivery_tax_amount
274
- write_attribute :delivery_tax_rate, self.delivery_tax_rate
275
- end
270
+ # Store the delivery prices with the order
271
+ if self.delivery_service
272
+ write_attribute :delivery_service_id, self.delivery_service.id
273
+ write_attribute :delivery_price, self.delivery_price
274
+ write_attribute :delivery_cost_price, self.delivery_cost_price
275
+ write_attribute :delivery_tax_amount, self.delivery_tax_amount
276
+ write_attribute :delivery_tax_rate, self.delivery_tax_rate
277
+ end
276
278
 
277
- run_callbacks :confirmation do
278
- # If we have successfully charged the card (i.e. no exception) we can go ahead and mark this
279
- # order as 'received' which means it can be accepted by staff.
280
- self.status = 'received'
281
- self.received_at = Time.now
282
- self.save!
279
+ run_callbacks :confirmation do
280
+ # If we have successfully charged the card (i.e. no exception) we can go ahead and mark this
281
+ # order as 'received' which means it can be accepted by staff.
282
+ self.status = 'received'
283
+ self.received_at = Time.now
284
+ self.save!
283
285
 
284
- self.order_items.each(&:confirm!)
286
+ self.order_items.each(&:confirm!)
285
287
 
286
- # Send an email to the customer
287
- Shoppe::OrderMailer.received(self).deliver
288
- end
288
+ # Send an email to the customer
289
+ Shoppe::OrderMailer.received(self).deliver
290
+ end
289
291
 
290
- # We're all good.
291
- true
292
- end
292
+ # We're all good.
293
+ true
294
+ end
293
295
 
294
- # This method will mark an order as paid.
295
- def pay!(reference, method)
296
- run_callbacks :payment do
297
- self.paid_at = Time.now.utc
298
- self.payment_reference = reference
299
- self.payment_method = method
300
- self.save!
296
+ # This method will mark an order as paid.
297
+ def pay!(reference, method)
298
+ run_callbacks :payment do
299
+ self.paid_at = Time.now.utc
300
+ self.payment_reference = reference
301
+ self.payment_method = method
302
+ self.save!
303
+ end
301
304
  end
302
- end
303
305
 
304
- # This method will accept the this order. It is called by a user (which is the only
305
- # parameter).
306
- def accept!(user)
307
- run_callbacks :acceptance do
308
- self.accepted_at = Time.now
309
- self.accepted_by = user.id
310
- self.status = 'accepted'
311
- self.save!
312
- self.order_items.each(&:accept!)
313
- Shoppe::OrderMailer.accepted(self).deliver
306
+ # This method will accept the this order. It is called by a user (which is the only
307
+ # parameter).
308
+ def accept!(user)
309
+ run_callbacks :acceptance do
310
+ self.accepted_at = Time.now
311
+ self.accepted_by = user.id
312
+ self.status = 'accepted'
313
+ self.save!
314
+ self.order_items.each(&:accept!)
315
+ Shoppe::OrderMailer.accepted(self).deliver
316
+ end
314
317
  end
315
- end
316
318
 
317
- # This method will reject the order. It is called by a user (which is the only parameter).
318
- def reject!(user)
319
- run_callbacks :rejection do
320
- self.rejected_at = Time.now
321
- self.rejected_by = user.id
322
- self.status = 'rejected'
323
- self.save!
324
- self.order_items.each(&:reject!)
325
- Shoppe::OrderMailer.rejected(self).deliver
319
+ # This method will reject the order. It is called by a user (which is the only parameter).
320
+ def reject!(user)
321
+ run_callbacks :rejection do
322
+ self.rejected_at = Time.now
323
+ self.rejected_by = user.id
324
+ self.status = 'rejected'
325
+ self.save!
326
+ self.order_items.each(&:reject!)
327
+ Shoppe::OrderMailer.rejected(self).deliver
328
+ end
326
329
  end
327
- end
328
330
 
329
- # This method will mark an order as shipped and store the given consignment number with the
330
- # order for use later in tracking.
331
- def ship!(user, consignment_number)
332
- run_callbacks :ship do
333
- self.shipped_at = Time.now
334
- self.shipped_by = user.id
335
- self.status = 'shipped'
336
- self.consignment_number = consignment_number
337
- self.save!
338
- Shoppe::OrderMailer.shipped(self).deliver
331
+ # This method will mark an order as shipped and store the given consignment number with the
332
+ # order for use later in tracking.
333
+ def ship!(user, consignment_number)
334
+ run_callbacks :ship do
335
+ self.shipped_at = Time.now
336
+ self.shipped_by = user.id
337
+ self.status = 'shipped'
338
+ self.consignment_number = consignment_number
339
+ self.save!
340
+ Shoppe::OrderMailer.shipped(self).deliver
341
+ end
339
342
  end
340
- end
341
343
 
342
- # Specify which attributes can be searched
343
- def self.ransackable_attributes(auth_object = nil)
344
- ["id", "postcode", "address1", "address2", "address3", "address4", "first_name", "last_name", "company", "email_address", "phone_number", "consignment_number", "status", "received_at"] + _ransackers.keys
345
- end
344
+ # Specify which attributes can be searched
345
+ def self.ransackable_attributes(auth_object = nil)
346
+ ["id", "postcode", "address1", "address2", "address3", "address4", "first_name", "last_name", "company", "email_address", "phone_number", "consignment_number", "status", "received_at"] + _ransackers.keys
347
+ end
346
348
 
347
- # Specify which associations can be searched
348
- def self.ransackable_associations(auth_object = nil)
349
- ['products']
350
- end
349
+ # Specify which associations can be searched
350
+ def self.ransackable_associations(auth_object = nil)
351
+ ['products']
352
+ end
351
353
 
354
+ end
352
355
  end