caboose-cms 0.5.15 → 0.5.16

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 (40) hide show
  1. checksums.yaml +9 -9
  2. data/app/assets/javascripts/caboose/cart2.js +98 -0
  3. data/app/assets/javascripts/caboose/model/index_table.js +82 -30
  4. data/app/assets/javascripts/caboose/model/model_binder.js +4 -4
  5. data/app/assets/stylesheets/caboose/admin.css +1 -0
  6. data/app/assets/stylesheets/caboose/admin_main.css +0 -42
  7. data/app/assets/stylesheets/caboose/cart.scss +41 -0
  8. data/app/assets/stylesheets/caboose/checkout.css.scss +2 -3
  9. data/app/assets/stylesheets/caboose/message_boxes.scss +49 -0
  10. data/app/assets/templates/caboose/cart/add_to_cart.jst.ejs +1 -1
  11. data/app/controllers/caboose/block_types_controller.rb +3 -1
  12. data/app/controllers/caboose/cart_controller.rb +30 -23
  13. data/app/controllers/caboose/checkout_controller.rb +11 -1
  14. data/app/controllers/caboose/products_controller.rb +28 -14
  15. data/app/controllers/caboose/stackable_groups_controller.rb +107 -0
  16. data/app/models/caboose/line_item.rb +20 -6
  17. data/app/models/caboose/order.rb +2 -1
  18. data/app/models/caboose/order_package.rb +103 -0
  19. data/app/models/caboose/product.rb +2 -1
  20. data/app/models/caboose/schema.rb +45 -3
  21. data/app/models/caboose/shipping_calculator.rb +101 -60
  22. data/app/models/caboose/shipping_package.rb +80 -0
  23. data/app/models/caboose/site.rb +3 -1
  24. data/app/models/caboose/stackable_group.rb +17 -0
  25. data/app/models/caboose/tax_calculator.rb +1 -2
  26. data/app/models/caboose/variant.rb +2 -5
  27. data/app/views/caboose/cart/index.html.erb +19 -6
  28. data/app/views/caboose/checkout/_cart.html.erb +49 -52
  29. data/app/views/caboose/checkout/empty.html.erb +9 -2
  30. data/app/views/caboose/checkout/step_one.html.erb +2 -3
  31. data/app/views/caboose/checkout/step_three.html.erb +28 -10
  32. data/app/views/caboose/checkout/step_two.html.erb +2 -4
  33. data/app/views/caboose/pages/admin_edit_content.html.erb +1 -1
  34. data/app/views/caboose/products/admin_index.html.erb +12 -11
  35. data/app/views/caboose/sites/admin_edit.html.erb +33 -6
  36. data/app/views/caboose/stackable_groups/admin_index.html.erb +43 -0
  37. data/config/routes.rb +15 -6
  38. data/lib/caboose/version.rb +1 -1
  39. data/lib/tasks/caboose.rake +7 -1
  40. metadata +36 -14
@@ -2,13 +2,13 @@
2
2
  // Checkout
3
3
  //
4
4
 
5
- #checkout { padding-top: 75px }
5
+ #checkout { }
6
6
  #checkout * { box-sizing: border-box }
7
7
 
8
8
  #checkout input[type=text],
9
9
  #checkout input[type=password],
10
10
  #checkout input[type=email] {
11
- outline: none !important;
11
+ border-width: 1px;
12
12
  height: 36px;
13
13
  }
14
14
 
@@ -199,7 +199,6 @@
199
199
  }
200
200
 
201
201
  #checkout #checkout-address {
202
- margin: 42px 0;
203
202
  .wrapper { overflow: hidden }
204
203
  h1, h2, h3, h4, h5, h6 {
205
204
  text-align: left;
@@ -0,0 +1,49 @@
1
+
2
+ /*******************************************************************************
3
+ Message boxes
4
+ *******************************************************************************/
5
+
6
+ .loading {
7
+ background-image: url('/assets/caboose/loading_black_on_white.gif');
8
+ background-position: 0 center;
9
+ background-repeat: no-repeat;
10
+ color: #000;
11
+ font-weight: normal;
12
+ font-size: 16pt;
13
+ padding: 10px 10px 10px 40px;
14
+ }
15
+
16
+ .note {
17
+ padding: 20px;
18
+ margin-bottom: 10px;
19
+ background: #fef49c;
20
+ color: #000000;
21
+ font-weight: bold;
22
+ font-size: 1.4em;
23
+ border-radius: 3px;
24
+
25
+ h2 { margin: 0 0 10px 0; padding: 0; }
26
+ }
27
+
28
+ .note_small {
29
+ display: block;
30
+ padding: 4px 10px;
31
+ background: #fef49c;
32
+ color: #000000;
33
+ font-size: 1em;
34
+ border-radius: 3px;
35
+ }
36
+
37
+ .error {
38
+ background: #9c171b;
39
+ color: #ffffff;
40
+ a { color: #fff; }
41
+ a:hover { color: #000; }
42
+ }
43
+
44
+ .success {
45
+ background: #009900;
46
+ color: #ffffff;
47
+ a { color: #ffffff; }
48
+ }
49
+
@@ -1,4 +1,4 @@
1
- <form action="/cart/items" method="post">
1
+ <form action="/cart" method="post">
2
2
  <input type="hidden" name="variant_id" />
3
3
  <label for="quantity">Qty:</label>
4
4
  <input type="text" name="quantity" value="1" />
@@ -23,13 +23,15 @@ module Caboose
23
23
  h['parent_id'] = ''
24
24
  else
25
25
  h['parent_id_null'] = true
26
+ params[:parent_id_null] = nil if params[:parent_id_null]
26
27
  end
27
28
  pager = Caboose::Pager.new(params, h, {
28
29
  'model' => 'Caboose::BlockType',
29
30
  'sort' => 'description',
30
31
  'desc' => 'false',
31
32
  'base_url' => "/admin/block-types",
32
- 'items_per_page' => 10
33
+ 'items_per_page' => 10,
34
+ 'skip' => ['parent_id_null']
33
35
  })
34
36
  render :json => {
35
37
  :pager => pager,
@@ -1,13 +1,8 @@
1
1
  module Caboose
2
2
  class CartController < Caboose::ApplicationController
3
- before_filter :get_line_item, :only => [:update, :remove]
4
-
5
- def get_line_item
6
- @line_item = @order.line_items.find(params[:id])
7
- end
8
3
 
9
4
  # GET /cart
10
- def index
5
+ def index
11
6
  end
12
7
 
13
8
  # GET /cart/items
@@ -20,31 +15,43 @@ module Caboose
20
15
  render :json => { :item_count => @order.line_items.count }
21
16
  end
22
17
 
23
- # POST /cart/add
18
+ # POST /cart
24
19
  def add
25
- if @order.line_items.exists?(:variant_id => params[:variant_id])
26
- @line_item = @order.line_items.find_by_variant_id(params[:variant_id])
27
- @line_item.quantity += params[:quantity] ? params[:quantity].to_i : 1
28
- else
29
- @line_item = LineItem.new
30
- @line_item.variant_id = params[:variant_id]
31
- @line_item.order_id = @order.id
32
- @line_item.status = 'pending'
33
- @line_item.quantity = params[:quantity] ? params[:quantity].to_i : 1
34
- end
20
+ variant_id = params[:variant_id]
21
+ qty = params[:quantity] ? params[:quantity].to_i : 1
35
22
 
36
- render :json => { :success => @line_item.save, :errors => @line_item.errors.full_messages, :item_count => @order.line_items.count }
23
+ if @order.line_items.exists?(:variant_id => variant_id)
24
+ li = @order.line_items.find_by_variant_id(variant_id)
25
+ li.quantity += qty
26
+ else
27
+ li = LineItem.new(
28
+ :order_id => @order.id,
29
+ :variant_id => variant_id,
30
+ :quantity => qty,
31
+ :status => 'pending'
32
+ )
33
+ end
34
+ render :json => {
35
+ :success => li.save,
36
+ :errors => li.errors.full_messages,
37
+ :item_count => @order.line_items.count
38
+ }
37
39
  end
38
40
 
39
- # PUT cart/update
41
+ # PUT /cart/:line_item_id
40
42
  def update
41
- @line_item.quantity = params[:quantity].to_i
42
- render :json => { :success => @line_item.save, :errors => @line_item.errors.full_messages, :line_item => @line_item, :order_subtotal => @order.calculate_subtotal }
43
+ li = LineItem.find(params[:line_item_id])
44
+ li.quantity = params[:quantity].to_i
45
+ li.save
46
+ li.destroy if li.quantity == 0
47
+ @order.calculate_subtotal
48
+ render :json => { :success => true }
43
49
  end
44
50
 
45
- # DELETE cart/delete
51
+ # DELETE /cart/:line_item_id
46
52
  def remove
47
- render :json => { :success => !!@order.line_items.delete(@line_item), :item_count => @order.line_items.count }
53
+ li = LineItem.find(params[:line_item_id]).destroy
54
+ render :json => { :success => true, :item_count => @order.line_items.count }
48
55
  end
49
56
  end
50
57
  end
@@ -36,8 +36,18 @@ module Caboose
36
36
  def step_three
37
37
  redirect_to '/checkout/step-one' and return if !logged_in?
38
38
  redirect_to '/checkout/step-two' and return if @order.shipping_address.nil? || @order.billing_address.nil?
39
+
40
+ # Remove any order packages
41
+ LineItem.where(:order_id => @order.id).update_all(:order_package_id => nil)
42
+ OrderPackage.where(:order_id => @order.id).destroy_all
43
+
44
+ # Calculate what shipping packages we'll need
45
+ OrderPackage.create_for_order(@order)
46
+
47
+ # Now get the rates for those packages
48
+ Caboose.log("Getting rates...")
39
49
  @rates = ShippingCalculator.rates(@order)
40
- @selected_rate = ShippingCalculator.rate(@order)
50
+ Caboose.log(@rates.inspect)
41
51
  end
42
52
 
43
53
  # GET /checkout/step-four
@@ -577,20 +577,21 @@ module Caboose
577
577
  save = true
578
578
  params.each do |name,value|
579
579
  case name
580
- when 'alternate_id' then product.alternate_id = value
581
- when 'title' then product.title = value
582
- when 'caption' then product.caption = value
583
- when 'featured' then product.featured = value
584
- when 'description' then product.description = value
585
- when 'vendor_id' then product.vendor_id = value
586
- when 'handle' then product.handle = value
587
- when 'seo_title' then product.seo_title = value
588
- when 'seo_description' then product.seo_description = value
589
- when 'status' then product.status = value
590
- when 'category_id' then product.toggle_category(value[0], value[1])
591
- when 'option1' then product.option1 = value
592
- when 'option2' then product.option2 = value
593
- when 'option3' then product.option3 = value
580
+ when 'alternate_id' then product.alternate_id = value
581
+ when 'title' then product.title = value
582
+ when 'caption' then product.caption = value
583
+ when 'featured' then product.featured = value
584
+ when 'description' then product.description = value
585
+ when 'vendor_id' then product.vendor_id = value
586
+ when 'handle' then product.handle = value
587
+ when 'seo_title' then product.seo_title = value
588
+ when 'seo_description' then product.seo_description = value
589
+ when 'status' then product.status = value
590
+ when 'category_id' then product.toggle_category(value[0], value[1])
591
+ when 'stackable_group_id' then product.stackable_group_id = value
592
+ when 'option1' then product.option1 = value
593
+ when 'option2' then product.option2 = value
594
+ when 'option3' then product.option3 = value
594
595
  when 'default1'
595
596
  product.default1 = value
596
597
  Variant.where(:product_id => product.id, :option1 => nil).each do |p|
@@ -677,6 +678,19 @@ module Caboose
677
678
  render :json => options
678
679
  end
679
680
 
681
+ # GET /products/stackable-group-options
682
+ def admin_stackable_group_options
683
+ arr = ['Active', 'Inactive', 'Deleted']
684
+ options = []
685
+ arr.each do |status|
686
+ options << {
687
+ :value => status,
688
+ :text => status
689
+ }
690
+ end
691
+ render :json => options
692
+ end
693
+
680
694
  # GET /admin/products/combine
681
695
  def admin_combine_select_products
682
696
  end
@@ -0,0 +1,107 @@
1
+ module Caboose
2
+ class StackableGroupsController < Caboose::ApplicationController
3
+
4
+ # GET /admin/stackable-groups
5
+ def admin_index
6
+ return if !user_is_allowed('products', 'view')
7
+ render :layout => 'caboose/admin'
8
+ end
9
+
10
+ # GET /admin/stackable-groups/json
11
+ def admin_json
12
+ return if !user_is_allowed('products', 'view')
13
+
14
+ pager = Caboose::PageBarGenerator.new(params, {
15
+ 'name' => ''
16
+ }, {
17
+ 'model' => 'Caboose::StackableGroup',
18
+ 'sort' => 'name',
19
+ 'desc' => false,
20
+ 'base_url' => '/admin/stackable-groups',
21
+ 'items_per_page' => 25,
22
+ 'use_url_params' => false
23
+ })
24
+ render :json => {
25
+ :pager => pager,
26
+ :models => pager.items
27
+ }
28
+ end
29
+
30
+ # GET /admin/stackable-groups/:id/json
31
+ def admin_json_single
32
+ sg = StackableGroup.find(params[:id])
33
+ render :json => sg
34
+ end
35
+
36
+ # PUT /admin/stackable-groups/:id
37
+ def admin_update
38
+ return if !user_is_allowed('products', 'edit')
39
+
40
+ resp = Caboose::StdClass.new
41
+ sg = StackableGroup.find(params[:id])
42
+
43
+ save = true
44
+ params.each do |name,value|
45
+ case name
46
+ when 'name' then sg.name = value
47
+ when 'extra_length' then sg.extra_length = value.to_f
48
+ when 'extra_width' then sg.extra_width = value.to_f
49
+ when 'extra_height' then sg.extra_height = value.to_f
50
+ when 'max_length' then sg.max_length = value.to_f
51
+ when 'max_width' then sg.max_width = value.to_f
52
+ when 'max_height' then sg.max_height = value.to_f
53
+ end
54
+ end
55
+ resp.success = save && sg.save
56
+ render :json => resp
57
+ end
58
+
59
+ # POST /admin/stackable-groups
60
+ def admin_add
61
+ return if !user_is_allowed('products', 'add')
62
+
63
+ resp = Caboose::StdClass.new
64
+ name = params[:name]
65
+
66
+ if name.length == 0
67
+ resp.error = "The title cannot be empty."
68
+ elsif StackableGroup.where(:name => name).exists?
69
+ resp.error = "A stackable group with that name already exists."
70
+ else
71
+ sg = StackableGroup.new(:name => name)
72
+ sg.save
73
+ resp.refresh = true
74
+ end
75
+ render :json => resp
76
+ end
77
+
78
+ # DELETE /admin/stackable-groups/:id
79
+ def admin_delete
80
+ return if !user_is_allowed('products', 'delete')
81
+ StackableGroup.find(params[:id]).destroy
82
+ render :json => true
83
+ end
84
+
85
+ # DELETE /admin/stackable-groups/bulk
86
+ def admin_bulk_delete
87
+ return if !user_is_allowed('products', 'delete')
88
+ params[:model_ids].each do |sg_id|
89
+ sg = StackableGroup.where(:id => sg_id).first
90
+ sg.destroy if sg
91
+ end
92
+ render :json => { :success => true }
93
+ end
94
+
95
+ # GET /admin/stackable-groups/options
96
+ def admin_options
97
+ options = StackableGroup.reorder(:name).all.collect do |sg|
98
+ {
99
+ :value => sg.id,
100
+ :text => sg.name
101
+ }
102
+ end
103
+ render :json => options
104
+ end
105
+
106
+ end
107
+ end
@@ -3,18 +3,19 @@ module Caboose
3
3
  self.table_name = 'store_line_items'
4
4
 
5
5
  belongs_to :variant
6
- belongs_to :order, :dependent => :destroy
6
+ belongs_to :order
7
+ belongs_to :order_package, :class_name => 'OrderPackage'
7
8
  belongs_to :parent, :class_name => 'LineItem', :foreign_key => 'parent_id'
8
9
  has_many :children, :class_name => 'LineItem', :foreign_key => 'parent_id'
9
10
 
10
11
  attr_accessible :id,
12
+ :order_package_id,
11
13
  :variant_id,
12
14
  :quantity,
13
15
  :price,
14
16
  :notes,
15
17
  :order_id,
16
- :status,
17
- :tracking_number,
18
+ :status,
18
19
  :custom1,
19
20
  :custom2,
20
21
  :custom3
@@ -66,16 +67,29 @@ module Caboose
66
67
  end
67
68
 
68
69
  def as_json(options={})
69
- self.attributes.merge({
70
+ self.attributes.merge({
70
71
  :variant => self.variant,
71
- :title => self.title,
72
- :product => { :images => self.variant.product.product_images }
72
+ :title => self.title
73
73
  })
74
74
  end
75
75
 
76
76
  def subtotal
77
77
  return self.quantity * self.price
78
78
  end
79
+
80
+ def copy
81
+ LineItem.new(
82
+ :variant_id => self.variant_id ,
83
+ :quantity => self.quantity ,
84
+ :price => self.price ,
85
+ :notes => self.notes ,
86
+ :order_id => self.order_id ,
87
+ :status => self.status ,
88
+ :custom1 => self.custom1 ,
89
+ :custom2 => self.custom2 ,
90
+ :custom3 => self.custom3
91
+ )
92
+ end
79
93
  end
80
94
  end
81
95
 
@@ -12,7 +12,8 @@ module Caboose
12
12
  belongs_to :billing_address, :class_name => 'Address'
13
13
  has_many :discounts, :through => :order_discounts
14
14
  has_many :order_discounts
15
- has_many :line_items, :after_add => :line_item_added, :after_remove => :line_item_removed
15
+ has_many :line_items, :after_add => :line_item_added, :after_remove => :line_item_removed, :order => :id
16
+ has_many :packages, :class_name => 'Caboose::OrderPackage'
16
17
 
17
18
  attr_accessible :id,
18
19
  :order_number,
@@ -0,0 +1,103 @@
1
+ module Caboose
2
+ class OrderPackage < ActiveRecord::Base
3
+ self.table_name = 'store_order_packages'
4
+
5
+ belongs_to :order
6
+ belongs_to :shipping_package
7
+ has_many :line_items
8
+ attr_accessible :id,
9
+ :order_id,
10
+ :shipping_package_id,
11
+ :status,
12
+ :tracking_number
13
+
14
+ STATUS_PENDING = 'Pending'
15
+ STATUS_SHIPPED = 'Shipped'
16
+
17
+ # Calculates the shipping packages required for all the items in the order
18
+ def self.create_for_order(order)
19
+
20
+ # Make sure all the line items in the order have a quantity of 1
21
+ extra_line_items = []
22
+ order.line_items.each do |li|
23
+ if li.quantity > 1
24
+ (1..li.quantity).each{ |i|
25
+ extra_line_items << li.copy
26
+ }
27
+ li.quantity = 1
28
+ li.save
29
+ end
30
+ end
31
+ extra_line_items.each do |li|
32
+ li.quantity = 1
33
+ li.save
34
+ end
35
+
36
+ # Make sure all the items in the order have attributes set
37
+ order.line_items.each do |li|
38
+ v = li.variant
39
+ Caboose.log("Error: variant #{v.id} has a zero weight") and return false if v.weight.nil? || v.weight == 0
40
+ next if v.volume && v.volume > 0
41
+ Caboose.log("Error: variant #{v.id} has a zero length") and return false if v.length.nil? || v.length == 0
42
+ Caboose.log("Error: variant #{v.id} has a zero width" ) and return false if v.width.nil? || v.width == 0
43
+ Caboose.log("Error: variant #{v.id} has a zero height") and return false if v.height.nil? || v.height == 0
44
+ v.volume = v.length * v.width * v.height
45
+ v.save
46
+ end
47
+
48
+ # Reorder the items in the order by volume
49
+ h = {}
50
+ order.line_items.each do |li|
51
+ (1..li.quantity).each do |i|
52
+ v = li.variant
53
+ h[v.volume] = li
54
+ end
55
+ end
56
+ line_items = h.sort_by{ |k,v| k }.collect{ |x| x[1] }
57
+ all_packages = ShippingPackage.reorder(:price).all
58
+
59
+ # Now go through each variant and fit it in a new or existing package
60
+ line_items.each do |li|
61
+
62
+ # See if the item will fit in any of the existing packages
63
+ it_fits = false
64
+ order.packages.each do |op|
65
+ it_fits = op.fits(li)
66
+ if it_fits
67
+ li.order_package_id = op.id
68
+ li.save
69
+ break
70
+ end
71
+ end
72
+ next if it_fits
73
+
74
+ # Otherwise find the cheapest package the item will fit into
75
+ all_packages.each do |sp|
76
+ if sp.fits(li.variant)
77
+ op = OrderPackage.create(:order_id => order.id, :shipping_package_id => sp.id)
78
+ li.order_package_id = op.id
79
+ li.save
80
+ break
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+
87
+ def fits(line_item = nil)
88
+ variants = self.line_items.collect{ |li| li.variant }
89
+ variants << line_item.variant if line_item
90
+ return self.shipping_package.fits(variants)
91
+ end
92
+
93
+ # Gets the activemerchant package based on the shipping package
94
+ def activemerchant_package
95
+ weight = 0.0
96
+ self.line_items.each{ |li| weight = weight + li.variant.weight }
97
+ weight = weight * 0.035274 # Convert from grams to ounces
98
+ sp = self.shipping_package
99
+ return Package.new(weight, [sp.length, sp.width, sp.height], :units => :imperial)
100
+ end
101
+
102
+ end
103
+ end