caboose-cms 0.5.15 → 0.5.16

Sign up to get free protection for your applications and to get access to all the features.
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