shoppe 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In -->
2
+ <svg version="1.1"
3
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
4
+ x="0px" y="0px" width="77.999px" height="78px" viewBox="0 0 77.999 78" enable-background="new 0 0 77.999 78"
5
+ xml:space="preserve">
6
+ <defs>
7
+ </defs>
8
+ <g>
9
+ <path d="M71.807,6.191c-7.215-7.216-12.629-6.133-12.629-6.133l-25.26,25.259L5.049,54.185L0,78l23.812-5.051l28.869-28.869
10
+ l25.26-25.257C77.941,18.824,79.025,13.409,71.807,6.191z M22.395,70.086l-8.117,1.748c-0.785-1.467-1.727-2.932-3.455-4.659
11
+ c-1.727-1.727-3.193-2.669-4.658-3.456l1.75-8.116l2.346-2.348c0,0,4.418,0.088,9.404,5.078c4.988,4.987,5.078,9.407,5.078,9.407
12
+ L22.395,70.086z"/>
13
+ </g>
14
+ </svg>
@@ -94,10 +94,26 @@ header.main {
94
94
  #flash-notice { background:#21a3e0; padding:15px 35px; color:#fff; font-size:1.2em; font-weight:bold;}
95
95
  #flash-alert { background:#e04e21; padding:15px 35px; color:#fff; font-size:1.2em; font-weight:bold;}
96
96
 
97
+ //
98
+ // sub title
99
+ //
100
+ .subtitle {
101
+ background:#fff;
102
+ border:1px solid #CFD4DD;
103
+ margin-bottom:25px;
104
+ padding:15px;
105
+ h3 { font-size:2.0em; font-weight:500;}
106
+ }
107
+
97
108
  //
98
109
  // data table
99
110
  //
100
111
  div.table {
112
+ p.info {
113
+ overflow:hidden;
114
+ margin-bottom:10px;
115
+ color:#40454D;
116
+ }
101
117
  table.data {
102
118
  width:100%;
103
119
  th, td { padding:10px;}
@@ -110,6 +126,15 @@ header.main {
110
126
  tfoot td.bold { font-weight:bold;}
111
127
  tfoot td { border-top:0 solid #ccc; background:#fafafa;}
112
128
  tfoot tr:first-child td { border-top-width:3px;}
129
+ tbody td a.edit { background:image-url('shoppe/icons/edit.svg') no-repeat; margin-top:3px;width:16px; width:16px; background-size:13px; display:inline-block; text-indent:-40000px; opacity:0.3}
130
+ tbody td a.edit:hover { opacity:0.5}
131
+ tbody tr.form {
132
+ td { background:#f2f6fb;}
133
+ input[type=text] { border:1px solid #CFD4DD; padding:5px;}
134
+ input[type=text]:focus { border-color:#9AC835;}
135
+ td.desc input[type=text] { width:95%;}
136
+ td.adjustment input[type=text] { width:50px; margin-right:5px;}
137
+ }
113
138
  }
114
139
  }
115
140
 
@@ -168,7 +193,7 @@ header.main {
168
193
  dd select { width:200px;}
169
194
  dd textarea { height:80px; }
170
195
  dd p.help { margin-top:15px; color:#333; background:image-url('shoppe/icons/support.svg') no-repeat 0 1px; background-size:13px; padding-left:15px;}
171
- dd.checkbox { margin-top:10px; font-size:1.1em; color:#999;}
196
+ dd.checkbox { margin-top:14px; font-size:1.1em; color:#999;}
172
197
  dd.checkbox input { float:left; margin-right:10px; margin-top:2px; }
173
198
 
174
199
  &.half {
@@ -374,4 +399,11 @@ footer {
374
399
  text-decoration:none;
375
400
  }
376
401
  }
377
- }
402
+ }
403
+
404
+ span.boolean span.true { color:#38BA4F}
405
+ span.boolean span.false { color:#c04a4a}
406
+
407
+ .float-right { float:right;}
408
+
409
+ div.field_with_errors { display:inline;}
@@ -4,7 +4,7 @@ class Shoppe::ProductsController < Shoppe::ApplicationController
4
4
  before_filter { params[:id] && @product = Shoppe::Product.find(params[:id]) }
5
5
 
6
6
  def index
7
- @products = Shoppe::Product.includes(:default_image, :product_category).order(:title).group_by(&:product_category).sort_by { |cat,pro| cat.name }
7
+ @products = Shoppe::Product.includes(:stock_level_adjustments, :default_image, :product_category).order(:title).group_by(&:product_category).sort_by { |cat,pro| cat.name }
8
8
  end
9
9
 
10
10
  def new
@@ -36,10 +36,24 @@ class Shoppe::ProductsController < Shoppe::ApplicationController
36
36
  redirect_to :products, :flash => {:notice => "Product has been removed successfully"}
37
37
  end
38
38
 
39
+ def stock_levels
40
+ @stock_level_adjustments = @product.stock_level_adjustments.ordered.page(params[:page])
41
+ if request.post?
42
+ @new_sla = @product.stock_level_adjustments.build(params[:stock_level_adjustment].permit(:description, :adjustment))
43
+ if @new_sla.save
44
+ redirect_to [:stock_levels, @product], :notice => "Stock level adjustment has been recorded"
45
+ else
46
+ flash.now[:alert] = @new_sla.errors.full_messages.to_sentence
47
+ end
48
+ else
49
+ @new_sla = @product.stock_level_adjustments.build
50
+ end
51
+ end
52
+
39
53
  private
40
54
 
41
55
  def safe_params
42
- params[:product].permit(:product_category_id, :title, :sku, :permalink, :description, :short_description, :weight, :price, :cost_price, :tax_rate, :stock_control, :stock, :default_image_file, :data_sheet_file, :active, :featured, :in_the_box, :product_attributes_array => [:key, :value, :searchable, :public])
56
+ params[:product].permit(:product_category_id, :title, :sku, :permalink, :description, :short_description, :weight, :price, :cost_price, :tax_rate, :stock_control, :default_image_file, :data_sheet_file, :active, :featured, :in_the_box, :product_attributes_array => [:key, :value, :searchable, :public])
43
57
  end
44
58
 
45
59
  end
@@ -301,6 +301,7 @@ class Shoppe::Order < ActiveRecord::Base
301
301
  self.accepted_by = user.id
302
302
  self.status = 'accepted'
303
303
  self.save!
304
+ self.order_items.each(&:accept!)
304
305
  Shoppe::OrderMailer.accepted(self).deliver
305
306
  end
306
307
  end
@@ -312,6 +313,7 @@ class Shoppe::Order < ActiveRecord::Base
312
313
  self.rejected_by = user.id
313
314
  self.status = 'rejected'
314
315
  self.save!
316
+ self.order_items.each(&:reject!)
315
317
  Shoppe::OrderMailer.rejected(self).deliver
316
318
  end
317
319
  end
@@ -6,6 +6,7 @@ class Shoppe::OrderItem < ActiveRecord::Base
6
6
  # Relationships
7
7
  belongs_to :order, :class_name => 'Shoppe::Order'
8
8
  belongs_to :product, :class_name => 'Shoppe::Product'
9
+ has_many :stock_level_adjustments, :as => :parent, :dependent => :nullify
9
10
 
10
11
  # Validations
11
12
  validates :quantity, :numericality => true
@@ -91,7 +92,18 @@ class Shoppe::OrderItem < ActiveRecord::Base
91
92
  # This method will be triggered when the parent order is confirmed. This should automatically
92
93
  # update the stock levels on the source product.
93
94
  def confirm!
94
- self.product.update_stock_level(quantity)
95
+ if self.product.stock_control?
96
+ self.product.stock_level_adjustments.create(:parent => self, :adjustment => 0 - self.quantity, :description => "Order ##{self.order.number} deduction")
97
+ end
98
+ end
99
+
100
+ # This method will be trigger when the parent order is accepted.
101
+ def accept!
102
+ end
103
+
104
+ # This method will be trigger when the parent order is rejected.
105
+ def reject!
106
+ self.stock_level_adjustments.destroy_all
95
107
  end
96
108
 
97
109
  # Do we have the stock needed to fulfil this order?
@@ -14,6 +14,7 @@ class Shoppe::Product < ActiveRecord::Base
14
14
  belongs_to :product_category, :class_name => 'Shoppe::ProductCategory'
15
15
  has_many :order_items, :dependent => :restrict_with_exception, :class_name => 'Shoppe::OrderItem'
16
16
  has_many :orders, :through => :order_items, :class_name => 'Shoppe::Order'
17
+ has_many :stock_level_adjustments, :dependent => :destroy
17
18
 
18
19
  # Validations
19
20
  validates :product_category_id, :presence => true
@@ -26,7 +27,6 @@ class Shoppe::Product < ActiveRecord::Base
26
27
  validates :price, :numericality => true
27
28
  validates :cost_price, :numericality => true, :allow_blank => true
28
29
  validates :tax_rate, :numericality => true
29
- validates :stock, :numericality => {:only_integer => true}
30
30
 
31
31
  # Set the permalink
32
32
  before_validation { self.permalink = self.title.parameterize if self.permalink.blank? && self.title.is_a?(String) }
@@ -37,15 +37,12 @@ class Shoppe::Product < ActiveRecord::Base
37
37
 
38
38
  # Is this product currently in stock?
39
39
  def in_stock?
40
- !stock_control? || stock > 0
40
+ stock > 0
41
41
  end
42
42
 
43
- # Remove the provided number of units from the current stock level of this product
44
- def update_stock_level(purchased = 1)
45
- if self.stock_control?
46
- self.stock -= purchased
47
- self.save!
48
- end
43
+ # Return the total number of items currently in stock
44
+ def stock
45
+ @stock ||= self.stock_level_adjustments.sum(:adjustment)
49
46
  end
50
47
 
51
48
  # Specify which attributes can be searched
@@ -0,0 +1,18 @@
1
+ module Shoppe
2
+ class StockLevelAdjustment < ActiveRecord::Base
3
+
4
+ # Relationships
5
+ belongs_to :product
6
+ belongs_to :parent, :polymorphic => true
7
+
8
+ # Validations
9
+ validates :product_id, :presence => true
10
+ validates :description, :presence => true
11
+ validates :adjustment, :numericality => true
12
+ validate { errors.add(:adjustment, "must be greater or less than zero") if adjustment == 0 }
13
+
14
+ # Scopes
15
+ scope :ordered, -> { order('created_at desc') }
16
+
17
+ end
18
+ end
@@ -95,17 +95,15 @@
95
95
 
96
96
  = field_set_tag "Stock Control" do
97
97
  .splitContainer
98
- %dl.third
98
+ %dl.half
99
+ %dt= f.label :weight
100
+ %dd= f.text_field :weight
101
+
102
+ %dl.half
99
103
  %dt= f.label :stock_control
100
104
  %dd.checkbox
101
105
  = f.check_box :stock_control
102
106
  = f.label :stock_control, "Enable stock control for this product?"
103
- %dl.third
104
- %dt= f.label :stock, "Current stock"
105
- %dd= f.text_field :stock
106
- %dl.third
107
- %dt= f.label :weight
108
- %dd= f.text_field :weight
109
107
 
110
108
  %p.submit
111
109
  - unless @product.new_record?
@@ -1,5 +1,7 @@
1
1
  - @page_title = "Products"
2
2
  = content_for :header do
3
- %p.buttons= link_to "Back to products list", :products, :class => 'button'
3
+ %p.buttons
4
+ = link_to "Stock levels", [:stock_levels, @product], :class => 'button'
5
+ = link_to "Back to products list", :products, :class => 'button'
4
6
  %h2.products Products
5
7
  = render 'form'
@@ -8,10 +8,10 @@
8
8
  %table.data
9
9
  %thead
10
10
  %tr
11
- %th SKU
12
- %th Name
13
- %th Price
14
- %th Stock
11
+ %th{:width => '20%'} SKU
12
+ %th{:width => '50%'} Name
13
+ %th{:width => '15%'} Price
14
+ %th{:width => '15%'} Stock
15
15
  %tbody
16
16
  - if @products.empty?
17
17
  %tr.empty
@@ -25,4 +25,6 @@
25
25
  %td= product.sku
26
26
  %td= link_to product.title, [:edit, product]
27
27
  %td= number_to_currency product.price
28
- %td= product.stock
28
+ %td
29
+ %span.float-right= link_to "Edit", stock_levels_product_path(product), :class => 'edit'
30
+ = product.stock_control? ? boolean_tag(product.in_stock?, nil, :true_text => product.stock, :false_text => 'No stock') : ''
@@ -0,0 +1,33 @@
1
+ - @page_title = "Stock Levels - #{@product.title}"
2
+ = content_for :header do
3
+ %p.buttons
4
+ = link_to "Edit", [:edit, @product], :class => 'button'
5
+ = link_to "Back to list", :products, :class => 'button'
6
+ %h2.products= @product.title
7
+
8
+ .table
9
+ %p.info
10
+ Current stock level is <b>#{@product.stock}</b>
11
+ %span.float-right= page_entries_info @stock_level_adjustments
12
+
13
+ = form_for @new_sla, :url => [:stock_levels, @product] do |f|
14
+ %table.data
15
+ %thead
16
+ %tr
17
+ %th{:width => '25%'} Date
18
+ %th{:width => '50%'} Description
19
+ %th{:width => '25%'} Adjustment
20
+ %tbody
21
+ %tr.form
22
+ %td
23
+ %td.desc= f.text_field :description
24
+ %td.adjustment
25
+ = f.text_field :adjustment
26
+ = f.submit "Add", :class => 'button button-mini green'
27
+ - for sla in @stock_level_adjustments
28
+ %tr
29
+ %td= sla.created_at.to_s(:long)
30
+ %td= sla.description
31
+ %td= sla.adjustment > 0 ? "+#{sla.adjustment}" : sla.adjustment
32
+
33
+ = paginate @stock_level_adjustments
data/config/routes.rb CHANGED
@@ -2,7 +2,9 @@ Shoppe::Engine.routes.draw do
2
2
 
3
3
  get 'attachment/:id/:filename.:extension' => 'attachments#show'
4
4
  resources :product_categories
5
- resources :products
5
+ resources :products do
6
+ match :stock_levels, :on => :member, :via => [:get, :post]
7
+ end
6
8
  resources :orders do
7
9
  post :search, :on => :collection
8
10
  post :accept, :on => :member
@@ -0,0 +1,18 @@
1
+ class CreateShoppeStockLevelAdjustments < ActiveRecord::Migration
2
+ def up
3
+ create_table :shoppe_stock_level_adjustments do |t|
4
+ t.integer :product_id
5
+ t.string :description
6
+ t.integer :adjustment, :default => 0
7
+ t.string :parent_type
8
+ t.integer :parent_id
9
+ t.timestamps
10
+ end
11
+ remove_column :shoppe_products, :stock
12
+ end
13
+
14
+ def down
15
+ drop_table :shoppe_stock_level_adjustments
16
+ add_column :shoppe_products, :stock, :integer, :default => 0, :before => :weight
17
+ end
18
+ end
data/db/seeds.rb CHANGED
@@ -42,9 +42,10 @@ end
42
42
 
43
43
  lorem = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
44
44
 
45
- pro = cat1.products.create!(:title => 'Yealink T20P', :sku => 'YL-SIP-T20P', :description => lorem, :short_description => 'If cheap & cheerful is what you’re after, the Yealink T20P is what you’re looking for.', :weight => 1.119, :price => 54.99, :cost_price => 44.99, :tax_rate => 20.0, :stock => 20, :featured => true)
45
+ pro = cat1.products.create!(:title => 'Yealink T20P', :sku => 'YL-SIP-T20P', :description => lorem, :short_description => 'If cheap & cheerful is what you’re after, the Yealink T20P is what you’re looking for.', :weight => 1.119, :price => 54.99, :cost_price => 44.99, :tax_rate => 20.0, :featured => true)
46
46
  pro.default_image_file = get_file('t20p.jpg')
47
47
  pro.save!
48
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 17)
48
49
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Yealink', :position => 1)
49
50
  pro.product_attributes.create!(:key => 'Model', :value => 'T20P', :position => 1)
50
51
  pro.product_attributes.create!(:key => 'Colour', :value => 'Black', :position => 1)
@@ -52,9 +53,10 @@ pro.product_attributes.create!(:key => 'Lines', :value => '3', :position => 1)
52
53
  pro.product_attributes.create!(:key => 'Colour Screen?', :value => 'No', :position => 1)
53
54
  pro.product_attributes.create!(:key => 'Power over ethernet?', :value => 'Yes', :position => 1)
54
55
 
55
- pro = cat1.products.create!(:title => 'Yealink T22P', :sku => 'YL-SIP-T22P', :description => lorem, :short_description => lorem, :weight => 1.419, :price => 64.99, :cost_price => 56.99, :tax_rate => 20.0, :stock => 12)
56
+ pro = cat1.products.create!(:title => 'Yealink T22P', :sku => 'YL-SIP-T22P', :description => lorem, :short_description => lorem, :weight => 1.419, :price => 64.99, :cost_price => 56.99, :tax_rate => 20.0)
56
57
  pro.default_image_file = get_file('t22p.jpg')
57
58
  pro.save!
59
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 200)
58
60
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Yealink', :position => 1)
59
61
  pro.product_attributes.create!(:key => 'Model', :value => 'T22P', :position => 1)
60
62
  pro.product_attributes.create!(:key => 'Colour', :value => 'Black', :position => 1)
@@ -63,9 +65,10 @@ pro.product_attributes.create!(:key => 'Colour Screen?', :value => 'No', :positi
63
65
  pro.product_attributes.create!(:key => 'Power over ethernet?', :value => 'Yes', :position => 1)
64
66
 
65
67
 
66
- pro = cat1.products.create!(:title => 'Yealink T26P', :sku => 'YL-SIP-T26P', :description => lorem, :short_description => lorem, :weight => 2.23, :price => 88.99, :cost_price => 78.99, :tax_rate => 20.0, :stock => 5)
68
+ pro = cat1.products.create!(:title => 'Yealink T26P', :sku => 'YL-SIP-T26P', :description => lorem, :short_description => lorem, :weight => 2.23, :price => 88.99, :cost_price => 78.99, :tax_rate => 20.0)
67
69
  pro.default_image_file = get_file('t26p.jpg')
68
70
  pro.save!
71
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 100)
69
72
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Yealink', :position => 1)
70
73
  pro.product_attributes.create!(:key => 'Model', :value => 'T26P', :position => 1)
71
74
  pro.product_attributes.create!(:key => 'Colour', :value => 'Black', :position => 1)
@@ -73,9 +76,10 @@ pro.product_attributes.create!(:key => 'Lines', :value => '6', :position => 1)
73
76
  pro.product_attributes.create!(:key => 'Colour Screen?', :value => 'No', :position => 1)
74
77
  pro.product_attributes.create!(:key => 'Power over ethernet?', :value => 'Yes', :position => 1)
75
78
 
76
- pro = cat1.products.create!(:title => 'Yealink T46GN', :sku => 'YL-SIP-T46GN', :description => lorem, :short_description => 'Colourful, sharp, fast & down right sexy. The Yealink T46P will make your scream!', :weight => 2.23, :price => 149.99, :cost_price => 139.99, :tax_rate => 20.0, :stock => 5, :featured => true)
79
+ pro = cat1.products.create!(:title => 'Yealink T46GN', :sku => 'YL-SIP-T46GN', :description => lorem, :short_description => 'Colourful, sharp, fast & down right sexy. The Yealink T46P will make your scream!', :weight => 2.23, :price => 149.99, :cost_price => 139.99, :tax_rate => 20.0, :featured => true)
77
80
  pro.default_image_file = get_file('t46gn.jpg')
78
81
  pro.save!
82
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 10)
79
83
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Yealink', :position => 1)
80
84
  pro.product_attributes.create!(:key => 'Model', :value => 'T46GN', :position => 1)
81
85
  pro.product_attributes.create!(:key => 'Colour', :value => 'Black', :position => 1)
@@ -83,9 +87,10 @@ pro.product_attributes.create!(:key => 'Lines', :value => '4', :position => 1)
83
87
  pro.product_attributes.create!(:key => 'Colour Screen?', :value => 'Yes', :position => 1)
84
88
  pro.product_attributes.create!(:key => 'Power over ethernet?', :value => 'Yes', :position => 1)
85
89
 
86
- pro = cat1.products.create!(:title => 'Snom 870 (Grey)', :sku => 'SM-870-GREY', :description => lorem, :short_description => 'The perfect & beautiful VoIP phone for the discerning professional desk.', :weight => 2.4, :price => 235.00, :cost_price => 225.00, :tax_rate => 20.0, :stock => 2)
90
+ pro = cat1.products.create!(:title => 'Snom 870 (Grey)', :sku => 'SM-870-GREY', :description => lorem, :short_description => 'The perfect & beautiful VoIP phone for the discerning professional desk.', :weight => 2.4, :price => 235.00, :cost_price => 225.00, :tax_rate => 20.0)
87
91
  pro.default_image_file = get_file('snom-870-grey.jpg')
88
92
  pro.save!
93
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 4)
89
94
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Snom', :position => 1)
90
95
  pro.product_attributes.create!(:key => 'Model', :value => '870', :position => 1)
91
96
  pro.product_attributes.create!(:key => 'Colour', :value => 'Grey', :position => 1)
@@ -93,9 +98,10 @@ pro.product_attributes.create!(:key => 'Lines', :value => '10', :position => 1)
93
98
  pro.product_attributes.create!(:key => 'Colour Screen?', :value => 'Yes', :position => 1)
94
99
  pro.product_attributes.create!(:key => 'Power over ethernet?', :value => 'Yes', :position => 1)
95
100
 
96
- pro = cat1.products.create!(:title => 'Snom 870 (Black)', :sku => 'SM-870-BLK', :description => lorem, :short_description => 'The perfect & beautiful VoIP phone for the discerning professional desk.', :weight => 2.4, :price => 235.00, :cost_price => 225.00, :tax_rate => 20.0, :stock => 0, :featured => true)
101
+ pro = cat1.products.create!(:title => 'Snom 870 (Black)', :sku => 'SM-870-BLK', :description => lorem, :short_description => 'The perfect & beautiful VoIP phone for the discerning professional desk.', :weight => 2.4, :price => 235.00, :cost_price => 225.00, :tax_rate => 20.0, :featured => true)
97
102
  pro.default_image_file = get_file('snom-870-blk.jpg')
98
103
  pro.save!
104
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 4)
99
105
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Snom', :position => 1)
100
106
  pro.product_attributes.create!(:key => 'Model', :value => '870', :position => 1)
101
107
  pro.product_attributes.create!(:key => 'Colour', :value => 'Black', :position => 1)
@@ -103,27 +109,30 @@ pro.product_attributes.create!(:key => 'Lines', :value => '10', :position => 1)
103
109
  pro.product_attributes.create!(:key => 'Colour Screen?', :value => 'Yes', :position => 1)
104
110
  pro.product_attributes.create!(:key => 'Power over ethernet?', :value => 'Yes', :position => 1)
105
111
 
106
- pro = cat2.products.create!(:title => 'Yealink Mono Headset', :sku => 'YL-YHS32', :description => lorem, :short_description => 'If you\'re often on the phone, this headset will make your life 100x easier. Guaranteed*.', :weight => 0.890, :price => 34.99, :cost_price => 24.99, :tax_rate => 20.0, :stock => 3, :featured => true)
112
+ pro = cat2.products.create!(:title => 'Yealink Mono Headset', :sku => 'YL-YHS32', :description => lorem, :short_description => 'If you\'re often on the phone, this headset will make your life 100x easier. Guaranteed*.', :weight => 0.890, :price => 34.99, :cost_price => 24.99, :tax_rate => 20.0, :featured => true)
107
113
  pro.default_image_file = get_file('yhs32.jpg')
108
114
  pro.save!
109
115
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Yealink', :position => 1)
110
116
  pro.product_attributes.create!(:key => 'Model', :value => 'YHS32', :position => 1)
111
117
 
112
- pro = cat2.products.create!(:title => 'Snom Wired Headset (MM2)', :sku => 'SM-MM2', :description => lorem, :short_description => lorem, :weight => 0.780, :price => 38.00, :cost_price => 30, :tax_rate => 20.0, :stock => 0)
118
+ pro = cat2.products.create!(:title => 'Snom Wired Headset (MM2)', :sku => 'SM-MM2', :description => lorem, :short_description => lorem, :weight => 0.780, :price => 38.00, :cost_price => 30, :tax_rate => 20.0)
113
119
  pro.default_image_file = get_file('snom-mm2.jpg')
114
120
  pro.save!
121
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 7)
115
122
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Snom', :position => 1)
116
123
  pro.product_attributes.create!(:key => 'Model', :value => 'MM2', :position => 1)
117
124
 
118
- pro = cat2.products.create!(:title => 'Snom Wired Headset (MM3)', :sku => 'SM-MM3', :description => lorem, :short_description => lorem, :weight => 0.780, :price => 38.00, :cost_price => 30, :tax_rate => 20.0, :stock => 1)
125
+ pro = cat2.products.create!(:title => 'Snom Wired Headset (MM3)', :sku => 'SM-MM3', :description => lorem, :short_description => lorem, :weight => 0.780, :price => 38.00, :cost_price => 30, :tax_rate => 20.0)
119
126
  pro.default_image_file = get_file('snom-mm2.jpg')
120
127
  pro.save!
128
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 5)
121
129
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Snom', :position => 1)
122
130
  pro.product_attributes.create!(:key => 'Model', :value => 'MM3', :position => 1)
123
131
 
124
- pro = cat1.products.create!(:title => 'Yealink W52P', :sku => 'TL-SIP-W52P', :description => lorem, :short_description => 'Wireless SIP phones are hard to come by but this beauty from Yealink is fab.', :weight => 1.280, :price => 99.99, :cost_price => 89.99, :tax_rate => 20.0, :stock => 1, :featured => true)
132
+ pro = cat1.products.create!(:title => 'Yealink W52P', :sku => 'TL-SIP-W52P', :description => lorem, :short_description => 'Wireless SIP phones are hard to come by but this beauty from Yealink is fab.', :weight => 1.280, :price => 99.99, :cost_price => 89.99, :tax_rate => 20.0, :featured => true)
125
133
  pro.default_image_file = get_file('w52p.jpg')
126
134
  pro.save!
135
+ pro.stock_level_adjustments.create(:description => 'Initial Stock', :adjustment => 10)
127
136
  pro.product_attributes.create!(:key => 'Manufacturer', :value => 'Snom', :position => 1)
128
137
  pro.product_attributes.create!(:key => 'Model', :value => 'W52P', :position => 1)
129
138
  pro.product_attributes.create!(:key => 'Lines', :value => '3', :position => 1)
@@ -1,3 +1,3 @@
1
1
  module Shoppe
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
Binary file
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20131013131658) do
14
+ ActiveRecord::Schema.define(version: 20131017144430) do
15
15
 
16
16
  create_table "shoppe_delivery_service_prices", force: true do |t|
17
17
  t.integer "delivery_service_id"
@@ -114,7 +114,6 @@ ActiveRecord::Schema.define(version: 20131013131658) do
114
114
  t.decimal "weight", precision: 8, scale: 3, default: 0.0
115
115
  t.decimal "price", precision: 8, scale: 2, default: 0.0
116
116
  t.decimal "tax_rate", precision: 8, scale: 2, default: 0.0
117
- t.integer "stock", default: 0
118
117
  t.datetime "created_at"
119
118
  t.datetime "updated_at"
120
119
  t.boolean "featured", default: false
@@ -123,6 +122,16 @@ ActiveRecord::Schema.define(version: 20131013131658) do
123
122
  t.boolean "stock_control", default: true
124
123
  end
125
124
 
125
+ create_table "shoppe_stock_level_adjustments", force: true do |t|
126
+ t.integer "product_id"
127
+ t.string "description"
128
+ t.integer "adjustment", default: 0
129
+ t.string "parent_type"
130
+ t.integer "parent_id"
131
+ t.datetime "created_at"
132
+ t.datetime "updated_at"
133
+ end
134
+
126
135
  create_table "shoppe_users", force: true do |t|
127
136
  t.string "first_name"
128
137
  t.string "last_name"
@@ -920,3 +920,92 @@ Migrating to AddStockControlBooleanToProducts (20131013131658)
920
920
  SQL (1.3ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20131013131658"]]
921
921
   (2.7ms) commit transaction
922
922
  ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
923
+ ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
924
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
925
+  (0.1ms) begin transaction
926
+  (0.5ms) CREATE TABLE "shoppe_stock_level_adjustments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_id" integer, "description" varchar(255), "adjustment" integer DEFAULT 0, "parent_type" varchar(255), "parent_id" integer, "created_at" datetime, "updated_at" datetime) 
927
+ SQL (1.6ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20131017144430"]]
928
+  (0.9ms) commit transaction
929
+ ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations"
930
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
931
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
932
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
933
+  (0.1ms) begin transaction
934
+  (0.5ms) DROP TABLE "shoppe_stock_level_adjustments"
935
+ SQL (2.0ms) DELETE FROM "schema_migrations" WHERE "schema_migrations"."version" = '20131017144430'
936
+  (0.7ms) commit transaction
937
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
938
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
939
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
940
+  (0.1ms) begin transaction
941
+  (0.5ms) CREATE TABLE "shoppe_stock_level_adjustments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_id" integer, "description" varchar(255), "adjustment" integer DEFAULT 0, "parent_type" varchar(255), "parent_id" integer, "created_at" datetime, "updated_at" datetime) 
942
+  (1.6ms) rollback transaction
943
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
944
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
945
+  (0.1ms) begin transaction
946
+  (0.5ms) CREATE TEMPORARY TABLE "ashoppe_products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_category_id" integer, "title" varchar(255), "sku" varchar(255), "permalink" varchar(255), "description" text, "short_description" text, "active" boolean DEFAULT 't', "weight" decimal(8,3) DEFAULT 0.0, "price" decimal(8,2) DEFAULT 0.0, "tax_rate" decimal(8,2) DEFAULT 0.0, "stock" integer DEFAULT 0, "created_at" datetime, "updated_at" datetime, "featured" boolean DEFAULT 'f', "in_the_box" text, "cost_price" decimal(8,2), "stock_control" boolean DEFAULT 't') 
947
+  (0.1ms) SELECT * FROM "shoppe_products"
948
+  (0.2ms) DROP TABLE "shoppe_products"
949
+  (0.2ms) CREATE TABLE "shoppe_products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_category_id" integer, "title" varchar(255), "sku" varchar(255), "permalink" varchar(255), "description" text, "short_description" text, "active" boolean DEFAULT 't', "weight" decimal(8,3) DEFAULT 0.0, "price" decimal(8,2) DEFAULT 0.0, "tax_rate" decimal(8,2) DEFAULT 0.0, "created_at" datetime, "updated_at" datetime, "featured" boolean DEFAULT 'f', "in_the_box" text, "cost_price" decimal(8,2), "stock_control" boolean DEFAULT 't')
950
+  (0.1ms) SELECT * FROM "ashoppe_products"
951
+  (0.2ms) DROP TABLE "ashoppe_products"
952
+ SQL (22.4ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20131017144430"]]
953
+  (2.5ms) commit transaction
954
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
955
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
956
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
957
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
958
+  (0.1ms) begin transaction
959
+  (0.2ms) DROP TABLE "shoppe_stock_level_adjustments"
960
+ SQLite3::SQLException: no such table: shoppe_stock_level_adjustments: DROP TABLE "shoppe_stock_level_adjustments"
961
+  (0.0ms) rollback transaction
962
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
963
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
964
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
965
+  (0.1ms) begin transaction
966
+  (0.2ms) DROP TABLE "shoppe_stock_level_adjustments"
967
+ SQLite3::SQLException: no such table: shoppe_stock_level_adjustments: DROP TABLE "shoppe_stock_level_adjustments"
968
+  (0.0ms) rollback transaction
969
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
970
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
971
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
972
+  (0.0ms) begin transaction
973
+  (0.5ms) ALTER TABLE "shoppe_products" ADD "stock" integer DEFAULT 0
974
+ SQL (1.3ms) DELETE FROM "schema_migrations" WHERE "schema_migrations"."version" = '20131017144430'
975
+  (2.5ms) commit transaction
976
+ ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations"
977
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
978
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
979
+  (0.1ms) begin transaction
980
+  (0.5ms) CREATE TABLE "shoppe_stock_level_adjustments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_id" integer, "description" varchar(255), "adjustment" integer DEFAULT 0, "parent_type" varchar(255), "parent_id" integer, "created_at" datetime, "updated_at" datetime) 
981
+  (0.4ms) CREATE TEMPORARY TABLE "ashoppe_products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_category_id" integer, "title" varchar(255), "sku" varchar(255), "permalink" varchar(255), "description" text, "short_description" text, "active" boolean DEFAULT 't', "weight" decimal(8,3) DEFAULT 0.0, "price" decimal(8,2) DEFAULT 0.0, "tax_rate" decimal(8,2) DEFAULT 0.0, "created_at" datetime, "updated_at" datetime, "featured" boolean DEFAULT 'f', "in_the_box" text, "cost_price" decimal(8,2), "stock_control" boolean DEFAULT 't', "stock" integer DEFAULT 0)
982
+  (0.1ms) SELECT * FROM "shoppe_products"
983
+  (0.3ms) DROP TABLE "shoppe_products"
984
+  (0.1ms) CREATE TABLE "shoppe_products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_category_id" integer, "title" varchar(255), "sku" varchar(255), "permalink" varchar(255), "description" text, "short_description" text, "active" boolean DEFAULT 't', "weight" decimal(8,3) DEFAULT 0.0, "price" decimal(8,2) DEFAULT 0.0, "tax_rate" decimal(8,2) DEFAULT 0.0, "created_at" datetime, "updated_at" datetime, "featured" boolean DEFAULT 'f', "in_the_box" text, "cost_price" decimal(8,2), "stock_control" boolean DEFAULT 't') 
985
+  (0.1ms) SELECT * FROM "ashoppe_products"
986
+  (0.2ms) DROP TABLE "ashoppe_products"
987
+ SQL (1.5ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20131017144430"]]
988
+  (2.9ms) commit transaction
989
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
990
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
991
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
992
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
993
+  (0.1ms) begin transaction
994
+  (0.3ms) DROP TABLE "shoppe_stock_level_adjustments"
995
+  (0.2ms) ALTER TABLE "shoppe_products" ADD "stock" integer DEFAULT 0
996
+ SQL (1.3ms) DELETE FROM "schema_migrations" WHERE "schema_migrations"."version" = '20131017144430'
997
+  (2.2ms) commit transaction
998
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
999
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
1000
+ Migrating to CreateShoppeStockLevelAdjustments (20131017144430)
1001
+  (0.1ms) begin transaction
1002
+  (0.7ms) CREATE TABLE "shoppe_stock_level_adjustments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_id" integer, "description" varchar(255), "adjustment" integer DEFAULT 0, "parent_type" varchar(255), "parent_id" integer, "created_at" datetime, "updated_at" datetime) 
1003
+  (0.4ms) CREATE TEMPORARY TABLE "ashoppe_products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_category_id" integer, "title" varchar(255), "sku" varchar(255), "permalink" varchar(255), "description" text, "short_description" text, "active" boolean DEFAULT 't', "weight" decimal(8,3) DEFAULT 0.0, "price" decimal(8,2) DEFAULT 0.0, "tax_rate" decimal(8,2) DEFAULT 0.0, "created_at" datetime, "updated_at" datetime, "featured" boolean DEFAULT 'f', "in_the_box" text, "cost_price" decimal(8,2), "stock_control" boolean DEFAULT 't', "stock" integer DEFAULT 0)
1004
+  (0.1ms) SELECT * FROM "shoppe_products"
1005
+  (0.3ms) DROP TABLE "shoppe_products"
1006
+  (0.1ms) CREATE TABLE "shoppe_products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_category_id" integer, "title" varchar(255), "sku" varchar(255), "permalink" varchar(255), "description" text, "short_description" text, "active" boolean DEFAULT 't', "weight" decimal(8,3) DEFAULT 0.0, "price" decimal(8,2) DEFAULT 0.0, "tax_rate" decimal(8,2) DEFAULT 0.0, "created_at" datetime, "updated_at" datetime, "featured" boolean DEFAULT 'f', "in_the_box" text, "cost_price" decimal(8,2), "stock_control" boolean DEFAULT 't') 
1007
+  (0.1ms) SELECT * FROM "ashoppe_products"
1008
+  (0.2ms) DROP TABLE "ashoppe_products"
1009
+ SQL (2.3ms) INSERT INTO "schema_migrations" ("version") VALUES (?) [["version", "20131017144430"]]
1010
+  (2.8ms) commit transaction
1011
+ ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoppe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-16 00:00:00.000000000 Z
12
+ date: 2013-10-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -258,6 +258,7 @@ files:
258
258
  - app/assets/images/shoppe/icons/cone.svg
259
259
  - app/assets/images/shoppe/icons/credit_card.svg
260
260
  - app/assets/images/shoppe/icons/currency.svg
261
+ - app/assets/images/shoppe/icons/edit.svg
261
262
  - app/assets/images/shoppe/icons/flowchart.svg
262
263
  - app/assets/images/shoppe/icons/gift.svg
263
264
  - app/assets/images/shoppe/icons/globe.svg
@@ -311,6 +312,7 @@ files:
311
312
  - app/models/shoppe/product.rb
312
313
  - app/models/shoppe/product_attribute.rb
313
314
  - app/models/shoppe/product_category.rb
315
+ - app/models/shoppe/stock_level_adjustment.rb
314
316
  - app/models/shoppe/user.rb
315
317
  - app/views/layouts/shoppe/application.html.haml
316
318
  - app/views/layouts/shoppe/sub.html.haml
@@ -336,6 +338,7 @@ files:
336
338
  - app/views/shoppe/products/edit.html.haml
337
339
  - app/views/shoppe/products/index.html.haml
338
340
  - app/views/shoppe/products/new.html.haml
341
+ - app/views/shoppe/products/stock_levels.html.haml
339
342
  - app/views/shoppe/sessions/new.html.haml
340
343
  - app/views/shoppe/sessions/reset.html.haml
341
344
  - app/views/shoppe/user_mailer/new_password.text.erb
@@ -350,6 +353,7 @@ files:
350
353
  - db/migrate/20131012163301_add_public_boolean_to_product_attributes.rb
351
354
  - db/migrate/20131013123937_add_cost_prices_to_various_objects.rb
352
355
  - db/migrate/20131013131658_add_stock_control_boolean_to_products.rb
356
+ - db/migrate/20131017144430_create_shoppe_stock_level_adjustments.rb
353
357
  - db/seeds.rb
354
358
  - db/seeds_data/poe400.jpg
355
359
  - db/seeds_data/snom-870-blk.jpg
@@ -431,7 +435,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
431
435
  version: '0'
432
436
  segments:
433
437
  - 0
434
- hash: 637768647073806952
438
+ hash: -387136076029433472
435
439
  required_rubygems_version: !ruby/object:Gem::Requirement
436
440
  none: false
437
441
  requirements:
@@ -440,7 +444,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
440
444
  version: '0'
441
445
  segments:
442
446
  - 0
443
- hash: 637768647073806952
447
+ hash: -387136076029433472
444
448
  requirements: []
445
449
  rubyforge_project:
446
450
  rubygems_version: 1.8.23