kylekthompson-shoppe 1.0.7

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 (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/shoppe/chosen-sprite.png +0 -0
  6. data/app/assets/images/shoppe/chosen-sprite@2x.png +0 -0
  7. data/app/assets/images/shoppe/document.svg +1 -0
  8. data/app/assets/images/shoppe/icons/bag.svg +1 -0
  9. data/app/assets/images/shoppe/icons/balance.svg +1 -0
  10. data/app/assets/images/shoppe/icons/box.svg +1 -0
  11. data/app/assets/images/shoppe/icons/building.svg +1 -0
  12. data/app/assets/images/shoppe/icons/chart.svg +1 -0
  13. data/app/assets/images/shoppe/icons/chat.svg +1 -0
  14. data/app/assets/images/shoppe/icons/checkbox.svg +1 -0
  15. data/app/assets/images/shoppe/icons/checkbox2.svg +1 -0
  16. data/app/assets/images/shoppe/icons/cloud.svg +1 -0
  17. data/app/assets/images/shoppe/icons/cone.svg +1 -0
  18. data/app/assets/images/shoppe/icons/credit_card.svg +1 -0
  19. data/app/assets/images/shoppe/icons/currency.svg +1 -0
  20. data/app/assets/images/shoppe/icons/edit.svg +14 -0
  21. data/app/assets/images/shoppe/icons/flowchart.svg +1 -0
  22. data/app/assets/images/shoppe/icons/gift.svg +1 -0
  23. data/app/assets/images/shoppe/icons/globe.svg +1 -0
  24. data/app/assets/images/shoppe/icons/id.svg +1 -0
  25. data/app/assets/images/shoppe/icons/id2.svg +1 -0
  26. data/app/assets/images/shoppe/icons/locked.svg +1 -0
  27. data/app/assets/images/shoppe/icons/report.svg +1 -0
  28. data/app/assets/images/shoppe/icons/search.svg +1 -0
  29. data/app/assets/images/shoppe/icons/support.svg +1 -0
  30. data/app/assets/images/shoppe/icons/tags.svg +1 -0
  31. data/app/assets/images/shoppe/icons/toolbox.svg +1 -0
  32. data/app/assets/images/shoppe/icons/unlocked.svg +1 -0
  33. data/app/assets/images/shoppe/icons/wallet.svg +1 -0
  34. data/app/assets/images/shoppe/logo.svg +47 -0
  35. data/app/assets/images/shoppe/move.svg +1 -0
  36. data/app/assets/images/shoppe/shoppe.svg +25 -0
  37. data/app/assets/images/shoppe/square.svg +9 -0
  38. data/app/assets/images/shoppe/statuses/accepted.svg +14 -0
  39. data/app/assets/images/shoppe/statuses/paid.svg +16 -0
  40. data/app/assets/images/shoppe/statuses/received.svg +15 -0
  41. data/app/assets/images/shoppe/statuses/rejected.svg +14 -0
  42. data/app/assets/images/shoppe/statuses/shipped.svg +14 -0
  43. data/app/assets/images/shoppe/table-tear-off.png +0 -0
  44. data/app/assets/javascripts/shoppe/application.coffee +123 -0
  45. data/app/assets/javascripts/shoppe/chosen.jquery.js +1166 -0
  46. data/app/assets/javascripts/shoppe/jquery_ui.js +6 -0
  47. data/app/assets/javascripts/shoppe/mousetrap.js +9 -0
  48. data/app/assets/javascripts/shoppe/order_form.coffee +47 -0
  49. data/app/assets/stylesheets/shoppe/application.scss +601 -0
  50. data/app/assets/stylesheets/shoppe/chosen.scss +424 -0
  51. data/app/assets/stylesheets/shoppe/dialog.scss +25 -0
  52. data/app/assets/stylesheets/shoppe/elements.scss +79 -0
  53. data/app/assets/stylesheets/shoppe/printable.scss +67 -0
  54. data/app/assets/stylesheets/shoppe/reset.scss +93 -0
  55. data/app/assets/stylesheets/shoppe/sub.scss +106 -0
  56. data/app/assets/stylesheets/shoppe/variables.scss +1 -0
  57. data/app/controllers/shoppe/addresses_controller.rb +47 -0
  58. data/app/controllers/shoppe/application_controller.rb +46 -0
  59. data/app/controllers/shoppe/attachments_controller.rb +14 -0
  60. data/app/controllers/shoppe/countries_controller.rb +47 -0
  61. data/app/controllers/shoppe/customers_controller.rb +55 -0
  62. data/app/controllers/shoppe/dashboard_controller.rb +9 -0
  63. data/app/controllers/shoppe/delivery_service_prices_controller.rb +45 -0
  64. data/app/controllers/shoppe/delivery_services_controller.rb +47 -0
  65. data/app/controllers/shoppe/orders_controller.rb +119 -0
  66. data/app/controllers/shoppe/payments_controller.rb +33 -0
  67. data/app/controllers/shoppe/product_categories_controller.rb +47 -0
  68. data/app/controllers/shoppe/product_category_localisations_controller.rb +58 -0
  69. data/app/controllers/shoppe/product_localisations_controller.rb +58 -0
  70. data/app/controllers/shoppe/products_controller.rb +58 -0
  71. data/app/controllers/shoppe/sessions_controller.rb +34 -0
  72. data/app/controllers/shoppe/settings_controller.rb +16 -0
  73. data/app/controllers/shoppe/stock_level_adjustments_controller.rb +40 -0
  74. data/app/controllers/shoppe/tax_rates_controller.rb +49 -0
  75. data/app/controllers/shoppe/users_controller.rb +53 -0
  76. data/app/controllers/shoppe/variants_controller.rb +50 -0
  77. data/app/helpers/shoppe/application_helper.rb +60 -0
  78. data/app/helpers/shoppe/product_category_helper.rb +36 -0
  79. data/app/mailers/shoppe/order_mailer.rb +25 -0
  80. data/app/mailers/shoppe/user_mailer.rb +10 -0
  81. data/app/models/shoppe/address.rb +44 -0
  82. data/app/models/shoppe/country.rb +27 -0
  83. data/app/models/shoppe/customer.rb +41 -0
  84. data/app/models/shoppe/delivery_service.rb +33 -0
  85. data/app/models/shoppe/delivery_service_price.rb +31 -0
  86. data/app/models/shoppe/order.rb +100 -0
  87. data/app/models/shoppe/order/actions.rb +94 -0
  88. data/app/models/shoppe/order/billing.rb +105 -0
  89. data/app/models/shoppe/order/delivery.rb +229 -0
  90. data/app/models/shoppe/order/states.rb +69 -0
  91. data/app/models/shoppe/order_item.rb +239 -0
  92. data/app/models/shoppe/payment.rb +86 -0
  93. data/app/models/shoppe/product.rb +189 -0
  94. data/app/models/shoppe/product/product_attributes.rb +20 -0
  95. data/app/models/shoppe/product/variants.rb +51 -0
  96. data/app/models/shoppe/product_attribute.rb +66 -0
  97. data/app/models/shoppe/product_categorization.rb +14 -0
  98. data/app/models/shoppe/product_category.rb +75 -0
  99. data/app/models/shoppe/setting.rb +68 -0
  100. data/app/models/shoppe/stock_level_adjustment.rb +19 -0
  101. data/app/models/shoppe/tax_rate.rb +46 -0
  102. data/app/models/shoppe/user.rb +49 -0
  103. data/app/validators/permalink_validator.rb +7 -0
  104. data/app/views/layouts/shoppe/application.html.haml +34 -0
  105. data/app/views/layouts/shoppe/printable.html.haml +11 -0
  106. data/app/views/layouts/shoppe/sub.html.haml +10 -0
  107. data/app/views/shoppe/addresses/_form.html.haml +33 -0
  108. data/app/views/shoppe/addresses/edit.html.haml +5 -0
  109. data/app/views/shoppe/addresses/new.html.haml +5 -0
  110. data/app/views/shoppe/countries/_form.html.haml +35 -0
  111. data/app/views/shoppe/countries/edit.html.haml +6 -0
  112. data/app/views/shoppe/countries/index.html.haml +25 -0
  113. data/app/views/shoppe/countries/new.html.haml +7 -0
  114. data/app/views/shoppe/customers/_addresses.html.haml +20 -0
  115. data/app/views/shoppe/customers/_form.html.haml +30 -0
  116. data/app/views/shoppe/customers/_search_form.html.haml +13 -0
  117. data/app/views/shoppe/customers/edit.html.haml +5 -0
  118. data/app/views/shoppe/customers/index.html.haml +32 -0
  119. data/app/views/shoppe/customers/new.html.haml +5 -0
  120. data/app/views/shoppe/customers/show.html.haml +53 -0
  121. data/app/views/shoppe/delivery_service_prices/_form.html.haml +44 -0
  122. data/app/views/shoppe/delivery_service_prices/edit.html.haml +6 -0
  123. data/app/views/shoppe/delivery_service_prices/index.html.haml +23 -0
  124. data/app/views/shoppe/delivery_service_prices/new.html.haml +6 -0
  125. data/app/views/shoppe/delivery_services/_form.html.haml +38 -0
  126. data/app/views/shoppe/delivery_services/edit.html.haml +9 -0
  127. data/app/views/shoppe/delivery_services/index.html.haml +27 -0
  128. data/app/views/shoppe/delivery_services/new.html.haml +6 -0
  129. data/app/views/shoppe/order_mailer/accepted.text.erb +12 -0
  130. data/app/views/shoppe/order_mailer/received.text.erb +9 -0
  131. data/app/views/shoppe/order_mailer/rejected.text.erb +10 -0
  132. data/app/views/shoppe/order_mailer/shipped.text.erb +16 -0
  133. data/app/views/shoppe/orders/_form.html.haml +58 -0
  134. data/app/views/shoppe/orders/_order_details.html.haml +57 -0
  135. data/app/views/shoppe/orders/_order_items.html.haml +38 -0
  136. data/app/views/shoppe/orders/_order_items_form.html.haml +61 -0
  137. data/app/views/shoppe/orders/_payments_form.html.haml +15 -0
  138. data/app/views/shoppe/orders/_payments_table.html.haml +37 -0
  139. data/app/views/shoppe/orders/_search_form.html.haml +24 -0
  140. data/app/views/shoppe/orders/_status_bar.html.haml +35 -0
  141. data/app/views/shoppe/orders/despatch_note.html.haml +45 -0
  142. data/app/views/shoppe/orders/edit.html.haml +21 -0
  143. data/app/views/shoppe/orders/index.html.haml +39 -0
  144. data/app/views/shoppe/orders/new.html.haml +14 -0
  145. data/app/views/shoppe/orders/show.html.haml +25 -0
  146. data/app/views/shoppe/payments/refund.html.haml +13 -0
  147. data/app/views/shoppe/product_categories/_form.html.haml +50 -0
  148. data/app/views/shoppe/product_categories/edit.html.haml +8 -0
  149. data/app/views/shoppe/product_categories/index.html.haml +27 -0
  150. data/app/views/shoppe/product_categories/new.html.haml +6 -0
  151. data/app/views/shoppe/product_category_localisations/form.html.haml +29 -0
  152. data/app/views/shoppe/product_category_localisations/index.html.haml +26 -0
  153. data/app/views/shoppe/product_localisations/form.html.haml +32 -0
  154. data/app/views/shoppe/product_localisations/index.html.haml +26 -0
  155. data/app/views/shoppe/products/_form.html.haml +118 -0
  156. data/app/views/shoppe/products/_table.html.haml +42 -0
  157. data/app/views/shoppe/products/edit.html.haml +9 -0
  158. data/app/views/shoppe/products/import.html.haml +67 -0
  159. data/app/views/shoppe/products/index.html.haml +9 -0
  160. data/app/views/shoppe/products/new.html.haml +7 -0
  161. data/app/views/shoppe/sessions/new.html.haml +12 -0
  162. data/app/views/shoppe/sessions/reset.html.haml +12 -0
  163. data/app/views/shoppe/settings/edit.html.haml +19 -0
  164. data/app/views/shoppe/shared/error.html.haml +6 -0
  165. data/app/views/shoppe/stock_level_adjustments/index.html.haml +40 -0
  166. data/app/views/shoppe/tax_rates/form.html.haml +28 -0
  167. data/app/views/shoppe/tax_rates/index.html.haml +17 -0
  168. data/app/views/shoppe/user_mailer/new_password.text.erb +9 -0
  169. data/app/views/shoppe/users/_form.html.haml +27 -0
  170. data/app/views/shoppe/users/edit.html.haml +5 -0
  171. data/app/views/shoppe/users/index.html.haml +17 -0
  172. data/app/views/shoppe/users/new.html.haml +7 -0
  173. data/app/views/shoppe/variants/form.html.haml +68 -0
  174. data/app/views/shoppe/variants/index.html.haml +33 -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_shoppe_initial_schema.rb +175 -0
  183. data/db/migrate/20131024201501_add_address_type_to_shoppe_tax_rates.rb +5 -0
  184. data/db/migrate/20131024204815_create_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_shoppe_customers.rb +14 -0
  187. data/db/migrate/20141026175622_add_indexes_to_shoppe_order_items.rb +6 -0
  188. data/db/migrate/20141026175943_add_indexes_to_shoppe_orders.rb +7 -0
  189. data/db/migrate/20141026180333_add_indexes_to_shoppe_payments.rb +6 -0
  190. data/db/migrate/20141026180835_add_indexes_to_shoppe_product_attributes.rb +7 -0
  191. data/db/migrate/20141026180952_add_indexes_to_shoppe_product_categories.rb +5 -0
  192. data/db/migrate/20141026181040_add_indexes_to_shoppe_products.rb +8 -0
  193. data/db/migrate/20141026181312_add_indexes_to_shoppe_settings.rb +5 -0
  194. data/db/migrate/20141026181354_add_indexes_to_shoppe_stock_level_adjustments.rb +6 -0
  195. data/db/migrate/20141026181559_add_indexes_to_shoppe_users.rb +5 -0
  196. data/db/migrate/20141026181716_add_indexes_to_shoppe_delivery_services.rb +9 -0
  197. data/db/migrate/20141026181717_allow_multiple_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_shoppe_addresses.rb +17 -0
  200. data/db/migrate/20150315215633_add_customer_to_shoppe_orders.rb +5 -0
  201. data/db/migrate/20150513171350_create_shoppe_product_category_translation_table.rb +17 -0
  202. data/db/migrate/20150519173350_create_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/shoppe.rb +60 -0
  219. data/lib/shoppe/associated_countries.rb +20 -0
  220. data/lib/shoppe/country_importer.rb +15 -0
  221. data/lib/shoppe/default_navigation.rb +21 -0
  222. data/lib/shoppe/engine.rb +55 -0
  223. data/lib/shoppe/error.rb +21 -0
  224. data/lib/shoppe/errors/inappropriate_delivery_service.rb +6 -0
  225. data/lib/shoppe/errors/insufficient_stock_to_fulfil.rb +15 -0
  226. data/lib/shoppe/errors/invalid_configuration.rb +6 -0
  227. data/lib/shoppe/errors/not_enough_stock.rb +15 -0
  228. data/lib/shoppe/errors/payment_declined.rb +6 -0
  229. data/lib/shoppe/errors/refund_failed.rb +6 -0
  230. data/lib/shoppe/errors/unorderable_item.rb +6 -0
  231. data/lib/shoppe/navigation_manager.rb +81 -0
  232. data/lib/shoppe/orderable_item.rb +39 -0
  233. data/lib/shoppe/settings.rb +26 -0
  234. data/lib/shoppe/settings_loader.rb +16 -0
  235. data/lib/shoppe/setup_generator.rb +10 -0
  236. data/lib/shoppe/version.rb +3 -0
  237. data/lib/shoppe/view_helpers.rb +16 -0
  238. data/lib/tasks/shoppe.rake +29 -0
  239. metadata +662 -0
@@ -0,0 +1,34 @@
1
+ module Shoppe
2
+ class SessionsController < Shoppe::ApplicationController
3
+
4
+ layout 'shoppe/sub'
5
+ skip_before_filter :login_required, :only => [:new, :create, :reset]
6
+
7
+ def create
8
+ if user = Shoppe::User.authenticate(params[:email_address], params[:password])
9
+ session[:shoppe_user_id] = user.id
10
+ redirect_to :orders
11
+ else
12
+ flash.now[:alert] = t('shoppe.sessions.create_alert')
13
+ render :action => "new"
14
+ end
15
+ end
16
+
17
+ def destroy
18
+ session[:shoppe_user_id] = nil
19
+ redirect_to :login
20
+ end
21
+
22
+ def reset
23
+
24
+ if request.post?
25
+ if user = Shoppe::User.find_by_email_address(params[:email_address])
26
+ user.reset_password!
27
+ redirect_to login_path(:email_address => params[:email_address]), :notice => t('shoppe.sessions.reset_notice', email_address: user.email_address)
28
+ else
29
+ flash.now[:alert] = t('shoppe.sessions.reset_alert')
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module Shoppe
2
+ class SettingsController < ApplicationController
3
+
4
+ before_filter { @active_nav = :settings }
5
+
6
+ def update
7
+ if Shoppe.settings.demo_mode?
8
+ raise Shoppe::Error, t('shoppe.settings.demo_mode_error')
9
+ end
10
+
11
+ Shoppe::Setting.update_from_hash(params[:settings].permit!)
12
+ redirect_to :settings, :notice => t('shoppe.settings.update_notice')
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ module Shoppe
2
+ class StockLevelAdjustmentsController < ApplicationController
3
+
4
+ SUITABLE_OBJECTS = ['Shoppe::Product']
5
+ before_filter do
6
+ raise Shoppe::Error, t('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('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 Shoppe
2
+ class TaxRatesController < Shoppe::ApplicationController
3
+
4
+ before_filter { @active_nav = :tax_rates }
5
+ before_filter { params[:id] && @tax_rate = Shoppe::TaxRate.find(params[:id]) }
6
+
7
+ def index
8
+ @tax_rates = Shoppe::TaxRate.ordered.all
9
+ end
10
+
11
+ def new
12
+ @tax_rate = Shoppe::TaxRate.new
13
+ render :action => "form"
14
+ end
15
+
16
+ def create
17
+ @tax_rate = Shoppe::TaxRate.new(safe_params)
18
+ if @tax_rate.save
19
+ redirect_to :tax_rates, :flash => {:notice => t('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('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('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 Shoppe
2
+ class UsersController < Shoppe::ApplicationController
3
+
4
+ before_filter { @active_nav = :users }
5
+ before_filter { params[:id] && @user = Shoppe::User.find(params[:id]) }
6
+ before_filter(:only => [:create, :update, :destroy]) do
7
+ if Shoppe.settings.demo_mode?
8
+ raise Shoppe::Error, t('shoppe.users.demo_mode_error')
9
+ end
10
+ end
11
+
12
+ def index
13
+ @users = Shoppe::User.all
14
+ end
15
+
16
+ def new
17
+ @user = Shoppe::User.new
18
+ end
19
+
20
+ def create
21
+ @user = Shoppe::User.new(safe_params)
22
+ if @user.save
23
+ redirect_to :users, :flash => {:notice => t('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('shoppe.users.update_notice') }
35
+ else
36
+ render :action => "edit"
37
+ end
38
+ end
39
+
40
+ def destroy
41
+ raise Shoppe::Error, t('shoppe.users.self_remove_error') if @user == current_user
42
+ @user.destroy
43
+ redirect_to :users, :flash => {:notice => t('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 Shoppe
2
+ class VariantsController < ApplicationController
3
+
4
+ before_filter { @active_nav = :products }
5
+ before_filter { @product = Shoppe::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('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('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('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 Shoppe
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("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("shoppe.settings.labels.#{field}")}</label>".html_safe
37
+ end
38
+
39
+ def settings_field(field, options = {})
40
+ default = I18n.t("shoppe.settings.defaults")[field.to_sym]
41
+ value = (params[:settings] && params[:settings][field]) || Shoppe.settings[field.to_s]
42
+ type = I18n.t("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("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("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 Shoppe
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('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('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 Shoppe
2
+ class OrderMailer < ActionMailer::Base
3
+
4
+ def received(order)
5
+ @order = order
6
+ mail :from => Shoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.subject', :default => "Order Confirmation")
7
+ end
8
+
9
+ def accepted(order)
10
+ @order = order
11
+ mail :from => Shoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.accepted', :default => "Order Accepted")
12
+ end
13
+
14
+ def rejected(order)
15
+ @order = order
16
+ mail :from => Shoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.rejected', :default => "Order Rejected")
17
+ end
18
+
19
+ def shipped(order)
20
+ @order = order
21
+ mail :from => Shoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.shipped', :default => "Order Shipped")
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ module Shoppe
2
+ class UserMailer < ActionMailer::Base
3
+
4
+ def new_password(user)
5
+ @user = user
6
+ mail :from => Shoppe.settings.outbound_email_address, :to => user.email_address, :subject => "Your new Shoppe password"
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ module Shoppe
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 = "shoppe_addresses"
9
+
10
+ # The customer which this address should be linked to
11
+ #
12
+ # @return [Shoppe::Customer]
13
+ belongs_to :customer, :class_name => "Shoppe::Customer"
14
+
15
+ # The order which this address should be linked to
16
+ #
17
+ # @return [Shoppe::Order]
18
+ belongs_to :order, :class_name => "Shoppe::Order"
19
+
20
+ # The country which this address should be linked to
21
+ #
22
+ # @return [Shoppe::Country]
23
+ belongs_to :country, :class_name => "Shoppe::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 Shoppe
2
+
3
+ # The Shoppe::Country model stores countries which can be used for delivery & billing
4
+ # addresses for orders.
5
+ #
6
+ # You can use the Shoppe::CountryImporter to import a pre-defined list of countries
7
+ # into your database. This automatically happens when you run the 'shoppe:setup'
8
+ # rake task.
9
+
10
+ class Country < ActiveRecord::Base
11
+
12
+ self.table_name = '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 => 'Shoppe::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 => 'Shoppe::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