office_clerk 0.0.1 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +14 -9
- data/README.md +1 -0
- data/app/assets/images/shop/ikkuna.jpg +0 -0
- data/app/assets/images/shop/violetti-lev.jpg +0 -0
- data/app/assets/javascripts/admin.js +2 -2
- data/app/assets/javascripts/shop.js +2 -1
- data/app/assets/stylesheets/shop-receipt.css.scss +1 -1
- data/app/assets/stylesheets/shop.css.scss +52 -31
- data/app/controllers/application_controller.rb +11 -5
- data/app/controllers/baskets_controller.rb +3 -27
- data/app/controllers/categories_controller.rb +2 -1
- data/app/controllers/clerks_controller.rb +3 -8
- data/app/controllers/manage_controller.rb +7 -0
- data/app/controllers/orders_controller.rb +16 -2
- data/app/controllers/products_controller.rb +20 -40
- data/app/controllers/purchases_controller.rb +2 -1
- data/app/controllers/sessions_controller.rb +15 -22
- data/app/helpers/admin_helper.rb +6 -8
- data/app/helpers/orders_helper.rb +6 -0
- data/app/models/basket.rb +1 -2
- data/app/models/category.rb +2 -0
- data/app/models/product.rb +43 -17
- data/app/models/purchase.rb +7 -6
- data/app/views/baskets/edit.html.haml +6 -8
- data/app/views/baskets/show.html.haml +1 -1
- data/app/views/clerks/edit.html.haml +1 -1
- data/app/views/layouts/_admin_menu.html.haml +2 -0
- data/app/views/layouts/admin.html.haml +9 -4
- data/app/views/layouts/shop.html.haml +8 -5
- data/app/views/manage/all.haml +3 -0
- data/app/views/orders/ship.haml +91 -0
- data/app/views/orders/show.html.haml +12 -4
- data/app/views/products/_head.haml +12 -0
- data/app/views/products/_line.html.haml +4 -8
- data/app/views/products/_online.html.haml +1 -1
- data/app/views/products/edit.html.haml +1 -7
- data/app/views/products/index.html.haml +4 -1
- data/app/views/products/show.html.haml +4 -18
- data/app/views/purchases/show.html.haml +3 -3
- data/app/views/sessions/{new.html.haml → sign_in.haml} +1 -1
- data/app/views/sessions/{new_clerk.html.haml → sign_up.haml} +2 -2
- data/app/views/shop/checkout.haml +3 -3
- data/app/views/shop/liikkeemme.html.haml +59 -0
- data/app/views/shop/order.haml +20 -15
- data/app/views/shop/product_list.html.haml +16 -13
- data/app/views/shop/tilaushistoria.html.haml +221 -0
- data/app/views/shop/toimitusehdot.html.haml +99 -0
- data/config/locales/config.yml +2 -1
- data/config/locales/en.yml +4 -5
- data/config/locales/fi.yml +13 -6
- data/config/routes.rb +11 -12
- data/lib/office_clerk/shipping_method.rb +1 -1
- data/office_clerk.gemspec +1 -1
- data/spec/controllers/products_controller_spec.rb +3 -3
- data/spec/controllers/sessions_controller_spec.rb +11 -3
- data/spec/factories/orders.rb +7 -1
- data/spec/factories/products.rb +16 -0
- data/spec/factories/purchases.rb +8 -4
- data/spec/features/baskets/buttons_spec.rb +1 -2
- data/spec/features/baskets/index_spec.rb +10 -4
- data/spec/features/clerks_spec.rb +22 -6
- data/spec/features/orders_spec.rb +18 -0
- data/spec/features/products/edit_spec.rb +32 -0
- data/spec/features/products/header_spec.rb +48 -0
- data/spec/features/products/index_spec.rb +4 -18
- data/spec/features/purchases_spec.rb +19 -0
- data/spec/features/sessions_spec.rb +58 -0
- data/spec/features/shop_spec.rb +63 -0
- data/spec/features/suppliers_spec.rb +2 -0
- data/spec/models/product_spec.rb +54 -8
- data/spec/models/purchase_spec.rb +20 -1
- data/spec/models/shipping_spec.rb +25 -0
- metadata +22 -9
- data/app/views/products/_name.html.haml +0 -4
- data/spec/features/products/new_spec.rb +0 -20
- data/spec/features/shops_spec.rb +0 -18
@@ -27,7 +27,7 @@ class ProductsController < AdminController
|
|
27
27
|
def new
|
28
28
|
if params[:parent_id]
|
29
29
|
parent = Product.find params[:parent_id]
|
30
|
-
@product = parent.
|
30
|
+
@product = parent.new_product_item
|
31
31
|
else
|
32
32
|
@product = Product.new :tax => OfficeClerk.config("defaults.tax")
|
33
33
|
end
|
@@ -38,47 +38,19 @@ class ProductsController < AdminController
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def create
|
41
|
-
flash.notice = ""
|
42
41
|
@product = Product.create(params_for_model)
|
43
|
-
#TODO maybe there is a better way, but this "validation" happens "after the fact", ie by adding
|
44
|
-
# an item to a parent the parent can become "invalid" even it is not what is being edited. hmmm
|
45
|
-
if @product.line_item? and not @product.product.ean.blank?
|
46
|
-
flash.notice += t(:product_line_has_ean)
|
47
|
-
flash.notice += "<br/>"
|
48
|
-
end
|
49
|
-
if @product.line_item? and not @product.product.scode.blank?
|
50
|
-
flash.notice += t(:product_line_has_scode)
|
51
|
-
flash.notice += "<br/>"
|
52
|
-
end
|
53
42
|
if @product.save
|
54
|
-
flash.notice
|
55
|
-
|
43
|
+
flash.notice = t(:create_success, :model => "product")
|
44
|
+
show = @product.product_item? ? @product.product : @product
|
45
|
+
redirect_to product_path(show)
|
56
46
|
else
|
57
47
|
render :action => :edit
|
58
48
|
end
|
59
49
|
end
|
60
50
|
|
61
51
|
def update
|
62
|
-
|
63
|
-
|
64
|
-
flash.notice += t(:update_success, :model => "product")
|
65
|
-
flash.notice += "<br/>"
|
66
|
-
end
|
67
|
-
if (@product.line_item? and not @product.link.blank?)
|
68
|
-
flash.notice += t(:product_item_has_link)
|
69
|
-
flash.notice += "<br/>"
|
70
|
-
ok = false
|
71
|
-
end
|
72
|
-
if (@product.line_item? and @product.product.ean) or (@product.line? and not @product.ean.blank?)
|
73
|
-
flash.notice += t(:product_line_has_ean)
|
74
|
-
flash.notice += "<br/>"
|
75
|
-
ok = false
|
76
|
-
end
|
77
|
-
if (@product.line_item? and @product.product.scode) or (@product.line? and not @product.scode.blank?)
|
78
|
-
flash.notice += t(:product_line_has_scode)
|
79
|
-
ok = false
|
80
|
-
end
|
81
|
-
if ok
|
52
|
+
if @product.update_attributes(params_for_model)
|
53
|
+
flash.notice = t(:update_success, :model => "product")
|
82
54
|
redirect_to product_path(@product)
|
83
55
|
else
|
84
56
|
render :action => :edit
|
@@ -90,29 +62,37 @@ class ProductsController < AdminController
|
|
90
62
|
if @product.save
|
91
63
|
redirect_to products_url , :notice => t("deleted")
|
92
64
|
else
|
93
|
-
redirect_to
|
65
|
+
redirect_to product_url , :notice => "#{t(:error)} : #{t(:inventory)}"
|
94
66
|
end
|
95
67
|
end
|
96
68
|
|
97
69
|
# loads of ways to create barcodes nowadays, this is a bit older.
|
98
70
|
# Used to be html but moved to pdf for better layout control
|
99
71
|
def barcode
|
100
|
-
|
101
|
-
|
102
|
-
pdf.text( "#{@product.price} € " , :align => :right , :padding => 5.mm)
|
103
|
-
code = @product.ean.blank? || ""
|
72
|
+
code = @product.ean
|
73
|
+
code = @product.name if code.blank?
|
104
74
|
if code.length == 12
|
105
75
|
aBarcode = ::Barby::EAN13.new( code )
|
106
76
|
else
|
77
|
+
puts "product #{code}"
|
78
|
+
puts code.class
|
107
79
|
aBarcode = ::Barby::Code128B.new( code )
|
108
80
|
end
|
81
|
+
pdf = create_pdf
|
109
82
|
pdf.image( StringIO.new( aBarcode.to_png(:xdim => 5)) , :width => 50.mm ,
|
110
83
|
:height => 10.mm , :at => [ 0 , 10.mm])
|
111
84
|
send_data pdf.render , :type => "application/pdf" , :filename => "#{@product.full_name}.pdf"
|
112
85
|
end
|
113
86
|
|
114
87
|
private
|
115
|
-
|
88
|
+
|
89
|
+
def create_pdf
|
90
|
+
pdf = Prawn::Document.new( :page_size => [ 54.mm , 25.mm ] , :margin => 2.mm )
|
91
|
+
pdf.text( @product.full_name , :align => :left )
|
92
|
+
pdf.text( "#{@product.price} € " , :align => :right , :padding => 5.mm)
|
93
|
+
pdf
|
94
|
+
end
|
95
|
+
|
116
96
|
def load_product
|
117
97
|
@product = Product.find(params[:id])
|
118
98
|
end
|
@@ -30,7 +30,8 @@ class PurchasesController < AdminController
|
|
30
30
|
# receive the stuff (ie add to inventory)
|
31
31
|
def inventory
|
32
32
|
items = @purchase.inventory!
|
33
|
-
|
33
|
+
flash.notice = [t(:inventorized) ,items ,t(:items)].join(" ")
|
34
|
+
redirect_to purchase_path(@purchase)
|
34
35
|
end
|
35
36
|
|
36
37
|
def new
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class SessionsController < ApplicationController
|
2
2
|
layout "shop"
|
3
3
|
|
4
|
-
def
|
4
|
+
def sign_in
|
5
5
|
end
|
6
6
|
|
7
7
|
def create
|
@@ -11,36 +11,29 @@ class SessionsController < ApplicationController
|
|
11
11
|
url = clerk.admin ? baskets_url : root_url
|
12
12
|
redirect_to url , :notice => I18n.t(:signed_in)
|
13
13
|
else
|
14
|
-
|
14
|
+
redirect_to :sign_in , :notice => I18n.t(:sign_in_invalid)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def sign_out
|
19
19
|
session[:clerk_email] = nil
|
20
20
|
redirect_to root_url, :notice => I18n.t(:signed_out)
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def update_clerk
|
28
|
-
@clerk = current_clerk
|
29
|
-
if @clerk.update_attributes(params[:clerk])
|
30
|
-
redirect_to root_url, :notice => I18n.t(:updated)
|
23
|
+
def sign_up
|
24
|
+
if request.get?
|
25
|
+
@clerk = Clerk.new
|
31
26
|
else
|
32
|
-
|
27
|
+
@clerk = Clerk.new(params_for_clerk)
|
28
|
+
if @clerk.save
|
29
|
+
session[:clerk_email] = @clerk.email
|
30
|
+
redirect_to root_url, :notice => "Signed up!"
|
31
|
+
return
|
32
|
+
end
|
33
33
|
end
|
34
34
|
end
|
35
|
-
|
36
|
-
|
37
|
-
@clerk = Clerk.new(params[:clerk])
|
38
|
-
if @clerk.save
|
39
|
-
session[:clerk_email] = @clerk.email
|
40
|
-
redirect_to root_url, :notice => "Signed up!"
|
41
|
-
else
|
42
|
-
render "new"
|
43
|
-
end
|
35
|
+
def params_for_clerk
|
36
|
+
params.require(:clerk).permit(:email,:password,:password_confirmation)
|
44
37
|
end
|
45
|
-
|
38
|
+
|
46
39
|
end
|
data/app/helpers/admin_helper.rb
CHANGED
@@ -4,23 +4,21 @@ module AdminHelper
|
|
4
4
|
def basket_edit_link basket , options = {}
|
5
5
|
return "---" unless basket
|
6
6
|
return "" unless request.url.include?("basket")
|
7
|
-
text = t(:edit) + " "
|
7
|
+
text = t(:edit) + " "
|
8
8
|
link = edit_basket_path(basket)
|
9
|
-
case basket.
|
10
|
-
when
|
9
|
+
case basket.kori
|
10
|
+
when Order
|
11
11
|
text += I18n.t(:order)
|
12
12
|
link = order_path(basket.kori) rescue ""
|
13
|
-
when
|
13
|
+
when Purchase
|
14
14
|
text += I18n.t(:purchase)
|
15
15
|
link = purchase_path(basket.kori) rescue ""
|
16
|
+
else
|
17
|
+
text += t(:basket)
|
16
18
|
end
|
17
19
|
return link_to text , link , options
|
18
20
|
end
|
19
21
|
|
20
|
-
def sorting_header(model_name, attribute_name, namespace)
|
21
|
-
attribute_name
|
22
|
-
end
|
23
|
-
|
24
22
|
def sort_date key
|
25
23
|
return "" unless params[:q]
|
26
24
|
params[:q][key] || ""
|
data/app/models/basket.rb
CHANGED
data/app/models/category.rb
CHANGED
@@ -7,6 +7,8 @@ class Category < ActiveRecord::Base
|
|
7
7
|
belongs_to :category
|
8
8
|
has_attached_file :main_picture
|
9
9
|
has_attached_file :extra_picture
|
10
|
+
validates_attachment_content_type :main_picture, :content_type => /\Aimage\/.*\Z/
|
11
|
+
validates_attachment_content_type :extra_picture, :content_type => /\Aimage\/.*\Z/
|
10
12
|
|
11
13
|
validates :name, :presence => true
|
12
14
|
validates :link, presence: true, :if => :generate_url_if_needed
|
data/app/models/product.rb
CHANGED
@@ -21,6 +21,8 @@ class Product < ActiveRecord::Base
|
|
21
21
|
belongs_to :supplier
|
22
22
|
has_attached_file :main_picture
|
23
23
|
has_attached_file :extra_picture
|
24
|
+
validates_attachment_content_type :main_picture, :content_type => /\Aimage\/.*\Z/
|
25
|
+
validates_attachment_content_type :extra_picture, :content_type => /\Aimage\/.*\Z/
|
24
26
|
|
25
27
|
# default product scope only lists non-deleted products
|
26
28
|
default_scope {where(:deleted_on => nil).order('created_at DESC') }
|
@@ -32,10 +34,12 @@ class Product < ActiveRecord::Base
|
|
32
34
|
validates :price, :numericality => true
|
33
35
|
validates :cost, :numericality => true
|
34
36
|
validates :name, :presence => true
|
37
|
+
validates :deleted_on , :absence => true , :if => "inventory > 0"
|
35
38
|
|
36
|
-
before_save :
|
39
|
+
before_save :check_attributes
|
37
40
|
before_save :adjust_cost
|
38
41
|
after_save :update_line_inventory , :if => :product_id
|
42
|
+
after_save :check_parent_ean , :if => :product_id
|
39
43
|
|
40
44
|
|
41
45
|
def update_line_inventory
|
@@ -46,13 +50,24 @@ class Product < ActiveRecord::Base
|
|
46
50
|
parent.save! if inv != parent.inventory
|
47
51
|
end
|
48
52
|
|
53
|
+
def check_parent_ean
|
54
|
+
parent = self.product
|
55
|
+
return unless parent
|
56
|
+
parent.check_attributes
|
57
|
+
parent.save! if parent.changed?
|
58
|
+
end
|
59
|
+
|
49
60
|
# if no url is set we generate one based on the name
|
50
|
-
# but
|
51
|
-
def
|
52
|
-
if
|
61
|
+
# but product_items don't have urls, so not for them
|
62
|
+
def check_attributes
|
63
|
+
if product_item?
|
53
64
|
self.link = ""
|
54
65
|
else
|
55
|
-
self.link = name.gsub(" " , "_").downcase if link.blank? && name != nil
|
66
|
+
self.link = self.name.gsub(" " , "_").downcase if self.link.blank? && self.name != nil
|
67
|
+
end
|
68
|
+
if line?
|
69
|
+
self.ean = "" unless self.ean.blank?
|
70
|
+
self.scode = "" unless self.scode.blank?
|
56
71
|
end
|
57
72
|
end
|
58
73
|
|
@@ -61,6 +76,7 @@ class Product < ActiveRecord::Base
|
|
61
76
|
self.cost = self.price / 2
|
62
77
|
end
|
63
78
|
end
|
79
|
+
|
64
80
|
def deleted?
|
65
81
|
not deleted_on.blank?
|
66
82
|
end
|
@@ -72,28 +88,38 @@ class Product < ActiveRecord::Base
|
|
72
88
|
self
|
73
89
|
end
|
74
90
|
|
75
|
-
#
|
91
|
+
# the type is one of:
|
92
|
+
# - product
|
93
|
+
# -product_line
|
94
|
+
# -product_item
|
95
|
+
# mostly used for translation. Function below let you test for each of the possibilities
|
96
|
+
def type
|
97
|
+
return :product_item if product_item?
|
98
|
+
products.empty? ? :product : :product_line
|
99
|
+
end
|
100
|
+
|
101
|
+
# this product represents a product line (ie is not sellable in itself)
|
76
102
|
def line?
|
77
|
-
!
|
103
|
+
!product_item? and !products.empty?
|
104
|
+
end
|
105
|
+
# this product is an item of a product line (so is sellable)
|
106
|
+
def product_item?
|
107
|
+
self.product_id != nil
|
108
|
+
end
|
109
|
+
# only products and product items are sellable. in other words if it's not a line
|
110
|
+
def sellable?
|
111
|
+
!line?
|
78
112
|
end
|
79
113
|
|
80
114
|
def full_name
|
81
|
-
if
|
115
|
+
if product_item?
|
82
116
|
product.name + " : " + self.name
|
83
117
|
else
|
84
118
|
self.name
|
85
119
|
end
|
86
120
|
end
|
87
|
-
#this product is an item of a product line (so is sellable)
|
88
|
-
def line_item?
|
89
|
-
self.product_id != nil
|
90
|
-
end
|
91
|
-
|
92
|
-
def sellable?
|
93
|
-
!line?
|
94
|
-
end
|
95
121
|
|
96
|
-
def
|
122
|
+
def new_product_item
|
97
123
|
Product.new :tax => self.tax , :weight => self.weight , :cost => self.cost , :product_id => self.id ,
|
98
124
|
:supplier_id => self.supplier_id , :category_id => self.category_id , :price => self.price
|
99
125
|
end
|
data/app/models/purchase.rb
CHANGED
@@ -9,18 +9,19 @@ class Purchase < ActiveRecord::Base
|
|
9
9
|
|
10
10
|
def receive!
|
11
11
|
items = basket.receive!
|
12
|
-
|
13
|
-
self.received_on = Date.today
|
14
|
-
self.save!
|
15
|
-
items
|
12
|
+
return stamp_today items
|
16
13
|
end
|
17
14
|
|
18
15
|
def inventory!
|
19
|
-
items = basket.
|
16
|
+
items = basket.inventory!
|
17
|
+
return stamp_today items
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def stamp_today items
|
20
22
|
self.ordered_on = Date.today unless ordered_on
|
21
23
|
self.received_on = Date.today
|
22
24
|
self.save!
|
23
25
|
items
|
24
26
|
end
|
25
|
-
|
26
27
|
end
|
@@ -51,13 +51,10 @@
|
|
51
51
|
.row.form-actions
|
52
52
|
.col-md-2
|
53
53
|
= f.submit :class => "btn btn-success commit"
|
54
|
-
.
|
55
|
-
|
56
|
-
|
57
|
-
= link_to t(:new) + ' ' + t(:basket) , new_basket_path , :class => "btn btn-primary"
|
58
|
-
- if @basket.isa(:cart) and not @basket.empty?
|
54
|
+
- if @basket.kori_type.blank? and not @basket.empty?
|
55
|
+
.col-md-1
|
56
|
+
= link_to t(:make_order), order_basket_path(@basket), :class => "btn btn-primary make_order"
|
59
57
|
.col-md-2
|
60
|
-
- link_to t(:make_order), order_basket_path(@basket), :class => "btn btn-primary make_order"
|
61
58
|
= link_to t(:make_purchase), purchase_basket_path(@basket), :class => "btn btn-primary make_purchase"
|
62
59
|
.col-md-2{"data-no-turbolink" => true}
|
63
60
|
= link_to t(:checkout), checkout_basket_path(@basket) , :target => "_blank" , :class => "btn btn-primary print_order"
|
@@ -69,6 +66,7 @@
|
|
69
66
|
- elsif @basket.isa(:purchase)
|
70
67
|
.col-md-2
|
71
68
|
= link_to t(:to_purchase), purchase_path(@basket.kori), :class => "btn btn-primary to_purchase"
|
72
|
-
|
73
69
|
.col-md-2
|
74
|
-
= link_to t(:destroy) , basket_path(@basket), :data => { :confirm => t(:are_you_sure )}, :method => :delete, :title => t(:destroy) , :class => "btn btn-danger" unless @basket.
|
70
|
+
= link_to t(:destroy) , basket_path(@basket), :data => { :confirm => t(:are_you_sure )}, :method => :delete, :title => t(:destroy) , :class => "btn btn-danger" unless @basket.kori
|
71
|
+
.col-md-1
|
72
|
+
= link_to t(:new) + ' ' + t(:basket) , new_basket_path , :class => "btn btn-primary"
|
@@ -15,4 +15,4 @@
|
|
15
15
|
.col-md-12{"data-no-turbolink" => true}
|
16
16
|
= link_to t(:back), orders_path, :class => "btn btn-warning"
|
17
17
|
= link_to t(:destroy) , basket_path(@basket), :data => { :confirm => t(:are_you_sure) }, :method => :delete, :title => t(:destroy) , :class => "btn btn-danger" unless @basket.kori_type
|
18
|
-
= link_to t(:print_order),
|
18
|
+
= link_to t(:print_order), receipt_order_path(@basket.kori) , :target => "_blank" , :class => "btn btn-primary print_order" if @basket.isa(:order)
|
@@ -20,23 +20,28 @@
|
|
20
20
|
%li= link_to current_clerk.email , edit_clerk_path(current_clerk)
|
21
21
|
%li= link_to t(:sign_out), sign_out_path
|
22
22
|
|
23
|
-
.
|
24
|
-
|
23
|
+
- if not flash[:notice].blank? then
|
24
|
+
.row.content
|
25
25
|
.col-md-12.alert
|
26
26
|
.alert-notice.row
|
27
27
|
.col-md-3
|
28
28
|
.alert-heading= t(:info) + " :"
|
29
29
|
.col-md-9
|
30
30
|
.alert-message!= flash[:notice]
|
31
|
-
|
31
|
+
- if not flash[:alert].blank? then
|
32
|
+
.row.content
|
32
33
|
.col-md-12.alert
|
33
34
|
.alert-error.row
|
34
35
|
.col-md-3
|
35
36
|
.alert-heading= t(:info) + " :"
|
36
37
|
.col-md-9
|
37
|
-
.alert-message
|
38
|
+
.alert-message= flash[:alert]
|
38
39
|
.large-12.column
|
39
40
|
.row.space
|
40
41
|
=image_tag "shop/spacer.gif"
|
41
42
|
|
42
43
|
= yield
|
44
|
+
|
45
|
+
.row
|
46
|
+
.col-md-12
|
47
|
+
%br
|