kkt_shoppe 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +55 -0
- data/Rakefile +36 -0
- data/app/assets/images/kkt_shoppe/chosen-sprite.png +0 -0
- data/app/assets/images/kkt_shoppe/chosen-sprite@2x.png +0 -0
- data/app/assets/images/kkt_shoppe/document.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/bag.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/balance.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/box.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/building.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/chart.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/chat.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/checkbox.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/checkbox2.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/cloud.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/cone.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/credit_card.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/currency.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/edit.svg +14 -0
- data/app/assets/images/kkt_shoppe/icons/flowchart.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/gift.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/globe.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/id.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/id2.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/locked.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/report.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/search.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/support.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/tags.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/toolbox.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/unlocked.svg +1 -0
- data/app/assets/images/kkt_shoppe/icons/wallet.svg +1 -0
- data/app/assets/images/kkt_shoppe/kkt_shoppe.svg +25 -0
- data/app/assets/images/kkt_shoppe/logo.svg +47 -0
- data/app/assets/images/kkt_shoppe/move.svg +1 -0
- data/app/assets/images/kkt_shoppe/square.svg +9 -0
- data/app/assets/images/kkt_shoppe/statuses/accepted.svg +14 -0
- data/app/assets/images/kkt_shoppe/statuses/paid.svg +16 -0
- data/app/assets/images/kkt_shoppe/statuses/received.svg +15 -0
- data/app/assets/images/kkt_shoppe/statuses/rejected.svg +14 -0
- data/app/assets/images/kkt_shoppe/statuses/shipped.svg +14 -0
- data/app/assets/images/kkt_shoppe/table-tear-off.png +0 -0
- data/app/assets/javascripts/kkt_shoppe/application.coffee +123 -0
- data/app/assets/javascripts/kkt_shoppe/chosen.jquery.js +1166 -0
- data/app/assets/javascripts/kkt_shoppe/jquery_ui.js +6 -0
- data/app/assets/javascripts/kkt_shoppe/mousetrap.js +9 -0
- data/app/assets/javascripts/kkt_shoppe/order_form.coffee +47 -0
- data/app/assets/stylesheets/kkt_shoppe/application.scss +601 -0
- data/app/assets/stylesheets/kkt_shoppe/chosen.scss +424 -0
- data/app/assets/stylesheets/kkt_shoppe/dialog.scss +25 -0
- data/app/assets/stylesheets/kkt_shoppe/elements.scss +79 -0
- data/app/assets/stylesheets/kkt_shoppe/printable.scss +67 -0
- data/app/assets/stylesheets/kkt_shoppe/reset.scss +93 -0
- data/app/assets/stylesheets/kkt_shoppe/sub.scss +106 -0
- data/app/assets/stylesheets/kkt_shoppe/variables.scss +1 -0
- data/app/controllers/kkt_shoppe/addresses_controller.rb +47 -0
- data/app/controllers/kkt_shoppe/application_controller.rb +46 -0
- data/app/controllers/kkt_shoppe/attachments_controller.rb +14 -0
- data/app/controllers/kkt_shoppe/countries_controller.rb +47 -0
- data/app/controllers/kkt_shoppe/customers_controller.rb +55 -0
- data/app/controllers/kkt_shoppe/dashboard_controller.rb +9 -0
- data/app/controllers/kkt_shoppe/delivery_service_prices_controller.rb +45 -0
- data/app/controllers/kkt_shoppe/delivery_services_controller.rb +47 -0
- data/app/controllers/kkt_shoppe/orders_controller.rb +119 -0
- data/app/controllers/kkt_shoppe/payments_controller.rb +33 -0
- data/app/controllers/kkt_shoppe/product_categories_controller.rb +47 -0
- data/app/controllers/kkt_shoppe/product_category_localisations_controller.rb +58 -0
- data/app/controllers/kkt_shoppe/product_localisations_controller.rb +58 -0
- data/app/controllers/kkt_shoppe/products_controller.rb +58 -0
- data/app/controllers/kkt_shoppe/sessions_controller.rb +34 -0
- data/app/controllers/kkt_shoppe/settings_controller.rb +16 -0
- data/app/controllers/kkt_shoppe/stock_level_adjustments_controller.rb +40 -0
- data/app/controllers/kkt_shoppe/tax_rates_controller.rb +49 -0
- data/app/controllers/kkt_shoppe/users_controller.rb +53 -0
- data/app/controllers/kkt_shoppe/variants_controller.rb +50 -0
- data/app/helpers/kkt_shoppe/application_helper.rb +60 -0
- data/app/helpers/kkt_shoppe/product_category_helper.rb +36 -0
- data/app/mailers/kkt_shoppe/order_mailer.rb +25 -0
- data/app/mailers/kkt_shoppe/user_mailer.rb +10 -0
- data/app/models/kkt_shoppe/address.rb +44 -0
- data/app/models/kkt_shoppe/country.rb +27 -0
- data/app/models/kkt_shoppe/customer.rb +41 -0
- data/app/models/kkt_shoppe/delivery_service.rb +33 -0
- data/app/models/kkt_shoppe/delivery_service_price.rb +31 -0
- data/app/models/kkt_shoppe/order/actions.rb +94 -0
- data/app/models/kkt_shoppe/order/billing.rb +105 -0
- data/app/models/kkt_shoppe/order/delivery.rb +229 -0
- data/app/models/kkt_shoppe/order/states.rb +69 -0
- data/app/models/kkt_shoppe/order.rb +100 -0
- data/app/models/kkt_shoppe/order_item.rb +239 -0
- data/app/models/kkt_shoppe/payment.rb +86 -0
- data/app/models/kkt_shoppe/product/product_attributes.rb +20 -0
- data/app/models/kkt_shoppe/product/variants.rb +51 -0
- data/app/models/kkt_shoppe/product.rb +189 -0
- data/app/models/kkt_shoppe/product_attribute.rb +66 -0
- data/app/models/kkt_shoppe/product_categorization.rb +14 -0
- data/app/models/kkt_shoppe/product_category.rb +75 -0
- data/app/models/kkt_shoppe/setting.rb +68 -0
- data/app/models/kkt_shoppe/stock_level_adjustment.rb +19 -0
- data/app/models/kkt_shoppe/tax_rate.rb +46 -0
- data/app/models/kkt_shoppe/user.rb +49 -0
- data/app/validators/permalink_validator.rb +7 -0
- data/app/views/kkt_shoppe/addresses/_form.html.haml +33 -0
- data/app/views/kkt_shoppe/addresses/edit.html.haml +5 -0
- data/app/views/kkt_shoppe/addresses/new.html.haml +5 -0
- data/app/views/kkt_shoppe/countries/_form.html.haml +35 -0
- data/app/views/kkt_shoppe/countries/edit.html.haml +6 -0
- data/app/views/kkt_shoppe/countries/index.html.haml +25 -0
- data/app/views/kkt_shoppe/countries/new.html.haml +7 -0
- data/app/views/kkt_shoppe/customers/_addresses.html.haml +20 -0
- data/app/views/kkt_shoppe/customers/_form.html.haml +30 -0
- data/app/views/kkt_shoppe/customers/_search_form.html.haml +13 -0
- data/app/views/kkt_shoppe/customers/edit.html.haml +5 -0
- data/app/views/kkt_shoppe/customers/index.html.haml +32 -0
- data/app/views/kkt_shoppe/customers/new.html.haml +5 -0
- data/app/views/kkt_shoppe/customers/show.html.haml +53 -0
- data/app/views/kkt_shoppe/delivery_service_prices/_form.html.haml +44 -0
- data/app/views/kkt_shoppe/delivery_service_prices/edit.html.haml +6 -0
- data/app/views/kkt_shoppe/delivery_service_prices/index.html.haml +23 -0
- data/app/views/kkt_shoppe/delivery_service_prices/new.html.haml +6 -0
- data/app/views/kkt_shoppe/delivery_services/_form.html.haml +38 -0
- data/app/views/kkt_shoppe/delivery_services/edit.html.haml +9 -0
- data/app/views/kkt_shoppe/delivery_services/index.html.haml +27 -0
- data/app/views/kkt_shoppe/delivery_services/new.html.haml +6 -0
- data/app/views/kkt_shoppe/order_mailer/accepted.text.erb +12 -0
- data/app/views/kkt_shoppe/order_mailer/received.text.erb +9 -0
- data/app/views/kkt_shoppe/order_mailer/rejected.text.erb +10 -0
- data/app/views/kkt_shoppe/order_mailer/shipped.text.erb +16 -0
- data/app/views/kkt_shoppe/orders/_form.html.haml +58 -0
- data/app/views/kkt_shoppe/orders/_order_details.html.haml +57 -0
- data/app/views/kkt_shoppe/orders/_order_items.html.haml +38 -0
- data/app/views/kkt_shoppe/orders/_order_items_form.html.haml +61 -0
- data/app/views/kkt_shoppe/orders/_payments_form.html.haml +15 -0
- data/app/views/kkt_shoppe/orders/_payments_table.html.haml +37 -0
- data/app/views/kkt_shoppe/orders/_search_form.html.haml +24 -0
- data/app/views/kkt_shoppe/orders/_status_bar.html.haml +35 -0
- data/app/views/kkt_shoppe/orders/despatch_note.html.haml +45 -0
- data/app/views/kkt_shoppe/orders/edit.html.haml +21 -0
- data/app/views/kkt_shoppe/orders/index.html.haml +39 -0
- data/app/views/kkt_shoppe/orders/new.html.haml +14 -0
- data/app/views/kkt_shoppe/orders/show.html.haml +25 -0
- data/app/views/kkt_shoppe/payments/refund.html.haml +13 -0
- data/app/views/kkt_shoppe/product_categories/_form.html.haml +50 -0
- data/app/views/kkt_shoppe/product_categories/edit.html.haml +8 -0
- data/app/views/kkt_shoppe/product_categories/index.html.haml +27 -0
- data/app/views/kkt_shoppe/product_categories/new.html.haml +6 -0
- data/app/views/kkt_shoppe/product_category_localisations/form.html.haml +29 -0
- data/app/views/kkt_shoppe/product_category_localisations/index.html.haml +26 -0
- data/app/views/kkt_shoppe/product_localisations/form.html.haml +32 -0
- data/app/views/kkt_shoppe/product_localisations/index.html.haml +26 -0
- data/app/views/kkt_shoppe/products/_form.html.haml +118 -0
- data/app/views/kkt_shoppe/products/_table.html.haml +42 -0
- data/app/views/kkt_shoppe/products/edit.html.haml +9 -0
- data/app/views/kkt_shoppe/products/import.html.haml +67 -0
- data/app/views/kkt_shoppe/products/index.html.haml +9 -0
- data/app/views/kkt_shoppe/products/new.html.haml +7 -0
- data/app/views/kkt_shoppe/sessions/new.html.haml +12 -0
- data/app/views/kkt_shoppe/sessions/reset.html.haml +12 -0
- data/app/views/kkt_shoppe/settings/edit.html.haml +19 -0
- data/app/views/kkt_shoppe/shared/error.html.haml +6 -0
- data/app/views/kkt_shoppe/stock_level_adjustments/index.html.haml +40 -0
- data/app/views/kkt_shoppe/tax_rates/form.html.haml +28 -0
- data/app/views/kkt_shoppe/tax_rates/index.html.haml +17 -0
- data/app/views/kkt_shoppe/user_mailer/new_password.text.erb +9 -0
- data/app/views/kkt_shoppe/users/_form.html.haml +27 -0
- data/app/views/kkt_shoppe/users/edit.html.haml +5 -0
- data/app/views/kkt_shoppe/users/index.html.haml +17 -0
- data/app/views/kkt_shoppe/users/new.html.haml +7 -0
- data/app/views/kkt_shoppe/variants/form.html.haml +68 -0
- data/app/views/kkt_shoppe/variants/index.html.haml +33 -0
- data/app/views/layouts/kkt_shoppe/application.html.haml +34 -0
- data/app/views/layouts/kkt_shoppe/printable.html.haml +11 -0
- data/app/views/layouts/kkt_shoppe/sub.html.haml +10 -0
- data/config/locales/de.yml +653 -0
- data/config/locales/en.yml +744 -0
- data/config/locales/es.yml +653 -0
- data/config/locales/pl.yml +662 -0
- data/config/locales/pt-BR.yml +655 -0
- data/config/routes.rb +53 -0
- data/db/countries.txt +252 -0
- data/db/migrate/20130926094549_create_kkt_shoppe_initial_schema.rb +175 -0
- data/db/migrate/20131024201501_add_address_type_to_kkt_shoppe_tax_rates.rb +5 -0
- data/db/migrate/20131024204815_create_kkt_shoppe_payments.rb +32 -0
- data/db/migrate/20131102143930_remove_default_on_order_item_weight.rb +9 -0
- data/db/migrate/20141013192427_create_kkt_shoppe_customers.rb +14 -0
- data/db/migrate/20141026175622_add_indexes_to_kkt_shoppe_order_items.rb +6 -0
- data/db/migrate/20141026175943_add_indexes_to_kkt_shoppe_orders.rb +7 -0
- data/db/migrate/20141026180333_add_indexes_to_kkt_shoppe_payments.rb +6 -0
- data/db/migrate/20141026180835_add_indexes_to_kkt_shoppe_product_attributes.rb +7 -0
- data/db/migrate/20141026180952_add_indexes_to_kkt_shoppe_product_categories.rb +5 -0
- data/db/migrate/20141026181040_add_indexes_to_kkt_shoppe_products.rb +8 -0
- data/db/migrate/20141026181312_add_indexes_to_kkt_shoppe_settings.rb +5 -0
- data/db/migrate/20141026181354_add_indexes_to_kkt_shoppe_stock_level_adjustments.rb +6 -0
- data/db/migrate/20141026181559_add_indexes_to_kkt_shoppe_users.rb +5 -0
- data/db/migrate/20141026181716_add_indexes_to_kkt_shoppe_delivery_services.rb +9 -0
- data/db/migrate/20141026181717_allow_multiple_kkt_shoppe_products_per_shoppe_product_category.rb +46 -0
- data/db/migrate/20141026181718_add_nested_to_product_categories.rb +30 -0
- data/db/migrate/20141027215005_create_kkt_shoppe_addresses.rb +17 -0
- data/db/migrate/20150315215633_add_customer_to_kkt_shoppe_orders.rb +5 -0
- data/db/migrate/20150513171350_create_kkt_shoppe_product_category_translation_table.rb +17 -0
- data/db/migrate/20150519173350_create_kkt_shoppe_product_translation_table.rb +18 -0
- data/db/schema.rb +308 -0
- data/db/seeds.rb +137 -0
- data/db/seeds_data/poe400.jpg +0 -0
- data/db/seeds_data/snom-870-blk.jpg +0 -0
- data/db/seeds_data/snom-870-grey.jpg +0 -0
- data/db/seeds_data/snom-mm2.jpg +0 -0
- data/db/seeds_data/spa303.jpg +0 -0
- data/db/seeds_data/t18p.jpg +0 -0
- data/db/seeds_data/t20p.jpg +0 -0
- data/db/seeds_data/t22p.jpg +0 -0
- data/db/seeds_data/t26p.jpg +0 -0
- data/db/seeds_data/t41pn.jpg +0 -0
- data/db/seeds_data/t46gn.jpg +0 -0
- data/db/seeds_data/w52p.jpg +0 -0
- data/db/seeds_data/yhs32.jpg +0 -0
- data/lib/kkt_shoppe/associated_countries.rb +20 -0
- data/lib/kkt_shoppe/country_importer.rb +15 -0
- data/lib/kkt_shoppe/default_navigation.rb +21 -0
- data/lib/kkt_shoppe/engine.rb +55 -0
- data/lib/kkt_shoppe/error.rb +21 -0
- data/lib/kkt_shoppe/errors/inappropriate_delivery_service.rb +6 -0
- data/lib/kkt_shoppe/errors/insufficient_stock_to_fulfil.rb +15 -0
- data/lib/kkt_shoppe/errors/invalid_configuration.rb +6 -0
- data/lib/kkt_shoppe/errors/not_enough_stock.rb +15 -0
- data/lib/kkt_shoppe/errors/payment_declined.rb +6 -0
- data/lib/kkt_shoppe/errors/refund_failed.rb +6 -0
- data/lib/kkt_shoppe/errors/unorderable_item.rb +6 -0
- data/lib/kkt_shoppe/navigation_manager.rb +81 -0
- data/lib/kkt_shoppe/orderable_item.rb +39 -0
- data/lib/kkt_shoppe/settings.rb +26 -0
- data/lib/kkt_shoppe/settings_loader.rb +16 -0
- data/lib/kkt_shoppe/setup_generator.rb +10 -0
- data/lib/kkt_shoppe/version.rb +3 -0
- data/lib/kkt_shoppe/view_helpers.rb +16 -0
- data/lib/kkt_shoppe.rb +60 -0
- data/lib/tasks/kylekthompson_shoppe.rake +29 -0
- 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} ↳ #{child.name} (#{t('kkt_shoppe.product_category.nesting.current_category')})"
|
19
|
+
else
|
20
|
+
s << "#{nested_product_category_spacing_adjusted_for_depth child, relative_depth} ↳ #{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} ↳ #{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,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
|