office_clerk 0.8 → 0.9

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.
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