office_clerk 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +14 -9
  3. data/README.md +1 -0
  4. data/app/assets/images/shop/ikkuna.jpg +0 -0
  5. data/app/assets/images/shop/violetti-lev.jpg +0 -0
  6. data/app/assets/javascripts/admin.js +2 -2
  7. data/app/assets/javascripts/shop.js +2 -1
  8. data/app/assets/stylesheets/shop-receipt.css.scss +1 -1
  9. data/app/assets/stylesheets/shop.css.scss +52 -31
  10. data/app/controllers/application_controller.rb +11 -5
  11. data/app/controllers/baskets_controller.rb +3 -27
  12. data/app/controllers/categories_controller.rb +2 -1
  13. data/app/controllers/clerks_controller.rb +3 -8
  14. data/app/controllers/manage_controller.rb +7 -0
  15. data/app/controllers/orders_controller.rb +16 -2
  16. data/app/controllers/products_controller.rb +20 -40
  17. data/app/controllers/purchases_controller.rb +2 -1
  18. data/app/controllers/sessions_controller.rb +15 -22
  19. data/app/helpers/admin_helper.rb +6 -8
  20. data/app/helpers/orders_helper.rb +6 -0
  21. data/app/models/basket.rb +1 -2
  22. data/app/models/category.rb +2 -0
  23. data/app/models/product.rb +43 -17
  24. data/app/models/purchase.rb +7 -6
  25. data/app/views/baskets/edit.html.haml +6 -8
  26. data/app/views/baskets/show.html.haml +1 -1
  27. data/app/views/clerks/edit.html.haml +1 -1
  28. data/app/views/layouts/_admin_menu.html.haml +2 -0
  29. data/app/views/layouts/admin.html.haml +9 -4
  30. data/app/views/layouts/shop.html.haml +8 -5
  31. data/app/views/manage/all.haml +3 -0
  32. data/app/views/orders/ship.haml +91 -0
  33. data/app/views/orders/show.html.haml +12 -4
  34. data/app/views/products/_head.haml +12 -0
  35. data/app/views/products/_line.html.haml +4 -8
  36. data/app/views/products/_online.html.haml +1 -1
  37. data/app/views/products/edit.html.haml +1 -7
  38. data/app/views/products/index.html.haml +4 -1
  39. data/app/views/products/show.html.haml +4 -18
  40. data/app/views/purchases/show.html.haml +3 -3
  41. data/app/views/sessions/{new.html.haml → sign_in.haml} +1 -1
  42. data/app/views/sessions/{new_clerk.html.haml → sign_up.haml} +2 -2
  43. data/app/views/shop/checkout.haml +3 -3
  44. data/app/views/shop/liikkeemme.html.haml +59 -0
  45. data/app/views/shop/order.haml +20 -15
  46. data/app/views/shop/product_list.html.haml +16 -13
  47. data/app/views/shop/tilaushistoria.html.haml +221 -0
  48. data/app/views/shop/toimitusehdot.html.haml +99 -0
  49. data/config/locales/config.yml +2 -1
  50. data/config/locales/en.yml +4 -5
  51. data/config/locales/fi.yml +13 -6
  52. data/config/routes.rb +11 -12
  53. data/lib/office_clerk/shipping_method.rb +1 -1
  54. data/office_clerk.gemspec +1 -1
  55. data/spec/controllers/products_controller_spec.rb +3 -3
  56. data/spec/controllers/sessions_controller_spec.rb +11 -3
  57. data/spec/factories/orders.rb +7 -1
  58. data/spec/factories/products.rb +16 -0
  59. data/spec/factories/purchases.rb +8 -4
  60. data/spec/features/baskets/buttons_spec.rb +1 -2
  61. data/spec/features/baskets/index_spec.rb +10 -4
  62. data/spec/features/clerks_spec.rb +22 -6
  63. data/spec/features/orders_spec.rb +18 -0
  64. data/spec/features/products/edit_spec.rb +32 -0
  65. data/spec/features/products/header_spec.rb +48 -0
  66. data/spec/features/products/index_spec.rb +4 -18
  67. data/spec/features/purchases_spec.rb +19 -0
  68. data/spec/features/sessions_spec.rb +58 -0
  69. data/spec/features/shop_spec.rb +63 -0
  70. data/spec/features/suppliers_spec.rb +2 -0
  71. data/spec/models/product_spec.rb +54 -8
  72. data/spec/models/purchase_spec.rb +20 -1
  73. data/spec/models/shipping_spec.rb +25 -0
  74. metadata +22 -9
  75. data/app/views/products/_name.html.haml +0 -4
  76. data/spec/features/products/new_spec.rb +0 -20
  77. 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.new_line_item
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 += t(:create_success, :model => "product")
55
- redirect_to product_path(@product)
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
- flash.notice = ""
63
- if ok = @product.update_attributes(params_for_model)
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 products_url , :notice => t("error")
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
- pdf = Prawn::Document.new( :page_size => [ 54.mm , 25.mm ] , :margin => 2.mm )
101
- pdf.text( @product.full_name , :align => :left )
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
- redirect_to purchase_path(@purchase), :notice => [t(:inventorized) ,items ,t(:items)].join(" ")
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 new
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
- render "new" , :notice => I18n.t(:sign_in_invalid)
14
+ redirect_to :sign_in , :notice => I18n.t(:sign_in_invalid)
15
15
  end
16
16
  end
17
17
 
18
- def destroy
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 new_clerk
24
- @clerk = Clerk.new
25
- end
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
- render :action => 'edit'
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
- def create_clerk
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
@@ -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.kori_type
10
- when "Order"
9
+ case basket.kori
10
+ when Order
11
11
  text += I18n.t(:order)
12
12
  link = order_path(basket.kori) rescue ""
13
- when "Purchase"
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] || ""
@@ -1,4 +1,10 @@
1
1
  # encoding : utf-8
2
2
  require "admin_helper"
3
3
  module OrdersHelper
4
+ def print_styles
5
+ OfficeClerk.config(:print_styles).split
6
+ end
7
+ def print_path style
8
+ eval("#{style}_order_path(@order)")
9
+ end
4
10
  end
data/app/models/basket.rb CHANGED
@@ -80,8 +80,7 @@ class Basket < ActiveRecord::Base
80
80
  end
81
81
 
82
82
  def isa typ
83
- return typ == :cart if self.kori_type == nil
84
- self.kori_type.downcase == typ.to_s.downcase
83
+ self.kori_type.to_s.downcase == typ.to_s.downcase && self.kori_id != nil
85
84
  end
86
85
 
87
86
  def suppliers
@@ -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
@@ -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 :generate_url
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 line_items don't have urls, so not for them
51
- def generate_url
52
- if line_item? or deleted?
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
- #this product represents a product line (ie is not sellable in itself)
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
- !line_item? and !products.empty?
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 line_item?
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 new_line_item
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
@@ -9,18 +9,19 @@ class Purchase < ActiveRecord::Base
9
9
 
10
10
  def receive!
11
11
  items = basket.receive!
12
- self.ordered_on = Date.today unless ordered_on
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.receive!
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
- .col-md-2
55
- = link_to t(:back), baskets_path, :class => "btn btn-warning"
56
- .col-md-2
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.kori_type
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), invoice_order_path(@basket.kori) , :target => "_blank" , :class => "btn btn-primary print_order" if @basket.isa(:order)
18
+ = link_to t(:print_order), receipt_order_path(@basket.kori) , :target => "_blank" , :class => "btn btn-primary print_order" if @basket.isa(:order)
@@ -16,5 +16,5 @@
16
16
  = f.input :password
17
17
  = f.input :password_confirmation
18
18
  .form-actions
19
- = f.submit :class => "btn btn-success"
19
+ = f.submit :class => "btn btn-success" , :id => "submit"
20
20
  = link_to t(:back), clerks_path, :class => "btn btn-warning"
@@ -12,3 +12,5 @@
12
12
  = link_to t(:suppliers), suppliers_path
13
13
  %li{:class => params[:controller] == "clerks" && "active" }
14
14
  = link_to t(:clerks), clerks_path
15
+ %li{:class => params[:controller] == "manage" && "active" }
16
+ = link_to t(:manage), manage_all_path
@@ -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
- .row.content
24
- - if not flash[:notice].blank? then
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
- - if not flash[:error].blank? then
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!= flash[:notice]
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