kkt_shoppe 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (239) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +55 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/images/kkt_shoppe/chosen-sprite.png +0 -0
  6. data/app/assets/images/kkt_shoppe/chosen-sprite@2x.png +0 -0
  7. data/app/assets/images/kkt_shoppe/document.svg +1 -0
  8. data/app/assets/images/kkt_shoppe/icons/bag.svg +1 -0
  9. data/app/assets/images/kkt_shoppe/icons/balance.svg +1 -0
  10. data/app/assets/images/kkt_shoppe/icons/box.svg +1 -0
  11. data/app/assets/images/kkt_shoppe/icons/building.svg +1 -0
  12. data/app/assets/images/kkt_shoppe/icons/chart.svg +1 -0
  13. data/app/assets/images/kkt_shoppe/icons/chat.svg +1 -0
  14. data/app/assets/images/kkt_shoppe/icons/checkbox.svg +1 -0
  15. data/app/assets/images/kkt_shoppe/icons/checkbox2.svg +1 -0
  16. data/app/assets/images/kkt_shoppe/icons/cloud.svg +1 -0
  17. data/app/assets/images/kkt_shoppe/icons/cone.svg +1 -0
  18. data/app/assets/images/kkt_shoppe/icons/credit_card.svg +1 -0
  19. data/app/assets/images/kkt_shoppe/icons/currency.svg +1 -0
  20. data/app/assets/images/kkt_shoppe/icons/edit.svg +14 -0
  21. data/app/assets/images/kkt_shoppe/icons/flowchart.svg +1 -0
  22. data/app/assets/images/kkt_shoppe/icons/gift.svg +1 -0
  23. data/app/assets/images/kkt_shoppe/icons/globe.svg +1 -0
  24. data/app/assets/images/kkt_shoppe/icons/id.svg +1 -0
  25. data/app/assets/images/kkt_shoppe/icons/id2.svg +1 -0
  26. data/app/assets/images/kkt_shoppe/icons/locked.svg +1 -0
  27. data/app/assets/images/kkt_shoppe/icons/report.svg +1 -0
  28. data/app/assets/images/kkt_shoppe/icons/search.svg +1 -0
  29. data/app/assets/images/kkt_shoppe/icons/support.svg +1 -0
  30. data/app/assets/images/kkt_shoppe/icons/tags.svg +1 -0
  31. data/app/assets/images/kkt_shoppe/icons/toolbox.svg +1 -0
  32. data/app/assets/images/kkt_shoppe/icons/unlocked.svg +1 -0
  33. data/app/assets/images/kkt_shoppe/icons/wallet.svg +1 -0
  34. data/app/assets/images/kkt_shoppe/kkt_shoppe.svg +25 -0
  35. data/app/assets/images/kkt_shoppe/logo.svg +47 -0
  36. data/app/assets/images/kkt_shoppe/move.svg +1 -0
  37. data/app/assets/images/kkt_shoppe/square.svg +9 -0
  38. data/app/assets/images/kkt_shoppe/statuses/accepted.svg +14 -0
  39. data/app/assets/images/kkt_shoppe/statuses/paid.svg +16 -0
  40. data/app/assets/images/kkt_shoppe/statuses/received.svg +15 -0
  41. data/app/assets/images/kkt_shoppe/statuses/rejected.svg +14 -0
  42. data/app/assets/images/kkt_shoppe/statuses/shipped.svg +14 -0
  43. data/app/assets/images/kkt_shoppe/table-tear-off.png +0 -0
  44. data/app/assets/javascripts/kkt_shoppe/application.coffee +123 -0
  45. data/app/assets/javascripts/kkt_shoppe/chosen.jquery.js +1166 -0
  46. data/app/assets/javascripts/kkt_shoppe/jquery_ui.js +6 -0
  47. data/app/assets/javascripts/kkt_shoppe/mousetrap.js +9 -0
  48. data/app/assets/javascripts/kkt_shoppe/order_form.coffee +47 -0
  49. data/app/assets/stylesheets/kkt_shoppe/application.scss +601 -0
  50. data/app/assets/stylesheets/kkt_shoppe/chosen.scss +424 -0
  51. data/app/assets/stylesheets/kkt_shoppe/dialog.scss +25 -0
  52. data/app/assets/stylesheets/kkt_shoppe/elements.scss +79 -0
  53. data/app/assets/stylesheets/kkt_shoppe/printable.scss +67 -0
  54. data/app/assets/stylesheets/kkt_shoppe/reset.scss +93 -0
  55. data/app/assets/stylesheets/kkt_shoppe/sub.scss +106 -0
  56. data/app/assets/stylesheets/kkt_shoppe/variables.scss +1 -0
  57. data/app/controllers/kkt_shoppe/addresses_controller.rb +47 -0
  58. data/app/controllers/kkt_shoppe/application_controller.rb +46 -0
  59. data/app/controllers/kkt_shoppe/attachments_controller.rb +14 -0
  60. data/app/controllers/kkt_shoppe/countries_controller.rb +47 -0
  61. data/app/controllers/kkt_shoppe/customers_controller.rb +55 -0
  62. data/app/controllers/kkt_shoppe/dashboard_controller.rb +9 -0
  63. data/app/controllers/kkt_shoppe/delivery_service_prices_controller.rb +45 -0
  64. data/app/controllers/kkt_shoppe/delivery_services_controller.rb +47 -0
  65. data/app/controllers/kkt_shoppe/orders_controller.rb +119 -0
  66. data/app/controllers/kkt_shoppe/payments_controller.rb +33 -0
  67. data/app/controllers/kkt_shoppe/product_categories_controller.rb +47 -0
  68. data/app/controllers/kkt_shoppe/product_category_localisations_controller.rb +58 -0
  69. data/app/controllers/kkt_shoppe/product_localisations_controller.rb +58 -0
  70. data/app/controllers/kkt_shoppe/products_controller.rb +58 -0
  71. data/app/controllers/kkt_shoppe/sessions_controller.rb +34 -0
  72. data/app/controllers/kkt_shoppe/settings_controller.rb +16 -0
  73. data/app/controllers/kkt_shoppe/stock_level_adjustments_controller.rb +40 -0
  74. data/app/controllers/kkt_shoppe/tax_rates_controller.rb +49 -0
  75. data/app/controllers/kkt_shoppe/users_controller.rb +53 -0
  76. data/app/controllers/kkt_shoppe/variants_controller.rb +50 -0
  77. data/app/helpers/kkt_shoppe/application_helper.rb +60 -0
  78. data/app/helpers/kkt_shoppe/product_category_helper.rb +36 -0
  79. data/app/mailers/kkt_shoppe/order_mailer.rb +25 -0
  80. data/app/mailers/kkt_shoppe/user_mailer.rb +10 -0
  81. data/app/models/kkt_shoppe/address.rb +44 -0
  82. data/app/models/kkt_shoppe/country.rb +27 -0
  83. data/app/models/kkt_shoppe/customer.rb +41 -0
  84. data/app/models/kkt_shoppe/delivery_service.rb +33 -0
  85. data/app/models/kkt_shoppe/delivery_service_price.rb +31 -0
  86. data/app/models/kkt_shoppe/order/actions.rb +94 -0
  87. data/app/models/kkt_shoppe/order/billing.rb +105 -0
  88. data/app/models/kkt_shoppe/order/delivery.rb +229 -0
  89. data/app/models/kkt_shoppe/order/states.rb +69 -0
  90. data/app/models/kkt_shoppe/order.rb +100 -0
  91. data/app/models/kkt_shoppe/order_item.rb +239 -0
  92. data/app/models/kkt_shoppe/payment.rb +86 -0
  93. data/app/models/kkt_shoppe/product/product_attributes.rb +20 -0
  94. data/app/models/kkt_shoppe/product/variants.rb +51 -0
  95. data/app/models/kkt_shoppe/product.rb +189 -0
  96. data/app/models/kkt_shoppe/product_attribute.rb +66 -0
  97. data/app/models/kkt_shoppe/product_categorization.rb +14 -0
  98. data/app/models/kkt_shoppe/product_category.rb +75 -0
  99. data/app/models/kkt_shoppe/setting.rb +68 -0
  100. data/app/models/kkt_shoppe/stock_level_adjustment.rb +19 -0
  101. data/app/models/kkt_shoppe/tax_rate.rb +46 -0
  102. data/app/models/kkt_shoppe/user.rb +49 -0
  103. data/app/validators/permalink_validator.rb +7 -0
  104. data/app/views/kkt_shoppe/addresses/_form.html.haml +33 -0
  105. data/app/views/kkt_shoppe/addresses/edit.html.haml +5 -0
  106. data/app/views/kkt_shoppe/addresses/new.html.haml +5 -0
  107. data/app/views/kkt_shoppe/countries/_form.html.haml +35 -0
  108. data/app/views/kkt_shoppe/countries/edit.html.haml +6 -0
  109. data/app/views/kkt_shoppe/countries/index.html.haml +25 -0
  110. data/app/views/kkt_shoppe/countries/new.html.haml +7 -0
  111. data/app/views/kkt_shoppe/customers/_addresses.html.haml +20 -0
  112. data/app/views/kkt_shoppe/customers/_form.html.haml +30 -0
  113. data/app/views/kkt_shoppe/customers/_search_form.html.haml +13 -0
  114. data/app/views/kkt_shoppe/customers/edit.html.haml +5 -0
  115. data/app/views/kkt_shoppe/customers/index.html.haml +32 -0
  116. data/app/views/kkt_shoppe/customers/new.html.haml +5 -0
  117. data/app/views/kkt_shoppe/customers/show.html.haml +53 -0
  118. data/app/views/kkt_shoppe/delivery_service_prices/_form.html.haml +44 -0
  119. data/app/views/kkt_shoppe/delivery_service_prices/edit.html.haml +6 -0
  120. data/app/views/kkt_shoppe/delivery_service_prices/index.html.haml +23 -0
  121. data/app/views/kkt_shoppe/delivery_service_prices/new.html.haml +6 -0
  122. data/app/views/kkt_shoppe/delivery_services/_form.html.haml +38 -0
  123. data/app/views/kkt_shoppe/delivery_services/edit.html.haml +9 -0
  124. data/app/views/kkt_shoppe/delivery_services/index.html.haml +27 -0
  125. data/app/views/kkt_shoppe/delivery_services/new.html.haml +6 -0
  126. data/app/views/kkt_shoppe/order_mailer/accepted.text.erb +12 -0
  127. data/app/views/kkt_shoppe/order_mailer/received.text.erb +9 -0
  128. data/app/views/kkt_shoppe/order_mailer/rejected.text.erb +10 -0
  129. data/app/views/kkt_shoppe/order_mailer/shipped.text.erb +16 -0
  130. data/app/views/kkt_shoppe/orders/_form.html.haml +58 -0
  131. data/app/views/kkt_shoppe/orders/_order_details.html.haml +57 -0
  132. data/app/views/kkt_shoppe/orders/_order_items.html.haml +38 -0
  133. data/app/views/kkt_shoppe/orders/_order_items_form.html.haml +61 -0
  134. data/app/views/kkt_shoppe/orders/_payments_form.html.haml +15 -0
  135. data/app/views/kkt_shoppe/orders/_payments_table.html.haml +37 -0
  136. data/app/views/kkt_shoppe/orders/_search_form.html.haml +24 -0
  137. data/app/views/kkt_shoppe/orders/_status_bar.html.haml +35 -0
  138. data/app/views/kkt_shoppe/orders/despatch_note.html.haml +45 -0
  139. data/app/views/kkt_shoppe/orders/edit.html.haml +21 -0
  140. data/app/views/kkt_shoppe/orders/index.html.haml +39 -0
  141. data/app/views/kkt_shoppe/orders/new.html.haml +14 -0
  142. data/app/views/kkt_shoppe/orders/show.html.haml +25 -0
  143. data/app/views/kkt_shoppe/payments/refund.html.haml +13 -0
  144. data/app/views/kkt_shoppe/product_categories/_form.html.haml +50 -0
  145. data/app/views/kkt_shoppe/product_categories/edit.html.haml +8 -0
  146. data/app/views/kkt_shoppe/product_categories/index.html.haml +27 -0
  147. data/app/views/kkt_shoppe/product_categories/new.html.haml +6 -0
  148. data/app/views/kkt_shoppe/product_category_localisations/form.html.haml +29 -0
  149. data/app/views/kkt_shoppe/product_category_localisations/index.html.haml +26 -0
  150. data/app/views/kkt_shoppe/product_localisations/form.html.haml +32 -0
  151. data/app/views/kkt_shoppe/product_localisations/index.html.haml +26 -0
  152. data/app/views/kkt_shoppe/products/_form.html.haml +118 -0
  153. data/app/views/kkt_shoppe/products/_table.html.haml +42 -0
  154. data/app/views/kkt_shoppe/products/edit.html.haml +9 -0
  155. data/app/views/kkt_shoppe/products/import.html.haml +67 -0
  156. data/app/views/kkt_shoppe/products/index.html.haml +9 -0
  157. data/app/views/kkt_shoppe/products/new.html.haml +7 -0
  158. data/app/views/kkt_shoppe/sessions/new.html.haml +12 -0
  159. data/app/views/kkt_shoppe/sessions/reset.html.haml +12 -0
  160. data/app/views/kkt_shoppe/settings/edit.html.haml +19 -0
  161. data/app/views/kkt_shoppe/shared/error.html.haml +6 -0
  162. data/app/views/kkt_shoppe/stock_level_adjustments/index.html.haml +40 -0
  163. data/app/views/kkt_shoppe/tax_rates/form.html.haml +28 -0
  164. data/app/views/kkt_shoppe/tax_rates/index.html.haml +17 -0
  165. data/app/views/kkt_shoppe/user_mailer/new_password.text.erb +9 -0
  166. data/app/views/kkt_shoppe/users/_form.html.haml +27 -0
  167. data/app/views/kkt_shoppe/users/edit.html.haml +5 -0
  168. data/app/views/kkt_shoppe/users/index.html.haml +17 -0
  169. data/app/views/kkt_shoppe/users/new.html.haml +7 -0
  170. data/app/views/kkt_shoppe/variants/form.html.haml +68 -0
  171. data/app/views/kkt_shoppe/variants/index.html.haml +33 -0
  172. data/app/views/layouts/kkt_shoppe/application.html.haml +34 -0
  173. data/app/views/layouts/kkt_shoppe/printable.html.haml +11 -0
  174. data/app/views/layouts/kkt_shoppe/sub.html.haml +10 -0
  175. data/config/locales/de.yml +653 -0
  176. data/config/locales/en.yml +744 -0
  177. data/config/locales/es.yml +653 -0
  178. data/config/locales/pl.yml +662 -0
  179. data/config/locales/pt-BR.yml +655 -0
  180. data/config/routes.rb +53 -0
  181. data/db/countries.txt +252 -0
  182. data/db/migrate/20130926094549_create_kkt_shoppe_initial_schema.rb +175 -0
  183. data/db/migrate/20131024201501_add_address_type_to_kkt_shoppe_tax_rates.rb +5 -0
  184. data/db/migrate/20131024204815_create_kkt_shoppe_payments.rb +32 -0
  185. data/db/migrate/20131102143930_remove_default_on_order_item_weight.rb +9 -0
  186. data/db/migrate/20141013192427_create_kkt_shoppe_customers.rb +14 -0
  187. data/db/migrate/20141026175622_add_indexes_to_kkt_shoppe_order_items.rb +6 -0
  188. data/db/migrate/20141026175943_add_indexes_to_kkt_shoppe_orders.rb +7 -0
  189. data/db/migrate/20141026180333_add_indexes_to_kkt_shoppe_payments.rb +6 -0
  190. data/db/migrate/20141026180835_add_indexes_to_kkt_shoppe_product_attributes.rb +7 -0
  191. data/db/migrate/20141026180952_add_indexes_to_kkt_shoppe_product_categories.rb +5 -0
  192. data/db/migrate/20141026181040_add_indexes_to_kkt_shoppe_products.rb +8 -0
  193. data/db/migrate/20141026181312_add_indexes_to_kkt_shoppe_settings.rb +5 -0
  194. data/db/migrate/20141026181354_add_indexes_to_kkt_shoppe_stock_level_adjustments.rb +6 -0
  195. data/db/migrate/20141026181559_add_indexes_to_kkt_shoppe_users.rb +5 -0
  196. data/db/migrate/20141026181716_add_indexes_to_kkt_shoppe_delivery_services.rb +9 -0
  197. data/db/migrate/20141026181717_allow_multiple_kkt_shoppe_products_per_shoppe_product_category.rb +46 -0
  198. data/db/migrate/20141026181718_add_nested_to_product_categories.rb +30 -0
  199. data/db/migrate/20141027215005_create_kkt_shoppe_addresses.rb +17 -0
  200. data/db/migrate/20150315215633_add_customer_to_kkt_shoppe_orders.rb +5 -0
  201. data/db/migrate/20150513171350_create_kkt_shoppe_product_category_translation_table.rb +17 -0
  202. data/db/migrate/20150519173350_create_kkt_shoppe_product_translation_table.rb +18 -0
  203. data/db/schema.rb +308 -0
  204. data/db/seeds.rb +137 -0
  205. data/db/seeds_data/poe400.jpg +0 -0
  206. data/db/seeds_data/snom-870-blk.jpg +0 -0
  207. data/db/seeds_data/snom-870-grey.jpg +0 -0
  208. data/db/seeds_data/snom-mm2.jpg +0 -0
  209. data/db/seeds_data/spa303.jpg +0 -0
  210. data/db/seeds_data/t18p.jpg +0 -0
  211. data/db/seeds_data/t20p.jpg +0 -0
  212. data/db/seeds_data/t22p.jpg +0 -0
  213. data/db/seeds_data/t26p.jpg +0 -0
  214. data/db/seeds_data/t41pn.jpg +0 -0
  215. data/db/seeds_data/t46gn.jpg +0 -0
  216. data/db/seeds_data/w52p.jpg +0 -0
  217. data/db/seeds_data/yhs32.jpg +0 -0
  218. data/lib/kkt_shoppe/associated_countries.rb +20 -0
  219. data/lib/kkt_shoppe/country_importer.rb +15 -0
  220. data/lib/kkt_shoppe/default_navigation.rb +21 -0
  221. data/lib/kkt_shoppe/engine.rb +55 -0
  222. data/lib/kkt_shoppe/error.rb +21 -0
  223. data/lib/kkt_shoppe/errors/inappropriate_delivery_service.rb +6 -0
  224. data/lib/kkt_shoppe/errors/insufficient_stock_to_fulfil.rb +15 -0
  225. data/lib/kkt_shoppe/errors/invalid_configuration.rb +6 -0
  226. data/lib/kkt_shoppe/errors/not_enough_stock.rb +15 -0
  227. data/lib/kkt_shoppe/errors/payment_declined.rb +6 -0
  228. data/lib/kkt_shoppe/errors/refund_failed.rb +6 -0
  229. data/lib/kkt_shoppe/errors/unorderable_item.rb +6 -0
  230. data/lib/kkt_shoppe/navigation_manager.rb +81 -0
  231. data/lib/kkt_shoppe/orderable_item.rb +39 -0
  232. data/lib/kkt_shoppe/settings.rb +26 -0
  233. data/lib/kkt_shoppe/settings_loader.rb +16 -0
  234. data/lib/kkt_shoppe/setup_generator.rb +10 -0
  235. data/lib/kkt_shoppe/version.rb +3 -0
  236. data/lib/kkt_shoppe/view_helpers.rb +16 -0
  237. data/lib/kkt_shoppe.rb +60 -0
  238. data/lib/tasks/kylekthompson_shoppe.rake +29 -0
  239. metadata +662 -0
@@ -0,0 +1,58 @@
1
+ require "globalize"
2
+
3
+ module KktShoppe
4
+ class ProductLocalisationsController < ApplicationController
5
+
6
+ before_filter { @active_nav = :products }
7
+ before_filter { @product = KktShoppe::Product.find(params[:product_id]) }
8
+ before_filter { params[:id] && @localisation = @product.translations.find(params[:id]) }
9
+
10
+ def index
11
+ @localisations = @product.translations
12
+ end
13
+
14
+ def new
15
+ @localisation = @product.translations.new
16
+ render :action => "form"
17
+ end
18
+
19
+ def create
20
+ if I18n.available_locales.include? safe_params[:locale].to_sym
21
+ I18n.locale = safe_params[:locale].to_sym
22
+
23
+ if @product.update(safe_params)
24
+ I18n.locale = I18n.default_locale
25
+ redirect_to [@product, :localisations], :flash => { :notice => t("kkt_shoppe.localisations.localisation_created") }
26
+ else
27
+ render :action => "form"
28
+ end
29
+ else
30
+ redirect_to [@product, :localisations]
31
+ end
32
+ end
33
+
34
+ def edit
35
+ render :action => "form"
36
+ end
37
+
38
+ def update
39
+ if @localisation.update(safe_params)
40
+ redirect_to [@product, :localisations], :notice => t('kkt_shoppe.localisations.localisation_updated')
41
+ else
42
+ render :action => "form"
43
+ end
44
+ end
45
+
46
+ def destroy
47
+ @localisation.destroy
48
+ redirect_to [@product, :localisations], :notice => t('kkt_shoppe.localisations.localisation_destroyed')
49
+ end
50
+
51
+ private
52
+
53
+ def safe_params
54
+ params[:product_translation].permit(:name, :locale, :permalink, :description, :short_description)
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,58 @@
1
+ module KktShoppe
2
+ class ProductsController < KktShoppe::ApplicationController
3
+
4
+ before_filter { @active_nav = :products }
5
+ before_filter { params[:id] && @product = KktShoppe::Product.root.find(params[:id]) }
6
+
7
+ def index
8
+ @products = KktShoppe::Product.root.includes(:translations, :stock_level_adjustments, :default_image, :product_categories, :variants).order(:name).group_by(&:product_category).sort_by { |cat,pro| cat.name }
9
+ end
10
+
11
+ def new
12
+ @product = KktShoppe::Product.new
13
+ end
14
+
15
+ def create
16
+ @product = KktShoppe::Product.new(safe_params)
17
+ if @product.save
18
+ redirect_to :products, :flash => {:notice => t('kkt_shoppe.products.create_notice') }
19
+ else
20
+ render :action => "new"
21
+ end
22
+ end
23
+
24
+ def edit
25
+ end
26
+
27
+ def update
28
+ if @product.update(safe_params)
29
+ redirect_to [:edit, @product], :flash => {:notice => t('kkt_shoppe.products.update_notice') }
30
+ else
31
+ render :action => "edit"
32
+ end
33
+ end
34
+
35
+ def destroy
36
+ @product.destroy
37
+ redirect_to :products, :flash => {:notice => t('kkt_shoppe.products.destroy_notice')}
38
+ end
39
+
40
+ def import
41
+ if request.post?
42
+ if params[:import].nil?
43
+ redirect_to import_products_path, :flash => {:alert => I18n.t('kkt_shoppe.imports.errors.no_file')}
44
+ else
45
+ KktShoppe::Product.import(params[:import][:import_file])
46
+ redirect_to products_path, :flash => {:notice => I18n.t("kkt_shoppe.products.imports.success")}
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def safe_params
54
+ params[:product].permit(:name, :sku, :permalink, :description, :short_description, :weight, :price, :cost_price, :tax_rate_id, :stock_control, :default_image_file, :data_sheet_file, :active, :featured, :in_the_box, :product_attributes_array => [:key, :value, :searchable, :public], :product_category_ids => [])
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,34 @@
1
+ module KktShoppe
2
+ class SessionsController < KktShoppe::ApplicationController
3
+
4
+ layout 'kkt_shoppe/sub'
5
+ skip_before_filter :login_required, :only => [:new, :create, :reset]
6
+
7
+ def create
8
+ if user = KktShoppe::User.authenticate(params[:email_address], params[:password])
9
+ session[:kkt_shoppe_user_id] = user.id
10
+ redirect_to :orders
11
+ else
12
+ flash.now[:alert] = t('kkt_shoppe.sessions.create_alert')
13
+ render :action => "new"
14
+ end
15
+ end
16
+
17
+ def destroy
18
+ session[:kkt_shoppe_user_id] = nil
19
+ redirect_to :login
20
+ end
21
+
22
+ def reset
23
+
24
+ if request.post?
25
+ if user = KktShoppe::User.find_by_email_address(params[:email_address])
26
+ user.reset_password!
27
+ redirect_to login_path(:email_address => params[:email_address]), :notice => t('kkt_shoppe.sessions.reset_notice', email_address: user.email_address)
28
+ else
29
+ flash.now[:alert] = t('kkt_shoppe.sessions.reset_alert')
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module KktShoppe
2
+ class SettingsController < ApplicationController
3
+
4
+ before_filter { @active_nav = :settings }
5
+
6
+ def update
7
+ if KktShoppe.settings.demo_mode?
8
+ raise KktShoppe::Error, t('kkt_shoppe.settings.demo_mode_error')
9
+ end
10
+
11
+ KktShoppe::Setting.update_from_hash(params[:settings].permit!)
12
+ redirect_to :settings, :notice => t('kkt_shoppe.settings.update_notice')
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ module KktShoppe
2
+ class StockLevelAdjustmentsController < ApplicationController
3
+
4
+ SUITABLE_OBJECTS = ['KktShoppe::Product']
5
+ before_filter do
6
+ raise KktShoppe::Error, t('kkt_shoppe.stock_level_adjustments.invalid_item_type', suitable_objects: SUITABLE_OBJECTS.to_sentence) unless SUITABLE_OBJECTS.include?(params[:item_type])
7
+ @item = params[:item_type].constantize.find(params[:item_id].to_i)
8
+ end
9
+ before_filter { params[:id] && @sla = @item.stock_level_adjustments.find(params[:id].to_i) }
10
+
11
+ def index
12
+ @stock_level_adjustments = @item.stock_level_adjustments.ordered.page(params[:page]).per(10)
13
+ @new_sla = @item.stock_level_adjustments.build if @new_sla.nil?
14
+ if request.xhr?
15
+ render :action => 'index', :layout => false
16
+ end
17
+ end
18
+
19
+ def create
20
+ @new_sla = @item.stock_level_adjustments.build(params[:stock_level_adjustment].permit(:description, :adjustment))
21
+ if @new_sla.save
22
+ if request.xhr?
23
+ @new_sla = @item.stock_level_adjustments.build
24
+ index
25
+ else
26
+ redirect_to stock_level_adjustments_path(:item_id => params[:item_id], :item_type => params[:item_type]), :notice => t('kkt_shoppe.stock_level_adjustments.create_notice')
27
+ end
28
+ else
29
+ if request.xhr?
30
+ render :text => @new_sla.errors.full_messages.to_sentence, :status => 422
31
+ else
32
+ index
33
+ flash.now[:alert] = @new_sla.errors.full_messages.to_sentence
34
+ render :action => "index"
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ module KktShoppe
2
+ class TaxRatesController < KktShoppe::ApplicationController
3
+
4
+ before_filter { @active_nav = :tax_rates }
5
+ before_filter { params[:id] && @tax_rate = KktShoppe::TaxRate.find(params[:id]) }
6
+
7
+ def index
8
+ @tax_rates = KktShoppe::TaxRate.ordered.all
9
+ end
10
+
11
+ def new
12
+ @tax_rate = KktShoppe::TaxRate.new
13
+ render :action => "form"
14
+ end
15
+
16
+ def create
17
+ @tax_rate = KktShoppe::TaxRate.new(safe_params)
18
+ if @tax_rate.save
19
+ redirect_to :tax_rates, :flash => {:notice => t('kkt_shoppe.tax_rates.create_notice') }
20
+ else
21
+ render :action => "form"
22
+ end
23
+ end
24
+
25
+ def edit
26
+ render :action => "form"
27
+ end
28
+
29
+ def update
30
+ if @tax_rate.update(safe_params)
31
+ redirect_to [:edit, @tax_rate], :flash => {:notice => t('kkt_shoppe.tax_rates.update_notice')}
32
+ else
33
+ render :action => "form"
34
+ end
35
+ end
36
+
37
+ def destroy
38
+ @tax_rate.destroy
39
+ redirect_to :tax_rates, :flash => {:notice => t('kkt_shoppe.tax_rates.destroy_notice')}
40
+ end
41
+
42
+ private
43
+
44
+ def safe_params
45
+ params[:tax_rate].permit(:name, :rate, :address_type, :country_ids => [])
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,53 @@
1
+ module KktShoppe
2
+ class UsersController < KktShoppe::ApplicationController
3
+
4
+ before_filter { @active_nav = :users }
5
+ before_filter { params[:id] && @user = KktShoppe::User.find(params[:id]) }
6
+ before_filter(:only => [:create, :update, :destroy]) do
7
+ if KktShoppe.settings.demo_mode?
8
+ raise KktShoppe::Error, t('kkt_shoppe.users.demo_mode_error')
9
+ end
10
+ end
11
+
12
+ def index
13
+ @users = KktShoppe::User.all
14
+ end
15
+
16
+ def new
17
+ @user = KktShoppe::User.new
18
+ end
19
+
20
+ def create
21
+ @user = KktShoppe::User.new(safe_params)
22
+ if @user.save
23
+ redirect_to :users, :flash => {:notice => t('kkt_shoppe.users.create_notice') }
24
+ else
25
+ render :action => "new"
26
+ end
27
+ end
28
+
29
+ def edit
30
+ end
31
+
32
+ def update
33
+ if @user.update(safe_params)
34
+ redirect_to [:edit, @user], :flash => {:notice => t('kkt_shoppe.users.update_notice') }
35
+ else
36
+ render :action => "edit"
37
+ end
38
+ end
39
+
40
+ def destroy
41
+ raise KktShoppe::Error, t('kkt_shoppe.users.self_remove_error') if @user == current_user
42
+ @user.destroy
43
+ redirect_to :users, :flash => {:notice => t('kkt_shoppe.users.destroy_notice') }
44
+ end
45
+
46
+ private
47
+
48
+ def safe_params
49
+ params[:user].permit(:first_name, :last_name, :email_address, :password, :password_confirmation)
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,50 @@
1
+ module KktShoppe
2
+ class VariantsController < ApplicationController
3
+
4
+ before_filter { @active_nav = :products }
5
+ before_filter { @product = KktShoppe::Product.find(params[:product_id]) }
6
+ before_filter { params[:id] && @variant = @product.variants.find(params[:id]) }
7
+
8
+ def index
9
+ @variants = @product.variants.ordered
10
+ end
11
+
12
+ def new
13
+ @variant = @product.variants.build
14
+ render :action => "form"
15
+ end
16
+
17
+ def create
18
+ @variant = @product.variants.build(safe_params)
19
+ if @variant.save
20
+ redirect_to [@product, :variants], :notice => t('kkt_shoppe.variants.create_notice')
21
+ else
22
+ render :action => "form"
23
+ end
24
+ end
25
+
26
+ def edit
27
+ render :action => "form"
28
+ end
29
+
30
+ def update
31
+ if @variant.update(safe_params)
32
+ redirect_to edit_product_variant_path(@product, @variant), :notice => t('kkt_shoppe.variants.update_notice')
33
+ else
34
+ render :action => "form"
35
+ end
36
+ end
37
+
38
+ def destroy
39
+ @variant.destroy
40
+ redirect_to [@product, :variants], :notice => t('kkt_shoppe.variants.destroy_notice')
41
+ end
42
+
43
+ private
44
+
45
+ def safe_params
46
+ params[:product].permit(:name, :permalink, :sku, :default_image_file, :price, :cost_price, :tax_rate_id, :weight, :stock_control, :active, :default)
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ module KktShoppe
2
+ module ApplicationHelper
3
+
4
+ def navigation_manager_link(item)
5
+ link_to item.description, item.url(self), item.link_options.merge(:class => item.active?(self) ? 'active' : 'inactive')
6
+ end
7
+
8
+ def status_tag(status)
9
+ content_tag :span, t("kkt_shoppe.orders.statuses.#{status}"), :class => "status-tag #{status}"
10
+ end
11
+
12
+ def attachment_preview(attachment, options = {})
13
+ if attachment
14
+ String.new.tap do |s|
15
+ if attachment.image?
16
+ style = "style='background-image:url(#{attachment.path})'"
17
+ else
18
+ style = ''
19
+ end
20
+ s << "<div class='attachmentPreview #{attachment.image? ? 'image' : 'doc'}'>"
21
+ s << "<div class='imgContainer'><div class='img' #{style}></div></div>"
22
+ s << "<div class='desc'>"
23
+ s << "<span class='filename'><a href='#{attachment_path(attachment)}'>#{attachment.file_name}</a></span>"
24
+ s << "<span class='delete'>"
25
+ s << link_to(t('helpers.attachment_preview.delete', :default => 'Delete this file?'), attachment_path(attachment), :method => :delete, :data => {:confirm => t('helpers.attachment_preview.delete_confirm', :default => "Are you sure you wish to remove this attachment?")})
26
+ s << "</span>"
27
+ s << "</div>"
28
+ s << "</div>"
29
+ end.html_safe
30
+ elsif !options[:hide_if_blank]
31
+ "<div class='attachmentPreview'><div class='imgContainer'><div class='img none'></div></div><div class='desc none'>#{t('helpers.attachment_preview.no_attachment')},</div></div>".html_safe
32
+ end
33
+ end
34
+
35
+ def settings_label(field)
36
+ "<label for='settings_#{field}'>#{t("kkt_shoppe.settings.labels.#{field}")}</label>".html_safe
37
+ end
38
+
39
+ def settings_field(field, options = {})
40
+ default = I18n.t("kkt_shoppe.settings.defaults")[field.to_sym]
41
+ value = (params[:settings] && params[:settings][field]) || KktShoppe.settings[field.to_s]
42
+ type = I18n.t("kkt_shoppe.settings.types")[field.to_sym] || 'string'
43
+ case type
44
+ when 'boolean'
45
+ String.new.tap do |s|
46
+ value = default if value.blank?
47
+ s << "<div class='radios'>"
48
+ s << radio_button_tag("settings[#{field}]", 'true', value == true, :id => "settings_#{field}_true")
49
+ s << label_tag("settings_#{field}_true", t("kkt_shoppe.settings.options.#{field}.affirmative", :default => 'Yes'))
50
+ s << radio_button_tag("settings[#{field}]", 'false', value == false, :id => "settings_#{field}_false")
51
+ s << label_tag("settings_#{field}_false", t("kkt_shoppe.settings.options.#{field}.negative", :default => 'No'))
52
+ s << "</div>"
53
+ end.html_safe
54
+ else
55
+ text_field_tag "settings[#{field}]", value, options.merge(:placeholder => default, :class => 'text')
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ module KktShoppe
2
+ module ProductCategoryHelper
3
+
4
+ def nested_product_category_spacing_adjusted_for_depth(category, relative_depth)
5
+ depth = category.depth - relative_depth
6
+ spacing = depth < 2 ? 0.8 : 1.5
7
+ ("<span style='display:inline-block;width:#{spacing}em;'></span>"*category.depth).html_safe
8
+ end
9
+
10
+ def nested_product_category_rows(category, current_category = nil, link_to_current = true, relative_depth = 0)
11
+ if category.present? && category.children.count > 0
12
+ String.new.tap do |s|
13
+ category.children.ordered.each do |child|
14
+ s << "<tr>"
15
+ s << "<td>"
16
+ if child == current_category
17
+ if link_to_current == false
18
+ s << "#{nested_product_category_spacing_adjusted_for_depth child, relative_depth} &#8627; #{child.name} (#{t('kkt_shoppe.product_category.nesting.current_category')})"
19
+ else
20
+ s << "#{nested_product_category_spacing_adjusted_for_depth child, relative_depth} &#8627; #{link_to(child.name, [:edit, child]).html_safe} (#{t('kkt_shoppe.product_category.nesting.current_category')})"
21
+ end
22
+ else
23
+ s << "#{nested_product_category_spacing_adjusted_for_depth child, relative_depth} &#8627; #{link_to(child.name, [:edit, child]).html_safe}"
24
+ end
25
+ s << "</td>"
26
+ s << "</tr>"
27
+ s << nested_product_category_rows(child, current_category, link_to_current, relative_depth)
28
+ end
29
+ end.html_safe
30
+ else
31
+ ""
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ module KktShoppe
2
+ class OrderMailer < ActionMailer::Base
3
+
4
+ def received(order)
5
+ @order = order
6
+ mail :from => KktShoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('kkt_shoppe.order_mailer.received.subject', :default => "Order Confirmation")
7
+ end
8
+
9
+ def accepted(order)
10
+ @order = order
11
+ mail :from => KktShoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('kkt_shoppe.order_mailer.received.accepted', :default => "Order Accepted")
12
+ end
13
+
14
+ def rejected(order)
15
+ @order = order
16
+ mail :from => KktShoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('kkt_shoppe.order_mailer.received.rejected', :default => "Order Rejected")
17
+ end
18
+
19
+ def shipped(order)
20
+ @order = order
21
+ mail :from => KktShoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('kkt_shoppe.order_mailer.received.shipped', :default => "Order Shipped")
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ module KktShoppe
2
+ class UserMailer < ActionMailer::Base
3
+
4
+ def new_password(user)
5
+ @user = user
6
+ mail :from => KktShoppe.settings.outbound_email_address, :to => user.email_address, :subject => "Your new KktShoppe password"
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ module KktShoppe
2
+ class Address < ActiveRecord::Base
3
+
4
+ # An array of all the available types for an address
5
+ TYPES = ["billing", "delivery"]
6
+
7
+ # Set the table name
8
+ self.table_name = "kkt_shoppe_addresses"
9
+
10
+ # The customer which this address should be linked to
11
+ #
12
+ # @return [KktShoppe::Customer]
13
+ belongs_to :customer, :class_name => "KktShoppe::Customer"
14
+
15
+ # The order which this address should be linked to
16
+ #
17
+ # @return [KktShoppe::Order]
18
+ belongs_to :order, :class_name => "KktShoppe::Order"
19
+
20
+ # The country which this address should be linked to
21
+ #
22
+ # @return [KktShoppe::Country]
23
+ belongs_to :country, :class_name => "KktShoppe::Country"
24
+
25
+ # Validations
26
+ validates :address_type, :presence => true, :inclusion => {:in => TYPES}
27
+ validates :address1, :presence => true
28
+ validates :address3, :presence => true
29
+ validates :address4, :presence => true
30
+ validates :postcode, :presence => true
31
+ validates :country, :presence => true
32
+
33
+ # All addresses ordered by their id asending
34
+ scope :ordered, -> { order(:id => :desc)}
35
+ scope :default, -> { where(default: true)}
36
+ scope :billing, -> { where(address_type: "billing")}
37
+ scope :delivery, -> { where(address_type: "delivery")}
38
+
39
+ def full_address
40
+ [address1, address2, address3, address4, postcode, country.try(:name)].join(", ")
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ module KktShoppe
2
+
3
+ # The KktShoppe::Country model stores countries which can be used for delivery & billing
4
+ # addresses for orders.
5
+ #
6
+ # You can use the KktShoppe::CountryImporter to import a pre-defined list of countries
7
+ # into your database. This automatically happens when you run the 'kkt_shoppe:setup'
8
+ # rake task.
9
+
10
+ class Country < ActiveRecord::Base
11
+
12
+ self.table_name = 'kkt_shoppe_countries'
13
+
14
+ # All orders which have this country set as their billing country
15
+ has_many :billed_orders, :dependent => :restrict_with_exception, :class_name => 'KktShoppe::Order', :foreign_key => 'billing_country_id'
16
+
17
+ # All orders which have this country set as their delivery country
18
+ has_many :delivered_orders, :dependent => :restrict_with_exception, :class_name => 'KktShoppe::Order', :foreign_key => 'delivery_country_id'
19
+
20
+ # All countries ordered by their name asending
21
+ scope :ordered, -> { order(:name => :asc) }
22
+
23
+ # Validations
24
+ validates :name, :presence => true
25
+
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ module KktShoppe
2
+ class Customer < ActiveRecord::Base
3
+
4
+ self.table_name = "kkt_shoppe_customers"
5
+
6
+ has_many :addresses, :dependent => :restrict_with_exception, :class_name => "KktShoppe::Address"
7
+
8
+ has_many :orders, :dependent => :restrict_with_exception, :class_name => "KktShoppe::Order"
9
+
10
+ # Validations
11
+ validates :email, :presence => true, :uniqueness => true, :format => {:with => /\A\b[A-Z0-9\.\_\%\-\+]+@(?:[A-Z0-9\-]+\.)+[A-Z]{2,6}\b\z/i}
12
+ validates :phone, :presence => true, :format => {:with => /\A[\d\ \-x\(\)]{7,}\z/}
13
+
14
+ # All customers ordered by their ID desending
15
+ scope :ordered, -> { order(:id => :desc)}
16
+
17
+ # The name of the customer in the format of "Company (First Last)" or if they don't have
18
+ # company specified, just "First Last".
19
+ #
20
+ # @return [String]
21
+ def name
22
+ company.blank? ? full_name : "#{company} (#{full_name})"
23
+ end
24
+
25
+ # The full name of the customer created by concatinting the first & last name
26
+ #
27
+ # @return [String]
28
+ def full_name
29
+ "#{first_name} #{last_name}"
30
+ end
31
+
32
+ def self.ransackable_attributes(auth_object = nil)
33
+ ["id", "first_name", "last_name", "company", "email", "phone", "mobile"] + _ransackers.keys
34
+ end
35
+
36
+ def self.ransackable_associations(auth_object = nil)
37
+ []
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ module KktShoppe
2
+ class DeliveryService < ActiveRecord::Base
3
+
4
+ self.table_name = 'kkt_shoppe_delivery_services'
5
+
6
+ # Validations
7
+ validates :name, :presence => true
8
+ validates :courier, :presence => true
9
+
10
+ # Orders which are assigned to this delivery service
11
+ has_many :orders, :dependent => :restrict_with_exception, :class_name => 'KktShoppe::Order'
12
+
13
+ # Prices for the different levels of service within this delivery service
14
+ has_many :delivery_service_prices, :dependent => :destroy, :class_name => 'KktShoppe::DeliveryServicePrice'
15
+
16
+ # All active delivery services
17
+ scope :active, -> { where(:active => true)}
18
+
19
+ # Returns a tracking URL for the passed order
20
+ #
21
+ # @param order [KktShoppe::Order]
22
+ # @return [String] the full URL for the order.
23
+ def tracking_url_for(order)
24
+ return nil if self.tracking_url.blank?
25
+ tracking_url = self.tracking_url.dup
26
+ tracking_url.gsub!("{{consignment_number}}", CGI.escape(order.consignment_number.to_s))
27
+ tracking_url.gsub!("{{delivery_postcode}}", CGI.escape(order.delivery_postcode.to_s))
28
+ tracking_url.gsub!("{{billing_postcode}}", CGI.escape(order.billing_postcode.to_s))
29
+ tracking_url
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ module KktShoppe
2
+ class DeliveryServicePrice < ActiveRecord::Base
3
+
4
+ # Set the table name
5
+ self.table_name = 'kkt_shoppe_delivery_service_prices'
6
+
7
+ include KktShoppe::AssociatedCountries
8
+
9
+ # The delivery service which this price belongs to
10
+ belongs_to :delivery_service, :class_name => 'KktShoppe::DeliveryService'
11
+
12
+ # The tax rate which should be applied
13
+ belongs_to :tax_rate, :class_name => "KktShoppe::TaxRate"
14
+
15
+ # Validations
16
+ validates :code, :presence => true
17
+ validates :price, :numericality => true
18
+ validates :cost_price, :numericality => true, :allow_blank => true
19
+ validates :min_weight, :numericality => true
20
+ validates :max_weight, :numericality => true
21
+
22
+ # All prices ordered by their price ascending
23
+ scope :ordered, -> { order(:price => :asc) }
24
+
25
+ # All prices which are suitable for the weight passed.
26
+ #
27
+ # @param weight [BigDecimal] the weight of the order
28
+ scope :for_weight, -> weight { where("min_weight <= ? AND max_weight >= ?", weight, weight) }
29
+
30
+ end
31
+ end