office_clerk 0.0.1 → 0.1
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.
- 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
|