caboose-cms 0.5.122 → 0.5.123

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 (50) hide show
  1. checksums.yaml +8 -8
  2. data/app/assets/javascripts/caboose/admin.js +1 -0
  3. data/app/assets/javascripts/caboose/admin_edit_order.js +6 -8
  4. data/app/assets/javascripts/caboose/cart.js +2 -2
  5. data/app/assets/javascripts/caboose/jquery.datetimepicker.js +1925 -0
  6. data/app/assets/javascripts/caboose/model/all.js +1 -0
  7. data/app/assets/javascripts/caboose/model/bound_date.js +10 -3
  8. data/app/assets/javascripts/caboose/model/bound_date_time.js +121 -0
  9. data/app/assets/javascripts/caboose/model/model_binder.js +3 -0
  10. data/app/assets/stylesheets/caboose/admin.css +1 -0
  11. data/app/assets/stylesheets/caboose/jquery.datetimepicker.css +523 -0
  12. data/app/assets/templates/caboose/cart/line_items.jst.ejs +1 -1
  13. data/app/assets/templates/caboose/checkout/line_items.jst.ejs +2 -2
  14. data/app/controllers/caboose/checkout_controller.rb +3 -2
  15. data/app/controllers/caboose/line_items_controller.rb +4 -2
  16. data/app/controllers/caboose/my_account_controller.rb +43 -0
  17. data/app/controllers/caboose/my_account_orders_controller.rb +40 -6
  18. data/app/controllers/caboose/orders_controller.rb +26 -20
  19. data/app/controllers/caboose/products_controller.rb +1 -0
  20. data/app/controllers/caboose/sites_controller.rb +15 -1
  21. data/app/controllers/caboose/users_controller.rb +0 -35
  22. data/app/controllers/caboose/variants_controller.rb +51 -2
  23. data/app/controllers/caboose/vendors_controller.rb +3 -2
  24. data/app/models/caboose/address.rb +9 -1
  25. data/app/models/caboose/line_item.rb +17 -9
  26. data/app/models/caboose/model_binder.rb +63 -0
  27. data/app/models/caboose/order.rb +21 -14
  28. data/app/models/caboose/order_pdf.rb +3 -3
  29. data/app/models/caboose/pending_orders_pdf.rb +1 -1
  30. data/app/models/caboose/product.rb +15 -16
  31. data/app/models/caboose/schema.rb +20 -13
  32. data/app/models/caboose/site.rb +9 -0
  33. data/app/models/caboose/store_config.rb +6 -0
  34. data/app/models/caboose/variant.rb +15 -1
  35. data/app/views/caboose/checkout/_cart.html.erb +3 -3
  36. data/app/views/caboose/checkout/_confirm.html.erb +3 -3
  37. data/app/views/caboose/login/index.html.erb +7 -3
  38. data/app/views/caboose/{users/my_account.html.erb → my_account/index.html.erb} +7 -6
  39. data/app/views/caboose/my_account_orders/edit.html.erb +104 -0
  40. data/app/views/caboose/my_account_orders/index.html.erb +36 -0
  41. data/app/views/caboose/sites/admin_edit.html.erb +2 -0
  42. data/app/views/caboose/variants/admin_edit.html.erb +11 -1
  43. data/app/views/caboose/variants/admin_index.html.erb +5 -2
  44. data/config/routes.rb +7 -2
  45. data/lib/caboose/#Untitled-1# +264 -0
  46. data/lib/caboose/engine.rb +23 -140
  47. data/lib/caboose/version.rb +1 -1
  48. data/lib/tasks/caboose.rake +15 -1
  49. metadata +11 -4
  50. data/app/views/caboose/orders/admin_edit_old.html.erb +0 -155
@@ -18,7 +18,15 @@ module Caboose
18
18
  :country,
19
19
  :country_code,
20
20
  :phone
21
-
21
+
22
+ def name_and_address
23
+ str = "#{self.first_name} #{self.last_name}"
24
+ str << "<br />#{self.address1}"
25
+ str << "<br />#{self.address2}" if self.address2 && self.address2.length > 0
26
+ str << "<br />#{self.city}, #{self.state} #{self.zip}"
27
+ return str
28
+ end
29
+
22
30
  end
23
31
  end
24
32
 
@@ -11,8 +11,9 @@ module Caboose
11
11
  attr_accessible :id,
12
12
  :order_package_id,
13
13
  :variant_id,
14
+ :unit_price,
14
15
  :quantity,
15
- :price,
16
+ :subtotal,
16
17
  :notes,
17
18
  :order_id,
18
19
  :status,
@@ -47,20 +48,23 @@ module Caboose
47
48
  # Callbacks
48
49
  #
49
50
 
50
- before_save :update_price
51
+ before_save :update_subtotal
51
52
  after_save { self.order.calculate }
52
53
  after_initialize :check_nil_fields
53
54
 
54
- def check_nil_fields
55
- self.price = 0.00 if self.price.nil?
55
+ def check_nil_fields
56
+ self.subtotal = 0.00 if self.subtotal.nil?
56
57
  end
57
58
 
58
59
  #
59
60
  # Methods
60
61
  #
61
62
 
62
- def update_price
63
- self.price = self.variant.price * self.quantity
63
+ def update_subtotal
64
+ if self.unit_price.nil?
65
+ self.unit_price = self.variant.on_sale? ? self.variant.sale_price : self.variant.price
66
+ end
67
+ self.subtotal = self.unit_price * self.quantity
64
68
  end
65
69
 
66
70
  def title
@@ -78,15 +82,19 @@ module Caboose
78
82
  })
79
83
  end
80
84
 
81
- def subtotal
82
- return self.quantity * self.price
85
+ def verify_unit_price
86
+ if self.unit_price.nil?
87
+ self.unit_price = self.variant.on_sale? ? self.variant.sale_price : self.variant.price
88
+ self.save
89
+ end
83
90
  end
84
91
 
85
92
  def copy
86
93
  LineItem.new(
87
94
  :variant_id => self.variant_id ,
88
95
  :quantity => self.quantity ,
89
- :price => self.price ,
96
+ :unit_price => self.unit_price ,
97
+ :subtotal => self.subtotal ,
90
98
  :notes => self.notes ,
91
99
  :order_id => self.order_id ,
92
100
  :status => self.status ,
@@ -0,0 +1,63 @@
1
+
2
+ module Caboose
3
+ class ModelBinder
4
+
5
+ # Converts a 12-hour time string to a 24-hour time string.
6
+ # Example: 3:37 pm -> 15:37
7
+ def self.military_time(str)
8
+ return false if str.nil? || str.length == 0
9
+ arr = str.split(' ')
10
+ return str if arr.length == 1
11
+ hm = arr[0]
12
+ ampm = arr[1].downcase
13
+ return hm if ampm == 'am'
14
+ arr2 = hm.split(':')
15
+ arr2[0] = (arr2[0].to_i + 12).to_s
16
+ return arr2.join(':')
17
+ end
18
+
19
+ # Given a local date and time string (iso8601 format) and the local timezone,
20
+ # return a datetime object in the UTC zone.
21
+ def self.local_datetime_to_utc(str, zone)
22
+
23
+ return false if str.nil? || zone.nil?
24
+
25
+ # Split into date and time
26
+ arr = str.split('T')
27
+ arr = str.split(' ') if arr.count == 1
28
+ return false if arr.count == 1
29
+
30
+ # Split up the date into its components
31
+ date_string = arr.shift
32
+ d = date_string.split('-')
33
+ d = date_string.split('/') if d.count == 1
34
+ return false if d.count != 3
35
+ d = [d[2], d[0], d[1]] if date_string.include?('/')
36
+
37
+ # Split up the time into its components
38
+ time_string = arr.join(' ')
39
+ time_string = ModelBinder.military_time(time_string)
40
+ t = time_string.split(':')
41
+ return false if t.count != 2 && t.count != 3
42
+
43
+ # Convert timezones
44
+ old_timezone = Time.zone
45
+ Time.zone = zone
46
+ local_d = Time.zone.local(d[0].to_i, d[1].to_i, d[2].to_i, t[0].to_i, t[1].to_i, (t.count > 2 ? t[2].to_i : 0)).to_datetime.utc
47
+ Time.zone = old_timezone
48
+
49
+ return local_d
50
+ end
51
+
52
+ def self.update_date(d, value, timezone)
53
+ t = d ? d.in_time_zone(timezone).strftime('%H:%M') : '10:00'
54
+ return ModelBinder.local_datetime_to_utc("#{value} #{t}", timezone)
55
+ end
56
+
57
+ def self.update_time(d, value, timezone)
58
+ d2 = d ? d.in_time_zone(timezone).strftime('%Y-%m-%d') : DateTime.now.strftime('%Y-%m-%d')
59
+ return ModelBinder.local_datetime_to_utc("#{d2} #{value}", timezone)
60
+ end
61
+
62
+ end
63
+ end
@@ -38,21 +38,27 @@ module Caboose
38
38
  :date_created,
39
39
  :notes
40
40
 
41
- STATUS_CART = 'cart'
42
- STATUS_PENDING = 'pending'
43
- STATUS_CANCELED = 'canceled'
44
- STATUS_SHIPPED = 'shipped'
45
- STATUS_TESTING = 'testing'
41
+ STATUS_CART = 'cart'
42
+ STATUS_PENDING = 'pending'
43
+ STATUS_CANCELED = 'canceled'
44
+ STATUS_READY_TO_SHIP = 'ready to ship'
45
+ STATUS_SHIPPED = 'shipped'
46
+ STATUS_TESTING = 'testing'
47
+
48
+ FINANCIAL_STATUS_AUTHORIZED = 'authorized'
49
+ FINANCIAL_STATUS_CAPTURED = 'captured'
50
+ FINANCIAL_STATUS_REFUNDED = 'refunded'
51
+ FINANCIAL_STATUS_VOIDED = 'voided'
46
52
 
47
53
  #
48
54
  # Scopes
49
- #
50
-
55
+ #
56
+ scope :cart , where('status = ?', 'cart')
57
+ scope :pending , where('status = ?', 'pending')
58
+ scope :canceled , where('status = ?', 'canceled')
59
+ scope :shipped , where('status = ?', 'shipped')
51
60
  scope :test , where('status = ?', 'testing')
52
- scope :cancelled , where('status = ?', 'cancelled')
53
- scope :pending , where('status = ?', 'pending')
54
- #TODO scope :fulfilled
55
- #TODO scope :unfulfilled
61
+
56
62
  scope :authorized , where('financial_status = ?', 'authorized')
57
63
  scope :captured , where('financial_status = ?', 'captured')
58
64
  scope :refunded , where('financial_status = ?', 'refunded')
@@ -63,7 +69,7 @@ module Caboose
63
69
  #
64
70
 
65
71
  validates :status, :inclusion => {
66
- :in => ['cart', 'pending', 'cancelled', 'shipped', 'testing'],
72
+ :in => ['cart', 'pending', 'canceled', 'ready to ship', 'shipped', 'testing'],
67
73
  :message => "%{value} is not a valid status. Must be either 'pending' or 'shipped'"
68
74
  }
69
75
 
@@ -124,7 +130,7 @@ module Caboose
124
130
  PaymentProcessor.capture(self)
125
131
  end
126
132
 
127
- def refuned
133
+ def refund
128
134
  PaymentProcessor.refund(self)
129
135
  end
130
136
 
@@ -143,8 +149,9 @@ module Caboose
143
149
 
144
150
  def calculate_subtotal
145
151
  return 0.0 if self.line_items.empty?
152
+ self.line_items.each{ |li| li.verify_unit_price } # Make sure the unit prices are populated
146
153
  x = 0.0
147
- self.line_items.each{ |li| x = x + (li.variant.price * li.quantity) } # Fixed issue with quantity
154
+ self.line_items.each{ |li| x = x + (li.unit_price * li.quantity) } # Fixed issue with quantity
148
155
  return x
149
156
  end
150
157
 
@@ -92,9 +92,9 @@ module Caboose
92
92
  tbl << [
93
93
  "#{li.variant.product.title}\n#{li.variant.sku}\n#{li.variant.title}",
94
94
  { :content => li.tracking_number },
95
- { :content => sprintf("%.2f", li.variant.price) , :align => :right },
96
- { :content => "#{li.quantity}" , :align => :right },
97
- { :content => sprintf("%.2f", li.subtotal) , :align => :right }
95
+ { :content => sprintf("%.2f", li.unit_price) , :align => :right },
96
+ { :content => "#{li.quantity}" , :align => :right },
97
+ { :content => sprintf("%.2f", li.subtotal) , :align => :right }
98
98
  ]
99
99
  end
100
100
  tbl << [{ :content => "Subtotal" , :colspan => 4, :align => :right }, { :content => sprintf("%.2f", order.subtotal ) , :align => :right }]
@@ -66,7 +66,7 @@ module Caboose
66
66
  tbl << [
67
67
  "#{li.variant.product.title}\n#{li.variant.sku}\n#{li.variant.title}",
68
68
  { :content => li.tracking_number },
69
- { :content => sprintf("%.2f", li.variant.price) , :align => :right },
69
+ { :content => sprintf("%.2f", li.unit_price) , :align => :right },
70
70
  { :content => "#{li.quantity}" , :align => :right },
71
71
  { :content => sprintf("%.2f", li.subtotal) , :align => :right }
72
72
  ]
@@ -105,10 +105,11 @@ module Caboose
105
105
  min = 100000
106
106
  max = 0
107
107
 
108
- self.variants.each do |variant|
109
- next if variant.nil? or variant.price.nil? or variant.price <= 0
110
- min = variant.price if variant.price < min
111
- max = variant.price if variant.price > max
108
+ self.variants.each do |variant|
109
+ next if variant.nil? || variant.price.nil? || variant.price <= 0
110
+ price = variant.on_sale? ? v.sale_price : v.price
111
+ min = price if price < min
112
+ max = price if price > max
112
113
  end
113
114
 
114
115
  return [min, max]
@@ -171,18 +172,16 @@ module Caboose
171
172
  end
172
173
  end
173
174
 
174
- # TODO: Implement product sales
175
- #def self.products_on_sale
176
- #
177
- # query[0] = "select distinct product_id from store_variants
178
- # where sale_price is not null
179
- # and date_sale_starts < now()
180
- # and date_sale_ends > now()
181
- # order by title limit 20"
182
- # rows = ActiveRecord::Base.connection.select_rows(ActiveRecord::Base.send(:sanitize_sql_array, query))
183
- # arr = rows.collect{ |row| { :id => row[0], :title => row[1] }}
184
- #
185
- #end
175
+ def update_on_sale
176
+ is_on_sale = false
177
+ self.variants.each do |v|
178
+ is_on_sale = true if v.on_sale?
179
+ end
180
+ if (is_on_sale && !self.on_sale) || (!is_on_sale && self.on_sale)
181
+ self.on_sale = is_on_sale
182
+ self.save
183
+ end
184
+ end
186
185
 
187
186
  end
188
187
  end
@@ -57,13 +57,12 @@ class Caboose::Schema < Caboose::Utilities::Schema
57
57
  Caboose::Order => [
58
58
  :shipping_method ,
59
59
  :shipping_method_code ,
60
- :email ,
61
- :order_number ,
60
+ :email ,
62
61
  :payment_id ,
63
62
  :gateway_id ,
64
63
  :date_authorized ,
65
64
  :date_captured ,
66
- :date_cancelled ,
65
+ :date_canceled ,
67
66
  :shipping_carrier ,
68
67
  :shipping_service_code ,
69
68
  :shipping_service_name ,
@@ -79,7 +78,7 @@ class Caboose::Schema < Caboose::Utilities::Schema
79
78
  Caboose::ShippingPackage => [:price, :carrier, :service_code, :service_name, :shipping_method_id, :length, :width, :height],
80
79
  Caboose::Site => [:shipping_cost_function],
81
80
  Caboose::StoreConfig => [:use_usps, :allowed_shipping_codes, :default_shipping_code, :pp_relay_url, :pp_response_url],
82
- Caboose::Variant => [:quantity],
81
+ Caboose::Variant => [:quantity, :on_sale],
83
82
  Caboose::Vendor => [:vendor, :vendor_id]
84
83
  }
85
84
  end
@@ -285,11 +284,12 @@ class Caboose::Schema < Caboose::Utilities::Schema
285
284
  [ :order_id , :integer ],
286
285
  [ :order_package_id , :integer ],
287
286
  [ :variant_id , :integer ],
288
- [ :parent_id , :integer ],
289
- [ :quantity , :integer , :default => 0 ],
287
+ [ :parent_id , :integer ],
290
288
  [ :status , :string ],
291
- [ :tracking_number , :string ],
292
- [ :price , :decimal , { :precision => 8, :scale => 2 }],
289
+ [ :tracking_number , :string ],
290
+ [ :quantity , :integer , :default => 0 ],
291
+ [ :unit_price , :decimal , { :precision => 8, :scale => 2 }],
292
+ [ :subtotal , :decimal , { :precision => 8, :scale => 2 }],
293
293
  [ :notes , :text ],
294
294
  [ :custom1 , :string ],
295
295
  [ :custom2 , :string ],
@@ -336,6 +336,7 @@ class Caboose::Schema < Caboose::Utilities::Schema
336
336
  ],
337
337
  Caboose::Order => [
338
338
  [ :site_id , :integer ],
339
+ [ :order_number , :integer ],
339
340
  [ :alternate_id , :integer ],
340
341
  [ :subtotal , :decimal , { :precision => 8, :scale => 2 }],
341
342
  [ :tax , :decimal , { :precision => 8, :scale => 2 }],
@@ -354,7 +355,9 @@ class Caboose::Schema < Caboose::Utilities::Schema
354
355
  [ :referring_site , :text ],
355
356
  [ :landing_page , :string ],
356
357
  [ :landing_page_ref , :string ],
357
- [ :auth_amount , :decimal , { :precision => 8, :scale => 2 }]
358
+ [ :auth_amount , :decimal , { :precision => 8, :scale => 2 }],
359
+ [ :gift_message , :text ],
360
+ [ :include_receipt , :boolean , { :default => true }]
358
361
 
359
362
  #[ :email , :string ],
360
363
  #[ :order_number , :string ],
@@ -467,7 +470,8 @@ class Caboose::Schema < Caboose::Utilities::Schema
467
470
  [ :custom_input , :text ],
468
471
  [ :sort_order , :integer ],
469
472
  [ :featured , :boolean , :default => false ],
470
- [ :stackable_group_id , :integer ]
473
+ [ :stackable_group_id , :integer ],
474
+ [ :on_sale , :boolean , { :default => false }]
471
475
  ],
472
476
  Caboose::ProductImage => [
473
477
  [ :product_id , :integer ],
@@ -550,7 +554,8 @@ class Caboose::Schema < Caboose::Utilities::Schema
550
554
  [ :name , :string ],
551
555
  [ :description , :text ],
552
556
  [ :under_construction_html , :text ],
553
- [ :use_store , :boolean , { :default => false }]
557
+ [ :use_store , :boolean , { :default => false }],
558
+ [ :logo , :attachment ]
554
559
  ],
555
560
  Caboose::SiteMembership => [
556
561
  [ :site_id , :integer ],
@@ -618,7 +623,8 @@ class Caboose::Schema < Caboose::Utilities::Schema
618
623
  [ :shipping_rates_function , :text ],
619
624
  [ :length_unit , :string , { :default => 'in' }],
620
625
  [ :weight_unit , :string , { :default => 'oz' }],
621
- [ :download_url_expires_in , :string , { :default => 5 }]
626
+ [ :download_url_expires_in , :string , { :default => 5 }],
627
+ [ :starting_order_number , :integer , { :default => 1000 }]
622
628
  ],
623
629
  Caboose::User => [
624
630
  [ :site_id , :integer ],
@@ -652,7 +658,8 @@ class Caboose::Schema < Caboose::Utilities::Schema
652
658
  [ :price , :decimal , { :precision => 8, :scale => 2 }],
653
659
  [ :sale_price , :decimal , { :precision => 8, :scale => 2 }],
654
660
  [ :date_sale_starts , :datetime ],
655
- [ :date_sale_end , :datetime ],
661
+ [ :date_sale_ends , :datetime ],
662
+ [ :date_sale_end , :datetime ],
656
663
  [ :available , :boolean ],
657
664
  [ :quantity_in_stock , :integer , :default => 0 ],
658
665
  [ :ignore_quantity , :boolean ],
@@ -8,6 +8,15 @@ class Caboose::Site < ActiveRecord::Base
8
8
  has_many :domains, :class_name => 'Caboose::Domain', :dependent => :delete_all
9
9
  has_many :post_categories, :class_name => 'Caboose::PostCategory'
10
10
  has_one :store_config
11
+ has_attached_file :logo,
12
+ :path => ':path_prefixsite_logos/:id_:style.:extension',
13
+ :default_url => 'http://placehold.it/300x300',
14
+ :styles => {
15
+ :tiny => '150x200>',
16
+ :thumb => '300x400>',
17
+ :large => '600x800>'
18
+ }
19
+ do_not_validate_attachment_file_type :logo
11
20
  attr_accessible :id, :name, :description, :under_construction_html
12
21
 
13
22
  def smtp_config
@@ -32,6 +32,12 @@ module Caboose
32
32
  :shipping_rates_function,
33
33
  :length_unit,
34
34
  :weight_unit
35
+
36
+ def next_order_number
37
+ x = Order.where("order_number is not null").reorder("order_number desc").limit(1).first
38
+ return x.order_number + 1 if x
39
+ return self.starting_order_number
40
+ end
35
41
 
36
42
  end
37
43
  end
@@ -102,6 +102,20 @@ module Caboose
102
102
  arr << self.option3 if self.option3 && self.option3.strip.length > 0
103
103
  return arr
104
104
  end
105
-
105
+
106
+ def product_image
107
+ return self.product_images.first if self.product_images
108
+ return self.product.product_images.first if self.product.product_images
109
+ return nil
110
+ end
111
+
112
+ def on_sale?
113
+ return false if self.sale_price.nil?
114
+ d = DateTime.now.utc
115
+ return false if self.date_sale_starts && d < self.date_sale_starts
116
+ return false if self.date_sale_ends && d > self.date_sale_ends
117
+ return true
118
+ end
119
+
106
120
  end
107
121
  end
@@ -16,9 +16,9 @@
16
16
  <% end %>
17
17
  </td>
18
18
  <td valign='top'><%= li.title %></td>
19
- <td valign='top' align='right' class='qty' style='text-align: right;'><%= li.quantity %></td>
20
- <td valign='top' align='right' class='price' style='text-align: right;'><%= number_to_currency(li.price, :precision => 2) %></td>
21
- <td valign='top' align='right' class='subtotal' style='text-align: right;'><%= number_to_currency(li.price, :precision => 2) %></td>
19
+ <td valign='top' align='right' class='qty' style='text-align: right;'><%= li.quantity %></td>
20
+ <td valign='top' align='right' class='unit_price' style='text-align: right;'><%= number_to_currency(li.unit_price , :precision => 2) %></td>
21
+ <td valign='top' align='right' class='subtotal' style='text-align: right;'><%= number_to_currency(li.subtotal , :precision => 2) %></td>
22
22
  </tr>
23
23
  <% end %>
24
24
  <tr><td colspan='4' align='right' style='text-align: right;'>Subtotal: </td><td align='right' style='text-align: right;'><%= number_to_currency(@order.subtotal , :precision => 2) %></td></tr>
@@ -16,9 +16,9 @@
16
16
  <% end %>
17
17
  <p><%= li.title %></p>
18
18
  </td>
19
- <td valign='top' align='right' class='qty' style='text-align: right;'><%= li.quantity %></td>
20
- <td valign='top' align='right' class='price' style='text-align: right;'><%= number_to_currency(li.price, :precision => 2) %></td>
21
- <td valign='top' align='right' class='subtotal' style='text-align: right;'><%= number_to_currency(li.price, :precision => 2) %></td>
19
+ <td valign='top' align='right' class='qty' style='text-align: right;'><%= li.quantity %></td>
20
+ <td valign='top' align='right' class='unit_price' style='text-align: right;'><%= number_to_currency(li.unit_price , :precision => 2) %></td>
21
+ <td valign='top' align='right' class='subtotal' style='text-align: right;'><%= number_to_currency(li.subtotal , :precision => 2) %></td>
22
22
  </tr>
23
23
  <% end %>
24
24
  <tr><td colspan='3' align='right' style='text-align: right;'>Subtotal: </td><td align='right' style='text-align: right;'><%= number_to_currency(@order.subtotal, :precision => 2) %></td></tr>