office_clerk 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -2
  3. data/Gemfile.lock +159 -100
  4. data/Guardfile +3 -0
  5. data/README.md +7 -2
  6. data/Rakefile +9 -0
  7. data/app/assets/images/missing_list.png +0 -0
  8. data/app/assets/images/missing_product.png +0 -0
  9. data/app/assets/images/missing_thumb.png +0 -0
  10. data/app/assets/javascripts/office_clerk.js +7 -5
  11. data/app/assets/stylesheets/office_clerk.css.scss +89 -14
  12. data/app/controllers/baskets_controller.rb +37 -26
  13. data/app/controllers/orders_controller.rb +44 -29
  14. data/app/controllers/products_controller.rb +6 -6
  15. data/app/controllers/purchases_controller.rb +18 -8
  16. data/app/helpers/admin_helper.rb +16 -21
  17. data/app/helpers/baskets_helper.rb +31 -0
  18. data/app/helpers/categories_helper.rb +3 -0
  19. data/app/helpers/office_helper.rb +34 -7
  20. data/app/helpers/orders_helper.rb +2 -3
  21. data/app/models/basket.rb +46 -20
  22. data/app/models/clerk.rb +6 -1
  23. data/app/models/item.rb +7 -0
  24. data/app/models/order.rb +18 -6
  25. data/app/models/product.rb +23 -8
  26. data/app/views/addresses/show.html.haml +2 -2
  27. data/app/views/baskets/_small.html.haml +2 -2
  28. data/app/views/baskets/edit.html.haml +19 -16
  29. data/app/views/baskets/index.html.haml +13 -5
  30. data/app/views/baskets/show.html.haml +3 -3
  31. data/app/views/categories/_treeview.html.haml +1 -2
  32. data/app/views/categories/edit.html.haml +4 -5
  33. data/app/views/categories/index.html.haml +7 -7
  34. data/app/views/categories/show.html.haml +3 -4
  35. data/app/views/clerks/edit.html.haml +4 -3
  36. data/app/views/clerks/index.html.haml +6 -6
  37. data/app/views/clerks/show.html.haml +3 -3
  38. data/app/views/layouts/_admin_menu.html.haml +8 -8
  39. data/app/views/layouts/_messages.html.haml +2 -2
  40. data/app/views/layouts/office_clerk.haml +10 -27
  41. data/app/views/order_mailer/confirm.text.erb +1 -2
  42. data/app/views/order_mailer/shipped.text.erb +2 -1
  43. data/app/views/orders/index.csv.erb +6 -6
  44. data/app/views/orders/index.html.haml +25 -20
  45. data/app/views/orders/shipment.haml +57 -0
  46. data/app/views/orders/show.html.haml +106 -39
  47. data/app/views/products/_head.haml +1 -2
  48. data/app/views/products/_line.html.haml +1 -1
  49. data/app/views/products/_preview_box.haml +14 -10
  50. data/app/views/products/_triple.html.haml +1 -1
  51. data/app/views/products/edit.html.haml +4 -4
  52. data/app/views/products/index.html.haml +24 -13
  53. data/app/views/products/show.html.haml +15 -12
  54. data/app/views/purchases/index.html.haml +11 -7
  55. data/app/views/purchases/show.html.haml +42 -25
  56. data/app/views/sessions/sign_up.haml +10 -10
  57. data/app/views/suppliers/edit.html.haml +2 -2
  58. data/app/views/suppliers/index.html.haml +6 -7
  59. data/app/views/suppliers/show.html.haml +2 -3
  60. data/config/i18n-tasks.yml +97 -0
  61. data/config/initializers/migrate.rb +11 -1
  62. data/config/initializers/paperclip.rb +17 -0
  63. data/config/locales/config.yml +0 -1
  64. data/config/locales/en.yml +99 -116
  65. data/config/locales/fi.yml +101 -117
  66. data/config/routes.rb +18 -15
  67. data/db/migrate/20131226143612_categories.rb +2 -2
  68. data/db/migrate/20131226151332_products.rb +3 -3
  69. data/lib/office_clerk/engine.rb +5 -8
  70. data/lib/office_clerk/shipping_method.rb +12 -1
  71. data/lib/office_clerk/version.rb +1 -1
  72. data/office_clerk.gemspec +5 -5
  73. data/spec/controllers/baskets_controller_spec.rb +2 -2
  74. data/spec/controllers/orders_controller_spec.rb +0 -29
  75. data/spec/controllers/products_controller_spec.rb +4 -3
  76. data/spec/controllers/purchases_controller_spec.rb +3 -4
  77. data/spec/factories/orders.rb +6 -1
  78. data/spec/factories/products.rb +3 -0
  79. data/spec/features/baskets/edit_spec.rb +6 -1
  80. data/spec/features/baskets/index_spec.rb +1 -0
  81. data/spec/features/baskets/search_spec.rb +1 -1
  82. data/spec/features/orders/filter_spec.rb +19 -0
  83. data/spec/features/orders/inventory_spec.rb +19 -0
  84. data/spec/features/orders/ordering_spec.rb +84 -0
  85. data/spec/features/paginate_spec.rb +11 -0
  86. data/spec/features/sessions_spec.rb +2 -1
  87. data/spec/{models/locale_spec.rb → i18n_spec.rb} +19 -1
  88. data/spec/mailers/order_mailer_spec.rb +2 -1
  89. data/spec/models/baskets/inventory_spec.rb +9 -2
  90. data/spec/models/baskets/items_spec.rb +6 -1
  91. data/spec/models/order_spec.rb +20 -1
  92. data/spec/routing/baskets_routing_spec.rb +5 -1
  93. data/spec/routing/orders_routing_spec.rb +0 -12
  94. data/spec/spec_helper.rb +16 -3
  95. data/test_app/bin/setup +29 -0
  96. data/test_app/config/application.rb +3 -0
  97. data/test_app/config/boot.rb +1 -2
  98. data/test_app/config/environment.rb +1 -1
  99. data/test_app/config/environments/development.rb +1 -1
  100. data/test_app/config/environments/production.rb +20 -21
  101. data/test_app/config/environments/test.rb +7 -4
  102. data/test_app/config/initializers/assets.rb +11 -0
  103. data/test_app/config/initializers/cookies_serializer.rb +3 -0
  104. data/test_app/config/initializers/mime_types.rb +0 -1
  105. data/test_app/config/initializers/session_store.rb +1 -1
  106. data/test_app/config/routes.rb +1 -2
  107. data/test_app/config/secrets.yml +22 -0
  108. data/test_app/db/migrate/20141114205526_suppliers.office.rb +1 -1
  109. data/test_app/db/migrate/20141114205527_categories.office.rb +3 -3
  110. data/test_app/db/migrate/20141114205528_items.office.rb +1 -1
  111. data/test_app/db/migrate/20141114205529_orders.office.rb +1 -1
  112. data/test_app/db/migrate/20141114205530_baskets.office.rb +1 -1
  113. data/test_app/db/migrate/20141114205531_purchases.office.rb +1 -1
  114. data/test_app/db/migrate/20141114205532_products.office.rb +4 -4
  115. data/test_app/db/schema.rb +26 -26
  116. metadata +35 -28
  117. data/app/views/orders/edit.html.haml +0 -25
  118. data/app/views/orders/ship.haml +0 -91
  119. data/app/views/purchases/edit.html.haml +0 -9
  120. data/spec/features/orders_spec.rb +0 -37
@@ -2,7 +2,7 @@
2
2
 
3
3
  class ProductsController < AdminController
4
4
 
5
- before_filter :load_product, :only => [:show, :edit, :update, :delete ]
5
+ before_filter :load_product, :only => [:show, :edit, :update, :destroy ]
6
6
 
7
7
  # Uncomment for check abilities with CanCan
8
8
  #authorize_resource
@@ -39,6 +39,7 @@ class ProductsController < AdminController
39
39
  show = @product.product_item? ? @product.product : @product
40
40
  redirect_to product_path(show)
41
41
  else
42
+ flash.alert = t(:fix_errors, :model => "product")
42
43
  render :action => :edit
43
44
  end
44
45
  end
@@ -55,12 +56,12 @@ class ProductsController < AdminController
55
56
  end
56
57
  end
57
58
 
58
- def delete
59
+ def destroy
59
60
  @product.delete
60
61
  if @product.save
61
- redirect_to products_url , :notice => t("deleted")
62
+ redirect_to products_url , :notice => t("deleted") + ": " + @product.full_name
62
63
  else
63
- redirect_to product_url , :notice => "#{t(:error)} : #{t(:inventory)}"
64
+ redirect_to product_url(@product) , :notice => "#{t(:error)} : #{t(:product_has_inventory)}"
64
65
  end
65
66
  end
66
67
 
@@ -69,9 +70,8 @@ class ProductsController < AdminController
69
70
  end
70
71
 
71
72
  def params_for_model
72
- params.require(:product).permit(:price,:cost,:weight,:name,:description, :online, :summary,
73
+ params.require(:product).permit(:price,:cost,:weight,:name,:description, :online, :summary,:stock_level,
73
74
  :link,:ean,:tax,:properties,:scode,:product_id,:category_id,:supplier_id, :main_picture,:extra_picture
74
75
  )
75
76
  end
76
77
  end
77
-
@@ -13,6 +13,7 @@ class PurchasesController < AdminController
13
13
  end
14
14
 
15
15
  def show
16
+ gon.purchase_id = @purchase.id
16
17
  end
17
18
 
18
19
  # order this from supplier
@@ -35,8 +36,11 @@ class PurchasesController < AdminController
35
36
  end
36
37
 
37
38
  def new
38
- @purchase = Purchase.new
39
- render :edit
39
+ today = Date.today
40
+ basket = Basket.create!
41
+ @purchase = Purchase.new :name => "#{I18n.t(:purchase)} #{I18n.l(today)}" , :basket => basket
42
+ @purchase.save!
43
+ redirect_to edit_basket_path basket
40
44
  end
41
45
 
42
46
  def edit
@@ -48,24 +52,30 @@ class PurchasesController < AdminController
48
52
  if @purchase.save
49
53
  redirect_to purchase_path(@purchase), :notice => t(:create_success, :model => "purchase")
50
54
  else
51
- render :edit
55
+ render :show
52
56
  end
53
57
  end
54
58
 
55
59
  def update
56
- if @purchase.update_attributes(params_for_model)
57
- redirect_to purchase_path(@purchase), :notice => t(:update_success, :model => "purchase")
58
- else
59
- render :action => :edit
60
+ respond_to do |format|
61
+ if @purchase.update_attributes(params_for_model)
62
+ format.html { redirect_to purchase_path(@purchase), :notice => t(:update_success, :model => "purchase") }
63
+ format.json { respond_with_bip(@purchase) }
64
+ else
65
+ format.html { render :action => :show }
66
+ format.json { respond_with_bip(@purchase) }
67
+ end
60
68
  end
61
69
  end
62
70
 
63
- private
71
+ protected
64
72
 
65
73
  def load_purchase
66
74
  @purchase = Purchase.where( :id => params[:id]).includes( :basket => {:items => {:product => :supplier}} ).first
67
75
  end
68
76
 
77
+ private
78
+
69
79
  def params_for_model
70
80
  params.require(:purchase).permit(:name,:ordered_on,:received_on,:basket_id)
71
81
  end
@@ -1,24 +1,6 @@
1
1
  # encoding : utf-8
2
2
  module AdminHelper
3
-
4
- def basket_edit_link basket , options = {}
5
- return "---" unless basket
6
- return "" unless request.url.include?("basket")
7
- text = t(:edit) + " "
8
- link = edit_basket_path(basket)
9
- case basket.kori
10
- when Order
11
- text += I18n.t(:order)
12
- link = order_path(basket.kori) rescue ""
13
- when Purchase
14
- text += I18n.t(:purchase)
15
- link = purchase_path(basket.kori) rescue ""
16
- else
17
- text += t(:basket)
18
- end
19
- return link_to text , link , options
20
- end
21
-
3
+
22
4
  def sort_date key
23
5
  return "" unless params[:q]
24
6
  params[:q][key] || ""
@@ -30,10 +12,23 @@ module AdminHelper
30
12
  # a default note
31
13
  # same signature as best_in_place, ie object, field symbol , hash
32
14
  def in_place object , field , attributes ={}
33
- defaults = { :ok_button => I18n.t(:edit), :ok_button_class => "btn btn-success" ,
15
+ defaults = { :ok_button => I18n.t(:edit), :ok_button_class => "btn btn-success" ,
34
16
  :cancel_button => I18n.t(:cancel) , :cancel_button_class => "btn btn-warning",
35
17
  :place_holder => I18n.t(:edit) , :inner_class => "form-control" }
36
- attributes.reverse_merge! defaults
18
+ attributes.reverse_merge! defaults
37
19
  best_in_place(object , field , attributes)
38
20
  end
21
+
22
+ def office_assets
23
+ engines = Rails::Engine.subclasses.map(&:instance)
24
+ engines << Rails.application
25
+ engines.delete_if {|e| ! e.respond_to?(:office_assets) }
26
+ assets = engines.collect{ |e| e.office_assets }
27
+ assets.compact
28
+ end
29
+
30
+ # helper so one doesn't hav to write the funny content_for in templates
31
+ def title(page_title)
32
+ content_for(:title) { page_title }
33
+ end
39
34
  end
@@ -1,4 +1,35 @@
1
1
  # encoding : utf-8
2
2
  require "admin_helper"
3
3
  module BasketsHelper
4
+ def has_receipt?
5
+ styles = OfficeClerk.config(:print_styles)
6
+ return false if styles.nil?
7
+ return styles.split.include?("receipt")
8
+ end
9
+ def basket_edit_link basket , options = {}
10
+ return "---" unless basket
11
+ return "" unless request.url.include?("basket")
12
+ if basket.locked?
13
+ text = I18n.t(:locked) + ": "
14
+ case basket.kori
15
+ when Order
16
+ text += I18n.t(:order)
17
+ link = office.order_path(basket.kori)
18
+ when Purchase
19
+ text += I18n.t(:purchase)
20
+ link = office.purchase_path(basket.kori)
21
+ else
22
+ raise "System Error: Locked basket without order #{basket.id}"
23
+ end
24
+ else
25
+ if basket.kori
26
+ key = basket.kori.class.name.downcase
27
+ text = I18n.t(key)
28
+ else
29
+ text = t(:basket)
30
+ end
31
+ link = office.edit_basket_path(basket)
32
+ end
33
+ return link_to text , link , options
34
+ end
4
35
  end
@@ -11,5 +11,8 @@ module CategoriesHelper
11
11
  end
12
12
  parents.reverse
13
13
  end
14
+ def group_path group
15
+ category_path(group)
16
+ end
14
17
 
15
18
  end
@@ -1,17 +1,17 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module OfficeHelper
3
-
3
+
4
4
  # users are stored in the session by email
5
5
  # if user is not logged i , return nil
6
6
  def current_clerk
7
7
  return @current_clerk if @current_clerk
8
8
  return nil unless session[:clerk_email]
9
- @current_clerk = Clerk.where( :email => session[:clerk_email] ).limit(1).first
9
+ @current_clerk = Clerk.where( :email => session[:clerk_email] ).limit(1).first
10
10
  end
11
11
  def current_basket_or_nil
12
12
  return @current_basket unless @current_basket.nil?
13
13
  if session[:current_basket]
14
- Basket.where( :id => session[:current_basket] ).limit(1).first
14
+ Basket.where( :id => session[:current_basket] ).limit(1).first
15
15
  else
16
16
  nil
17
17
  end
@@ -29,30 +29,38 @@ module OfficeHelper
29
29
  end
30
30
  @current_basket
31
31
  end
32
-
32
+
33
33
  def has_ssl?
34
34
  return false unless Rails.env.production?
35
35
  OfficeClerk.config(:has_ssl) == true
36
36
  end
37
-
37
+
38
38
  # when the order is made and the basket locked, it's time to make a new one
39
39
  def new_basket
40
40
  session[:current_basket] = nil
41
41
  end
42
-
42
+
43
43
  def shipping_method name
44
44
  OfficeClerk::ShippingMethod.method(name)
45
45
  end
46
-
46
+
47
47
  def markdown text
48
48
  return "" if text.blank?
49
49
  return sanitize Kramdown::Document.new(text).to_html
50
50
  end
51
51
 
52
+ # euros displays the prices in ... da da .. . euros.
53
+ # This could of course be configurable, but since taxes and possibly shipping don't work in us, i wait for the pull
52
54
  def euros price
53
55
  price ? number_to_currency(price , :precision => 2 , :unit => "€") : 0.0
54
56
  end
55
57
 
58
+ # this is the helper that best in place uses to display euros.
59
+ # it is different so it can be overriden
60
+ def best_euros p
61
+ euros(p)
62
+ end
63
+
56
64
  def date d
57
65
  return "" unless d
58
66
  I18n.l d
@@ -62,7 +70,26 @@ module OfficeHelper
62
70
  def paginate(collection , options = {})
63
71
  #options = options.merge defaults
64
72
  options[:renderer] = BootstrapPagination::Rails
73
+ options[:params] = { :url_scope => :office }
65
74
  will_paginate collection, options
66
75
  end
67
76
 
68
77
  end
78
+
79
+ require "bootstrap_pagination/version"
80
+
81
+ BootstrapPagination::Rails.class_eval do
82
+ def url(page)
83
+ @base_url_params ||= begin
84
+ url_params = merge_get_params(default_url_params)
85
+ url_params[:only_path] = true
86
+ merge_optional_params(url_params)
87
+ end
88
+
89
+ url_params = @base_url_params.dup
90
+ add_current_page_param(url_params, page)
91
+
92
+ OfficeClerk::Engine.routes.url_for(url_params)
93
+ end
94
+
95
+ end
@@ -8,8 +8,7 @@ module OrdersHelper
8
8
  def mail_path action
9
9
 
10
10
  end
11
- def viite
12
- base = @order.number[1 .. -1]
13
- viite = open("http://www1.nordea.fi/P636V/H636VTXT.asp?action=tekstiLista&lkm=1&alkuViite=#{base}&L=1").read[/\d+ \d+/]
11
+ def number_with_comma n
12
+ number_with_precision(n , :precision => 2 , :separator => "," , :strip_insignificant_zeros => false)
14
13
  end
15
14
  end
data/app/models/basket.rb CHANGED
@@ -4,7 +4,8 @@ class Basket < ActiveRecord::Base
4
4
 
5
5
  belongs_to :kori, polymorphic: true #kori is basket in finnish
6
6
 
7
- has_many :items, autosave: true
7
+ has_many :items, autosave: true , :dependent => :destroy
8
+
8
9
  before_save :cache_total
9
10
 
10
11
  accepts_nested_attributes_for :items
@@ -17,11 +18,15 @@ class Basket < ActiveRecord::Base
17
18
  self.items.empty?
18
19
  end
19
20
 
21
+ def locked?
22
+ not self.locked.blank?
23
+ end
24
+
20
25
  def cache_total
21
26
  self.total_price = (items.to_a.sum{ |i| i.total }).round(2)
22
27
  self.total_tax = (items.to_a.sum{ |i| i.tax_amount}).round(2)
23
28
  end
24
-
29
+
25
30
  def touch
26
31
  cache_total
27
32
  super
@@ -39,27 +44,19 @@ class Basket < ActiveRecord::Base
39
44
 
40
45
  # receiving the goods means that the item quantity is added to the stock (product.inventory)
41
46
  # also we change the price to the products cost price
42
- # locks the basket so receiving or deductiing becomes an error.
47
+ # locks the basket so receiving or deducting becomes an error.
43
48
  def receive!
44
- raise "Locked since #{self.locked}" if self.locked
45
- sum = 0
46
- self.items.each do |item|
47
- prod = item.product
48
- prod.inventory = prod.inventory + item.quantity
49
- prod.save!
50
- sum += item.quantity
51
- item.price = item.product.cost
52
- # item.save!
53
- end
49
+ raise "Locked since #{self.locked}" if locked?
50
+ sum = do_receive(true) # change the prices
54
51
  self.locked = Date.today
55
52
  self.save!
56
53
  sum
57
54
  end
58
55
 
56
+ # deduct the items from inventory, change affects immediately in the products
59
57
  # locks the basket so receiving or deducting becomes an error.
60
- # deduct the items from inventory, change affects immediately in the products
61
58
  def deduct!
62
- raise "Locked since #{self.locked}" if self.locked
59
+ raise "Locked since #{self.locked}" if locked?
63
60
  sum = 0
64
61
  self.items.each do |item|
65
62
  prod = item.product
@@ -72,13 +69,30 @@ class Basket < ActiveRecord::Base
72
69
  sum
73
70
  end
74
71
 
75
- #inventoying the basket means setting the item quantity as the stock
76
- #we actually change the basket for it to be a relative change (so as to look like a receive)
72
+ # return inventory and cancel basket
73
+ # very similar to receive, just we don't change prices (and don't lock)
74
+ def cancel_order!
75
+ self.locked = nil
76
+ do_receive(false) #don't change prices
77
+ self.save!
78
+ end
79
+
80
+ # inventorying the basket means setting the item quantity as the stock
81
+ # we actually change the basket for it to be a relative change (so as to look like a receive)
77
82
  def inventory!
78
83
  self.items.each { |item| item.quantity -= item.product.inventory }
79
84
  self.receive!
80
85
  end
81
86
 
87
+ # set all items prices to zero
88
+ def zero_prices!
89
+ raise "Locked since #{self.locked}" if locked?
90
+ self.items.each do |item|
91
+ item.price = 0.0
92
+ end
93
+ self.save!
94
+ end
95
+
82
96
  def isa typ
83
97
  self.kori_type.to_s.downcase == typ.to_s.downcase && self.kori_id != nil
84
98
  end
@@ -89,17 +103,17 @@ class Basket < ActiveRecord::Base
89
103
  ss.delete(nil)
90
104
  ss
91
105
  end
92
-
106
+
93
107
  #when adding a product (with quantity) we ensure there is only one item for each product
94
108
  def add_product prod , quant = 1
95
109
  return unless prod
96
110
  return unless quant != 0
97
- raise "Locked since #{self.locked}" if self.locked
111
+ raise "Locked since #{self.locked}" if locked?
98
112
  exists = items.where(:product_id => prod.id ).limit(1).first
99
113
  if exists
100
114
  exists.quantity += quant
101
115
  else
102
- exists = items.new :quantity => quant , :product => prod , :price => prod.price ,
116
+ exists = items.new :quantity => quant , :product => prod , :price => prod.price ,
103
117
  :tax => prod.tax , :name => prod.full_name
104
118
  end
105
119
  if( exists.quantity == 0)
@@ -110,4 +124,16 @@ class Basket < ActiveRecord::Base
110
124
  end
111
125
  reload
112
126
  end
127
+ private
128
+ def do_receive change_prices
129
+ sum = 0
130
+ self.items.each do |item|
131
+ prod = item.product
132
+ prod.inventory = prod.inventory + item.quantity
133
+ prod.save!
134
+ sum += item.quantity
135
+ item.price = item.product.cost if change_prices
136
+ end
137
+ sum
138
+ end
113
139
  end
data/app/models/clerk.rb CHANGED
@@ -31,8 +31,13 @@ class Clerk < ActiveRecord::Base
31
31
  res == 0
32
32
  end
33
33
 
34
+ # just an ar association with order of the same email
35
+ def orders
36
+ Order.where(:email => self.email)
37
+ end
38
+
34
39
  def last_address
35
- order = Order.where(:email => self.email).first
40
+ order = orders.last
36
41
  order ? order.address : {}
37
42
  end
38
43
  private