shoppe 0.0.16 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Rakefile +5 -6
- data/app/assets/javascripts/shoppe/application.coffee +19 -0
- data/app/assets/stylesheets/shoppe/application.scss +62 -20
- data/app/assets/stylesheets/shoppe/{chosen.css → chosen.scss} +18 -24
- data/app/assets/stylesheets/shoppe/dialog.scss +17 -2
- data/app/controllers/shoppe/orders_controller.rb +9 -7
- data/app/controllers/shoppe/payments_controller.rb +33 -0
- data/app/controllers/shoppe/settings_controller.rb +4 -0
- data/app/controllers/shoppe/tax_rates_controller.rb +1 -1
- data/app/controllers/shoppe/users_controller.rb +1 -1
- data/app/helpers/shoppe/application_helper.rb +0 -9
- data/app/models/shoppe/country.rb +17 -20
- data/app/models/shoppe/delivery_service.rb +16 -22
- data/app/models/shoppe/delivery_service_price.rb +10 -21
- data/app/models/shoppe/order/actions.rb +80 -0
- data/app/models/shoppe/order/billing.rb +99 -0
- data/app/models/shoppe/order/delivery.rb +196 -0
- data/app/models/shoppe/order/states.rb +69 -0
- data/app/models/shoppe/order.rb +29 -365
- data/app/models/shoppe/order_item.rb +52 -39
- data/app/models/shoppe/payment.rb +80 -0
- data/app/models/shoppe/product/product_attributes.rb +6 -4
- data/app/models/shoppe/product/variants.rb +20 -7
- data/app/models/shoppe/product.rb +37 -37
- data/app/models/shoppe/product_attribute.rb +13 -20
- data/app/models/shoppe/product_category.rb +6 -19
- data/app/models/shoppe/setting.rb +9 -0
- data/app/models/shoppe/stock_level_adjustment.rb +5 -18
- data/app/models/shoppe/tax_rate.rb +18 -21
- data/app/models/shoppe/user.rb +8 -15
- data/app/views/shoppe/delivery_service_prices/_form.html.haml +9 -3
- data/app/views/shoppe/delivery_service_prices/index.html.haml +6 -4
- data/app/views/shoppe/delivery_services/_form.html.haml +1 -1
- data/app/views/shoppe/orders/edit.html.haml +62 -0
- data/app/views/shoppe/orders/index.html.haml +2 -3
- data/app/views/shoppe/orders/show.html.haml +100 -63
- data/app/views/shoppe/payments/refund.html.haml +14 -0
- data/app/views/shoppe/product_categories/_form.html.haml +1 -1
- data/app/views/shoppe/products/_form.html.haml +8 -2
- data/app/views/shoppe/settings/edit.html.haml +1 -1
- data/app/views/shoppe/tax_rates/form.html.haml +5 -4
- data/app/views/shoppe/users/_form.html.haml +1 -1
- data/app/views/shoppe/variants/form.html.haml +2 -2
- data/config/routes.rb +3 -1
- data/db/migrate/20130926094549_create_shoppe_initial_schema.rb +1 -1
- data/db/migrate/20131024201501_add_address_type_to_shoppe_tax_rates.rb +5 -0
- data/db/migrate/20131024204815_create_shoppe_payments.rb +32 -0
- data/db/schema.rb +218 -0
- data/db/seeds.rb +15 -15
- data/lib/shoppe/engine.rb +7 -2
- data/lib/shoppe/errors/refund_failed.rb +15 -0
- data/lib/shoppe/settings.rb +0 -2
- data/lib/shoppe/version.rb +1 -1
- data/lib/shoppe/view_helpers.rb +16 -0
- data/lib/shoppe.rb +23 -6
- data/test/app/db/schema.rb +21 -6
- data/test/app/log/development.log +12782 -0
- data/test/app/tmp/cache/assets/development/sass/edac894564dae62b78e653a08d1c41f10ade93f9/application.scssc +0 -0
- data/test/app/tmp/cache/assets/development/sass/edac894564dae62b78e653a08d1c41f10ade93f9/chosen.scssc +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/01d6eb5fc12044a487be4b93584690f2 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/02d3923383f72b56dd7919e301d22d24 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/0a6bca3e510625f255083bd154cc470b +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/2f80004fb2e2ce07283a83ac15cf920a +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/322295abdd8625fcce4da08f9565cc63 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/4c8cb5cfd87990ebddbaa5b5fd594be5 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/53c0f5159a54836310b1a3f5357bc4c6 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/7938636d16e11b754d4dd046b89863c4 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/7a90d9251a7c5506f33a3c72a224e571 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/9da17bb4868a0b762f8884db45c76ffd +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/a692ba7ed6cff183bb840c2622233c87 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/a9befe910d55141b8ba02d8198b8f966 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/accc4dc17ef18d0b510917a005340da5 +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/b9ad7ea18b7e55c3626a15d1dae142ed +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/c733f1a2fe9d05a3a634ff64a394f64b +0 -0
- data/test/app/tmp/cache/assets/development/sprockets/da76586dcb6d9a408b2cf33307790d66 +0 -0
- metadata +62 -63
- data/README.rdoc +0 -1
@@ -0,0 +1,80 @@
|
|
1
|
+
module Shoppe
|
2
|
+
class Payment < ActiveRecord::Base
|
3
|
+
|
4
|
+
# The associated order
|
5
|
+
#
|
6
|
+
# @return [Shoppe::Order]
|
7
|
+
belongs_to :order, :class_name => 'Shoppe::Order'
|
8
|
+
|
9
|
+
# An associated payment (only applies to refunds)
|
10
|
+
#
|
11
|
+
# @return [Shoppe::Payment]
|
12
|
+
belongs_to :parent, :class_name => "Shoppe::Payment", :foreign_key => "parent_payment_id"
|
13
|
+
|
14
|
+
# Validatiosn
|
15
|
+
validates :amount, :numericality => true
|
16
|
+
validates :reference, :presence => true
|
17
|
+
validates :method, :presence => true
|
18
|
+
|
19
|
+
# Payments can have associated properties
|
20
|
+
key_value_store :properties
|
21
|
+
|
22
|
+
# Callbacks
|
23
|
+
after_create :cache_amount_paid
|
24
|
+
after_destroy :cache_amount_paid
|
25
|
+
before_destroy { self.parent.update_attribute(:amount_refunded, self.parent.amount_refunded + amount) if self.parent }
|
26
|
+
|
27
|
+
# Is this payment a refund?
|
28
|
+
#
|
29
|
+
# @return [Boolean]
|
30
|
+
def refund?
|
31
|
+
self.amount < BigDecimal(0)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Has this payment had any refunds taken from it?
|
35
|
+
#
|
36
|
+
# @return [Boolean]
|
37
|
+
def refunded?
|
38
|
+
self.amount_refunded > BigDecimal(0)
|
39
|
+
end
|
40
|
+
|
41
|
+
# How much of the payment can be refunded
|
42
|
+
#
|
43
|
+
# @return [BigDecimal]
|
44
|
+
def refundable_amount
|
45
|
+
refundable? ? (self.amount - self.amount_refunded) : BigDecimal(0.0)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Process a refund from this payment.
|
49
|
+
#
|
50
|
+
# @param amount [String] the amount which should be refunded
|
51
|
+
# @return [Boolean]
|
52
|
+
def refund!(amount)
|
53
|
+
amount = BigDecimal(amount)
|
54
|
+
if refundable_amount >= amount
|
55
|
+
transaction do
|
56
|
+
self.class.create(:parent => self, :order_id => self.order_id, :amount => 0-amount, :method => self.method, :reference => reference)
|
57
|
+
self.update_attribute(:amount_refunded, self.amount_refunded + amount)
|
58
|
+
true
|
59
|
+
end
|
60
|
+
else
|
61
|
+
raise Shoppe::Errors::RefundFailed, :message => "Refunds must be less than the payment (#{refundable_amount})"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return a transaction URL for viewing further information about this
|
66
|
+
# payment.
|
67
|
+
#
|
68
|
+
# @return [String]
|
69
|
+
def transaction_url
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def cache_amount_paid
|
76
|
+
self.order.update_attribute(:amount_paid, self.order.payments.sum(:amount))
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
module Shoppe
|
2
2
|
class Product < ActiveRecord::Base
|
3
3
|
|
4
|
-
#
|
4
|
+
# Product attributes for this product
|
5
5
|
has_many :product_attributes, -> { order(:position) }, :class_name => 'Shoppe::ProductAttribute'
|
6
6
|
|
7
|
-
#
|
7
|
+
# Used for setting an array of product attributes which will be updated. Usually
|
8
|
+
# received from a web browser.
|
8
9
|
attr_accessor :product_attributes_array
|
9
|
-
|
10
|
-
#
|
10
|
+
|
11
|
+
# After saving automatically try to update the product attributes based on the
|
12
|
+
# the contents of the product_attributes_array array.
|
11
13
|
after_save do
|
12
14
|
return unless product_attributes_array.is_a?(Array)
|
13
15
|
self.product_attributes.update_from_array(product_attributes_array)
|
@@ -1,26 +1,39 @@
|
|
1
1
|
module Shoppe
|
2
2
|
class Product < ActiveRecord::Base
|
3
3
|
|
4
|
-
#
|
4
|
+
# Validations
|
5
|
+
validate { errors.add :base, "can only belong to a root product" if self.parent && self.parent.parent }
|
6
|
+
|
7
|
+
# Variants of the product
|
5
8
|
has_many :variants, -> { order("`default` desc, name asc")}, :class_name => 'Shoppe::Product', :foreign_key => 'parent_id', :dependent => :destroy
|
6
|
-
belongs_to :parent, :class_name => 'Shoppe::Product', :foreign_key => 'parent_id'
|
7
9
|
|
8
|
-
#
|
9
|
-
|
10
|
-
errors.add :base, "can only belong to a root product" if self.parent && self.parent.parent
|
11
|
-
end
|
10
|
+
# The parent product (only applies to variants)
|
11
|
+
belongs_to :parent, :class_name => 'Shoppe::Product', :foreign_key => 'parent_id'
|
12
12
|
|
13
|
-
#
|
13
|
+
# All products which are not variants
|
14
14
|
scope :root, -> { where(:parent_id => nil) }
|
15
15
|
|
16
|
+
# Does this product have any variants?
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
16
19
|
def has_variants?
|
17
20
|
!variants.empty?
|
18
21
|
end
|
19
22
|
|
23
|
+
# Returns the default variant for the product or nil if none exists.
|
24
|
+
#
|
25
|
+
# @return [Shoppe::Product]
|
20
26
|
def default_variant
|
21
27
|
return nil if self.parent
|
22
28
|
@default_variant ||= self.variants.select { |v| v.default? }.first
|
23
29
|
end
|
24
30
|
|
31
|
+
# Is this product a variant of another?
|
32
|
+
#
|
33
|
+
# @return [Boolean]
|
34
|
+
def variant?
|
35
|
+
!self.parent_id.blank?
|
36
|
+
end
|
37
|
+
|
25
38
|
end
|
26
39
|
end
|
@@ -1,47 +1,33 @@
|
|
1
|
-
# == Schema Information
|
2
|
-
#
|
3
|
-
# Table name: shoppe_products
|
4
|
-
#
|
5
|
-
# id :integer not null, primary key
|
6
|
-
# parent_id :integer
|
7
|
-
# product_category_id :integer
|
8
|
-
# name :string(255)
|
9
|
-
# sku :string(255)
|
10
|
-
# permalink :string(255)
|
11
|
-
# description :text
|
12
|
-
# short_description :text
|
13
|
-
# active :boolean default(TRUE)
|
14
|
-
# weight :decimal(8, 3) default(0.0)
|
15
|
-
# price :decimal(8, 2) default(0.0)
|
16
|
-
# cost_price :decimal(8, 2) default(0.0)
|
17
|
-
# tax_rate_id :integer
|
18
|
-
# created_at :datetime
|
19
|
-
# updated_at :datetime
|
20
|
-
# featured :boolean default(FALSE)
|
21
|
-
# in_the_box :text
|
22
|
-
# stock_control :boolean default(TRUE)
|
23
|
-
# default :boolean default(FALSE)
|
24
|
-
#
|
25
|
-
|
26
1
|
module Shoppe
|
27
2
|
class Product < ActiveRecord::Base
|
28
3
|
|
29
|
-
# Set the table name
|
30
4
|
self.table_name = 'shoppe_products'
|
31
5
|
|
32
|
-
#
|
6
|
+
# Add dependencies for products
|
33
7
|
require_dependency 'shoppe/product/product_attributes'
|
34
8
|
require_dependency 'shoppe/product/variants'
|
35
|
-
|
36
|
-
#
|
9
|
+
|
10
|
+
# Products have a default_image and a data_sheet
|
37
11
|
attachment :default_image
|
38
12
|
attachment :data_sheet
|
39
13
|
|
40
|
-
#
|
14
|
+
# The product's category
|
15
|
+
#
|
16
|
+
# @return [Shoppe::ProductCategory]
|
41
17
|
belongs_to :product_category, :class_name => 'Shoppe::ProductCategory'
|
18
|
+
|
19
|
+
# The product's tax rate
|
20
|
+
#
|
21
|
+
# @return [Shoppe::TaxRate]
|
42
22
|
belongs_to :tax_rate, :class_name => "Shoppe::TaxRate"
|
23
|
+
|
24
|
+
# Ordered items which are associated with this product
|
43
25
|
has_many :order_items, :dependent => :restrict_with_exception, :class_name => 'Shoppe::OrderItem', :as => :ordered_item
|
26
|
+
|
27
|
+
# Orders which have ordered this product
|
44
28
|
has_many :orders, :through => :order_items, :class_name => 'Shoppe::Order'
|
29
|
+
|
30
|
+
# Stock level adjustments for this product
|
45
31
|
has_many :stock_level_adjustments, :dependent => :destroy, :class_name => 'Shoppe::StockLevelAdjustment', :as => :item
|
46
32
|
|
47
33
|
# Validations
|
@@ -56,47 +42,59 @@ module Shoppe
|
|
56
42
|
validates :weight, :numericality => true
|
57
43
|
validates :price, :numericality => true
|
58
44
|
validates :cost_price, :numericality => true, :allow_blank => true
|
59
|
-
|
60
|
-
#
|
45
|
+
|
46
|
+
# Before validation, set the permalink if we don't already have one
|
61
47
|
before_validation { self.permalink = self.name.parameterize if self.permalink.blank? && self.name.is_a?(String) }
|
62
48
|
|
63
|
-
#
|
49
|
+
# All active products
|
64
50
|
scope :active, -> { where(:active => true) }
|
51
|
+
|
52
|
+
# All featured products
|
65
53
|
scope :featured, -> {where(:featured => true)}
|
54
|
+
|
55
|
+
# All products ordered with default items first followed by name ascending
|
66
56
|
scope :ordered, -> {order('`default` desc, name asc')}
|
67
57
|
|
68
58
|
# Return the name of the product
|
59
|
+
#
|
60
|
+
# @return [String]
|
69
61
|
def full_name
|
70
62
|
self.parent ? "#{self.parent.name} (#{name})" : name
|
71
63
|
end
|
72
64
|
|
73
|
-
# Is this product
|
65
|
+
# Is this product orderable?
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
74
68
|
def orderable?
|
75
69
|
return false if self.has_variants?
|
76
70
|
true
|
77
71
|
end
|
78
72
|
|
79
|
-
#
|
73
|
+
# The price for the product
|
74
|
+
#
|
75
|
+
# @return [BigDecimal]
|
80
76
|
def price
|
81
77
|
self.default_variant ? self.default_variant.price : read_attribute(:price)
|
82
78
|
end
|
83
79
|
|
84
80
|
# Is this product currently in stock?
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
85
83
|
def in_stock?
|
86
84
|
self.default_variant ? self.default_variant.in_stock? : stock > 0
|
87
85
|
end
|
88
86
|
|
89
87
|
# Return the total number of items currently in stock
|
88
|
+
#
|
89
|
+
# @return [Fixnum]
|
90
90
|
def stock
|
91
91
|
@stock ||= self.stock_level_adjustments.sum(:adjustment)
|
92
92
|
end
|
93
93
|
|
94
|
-
# Specify which attributes can be searched
|
95
94
|
def self.ransackable_attributes(auth_object = nil)
|
96
95
|
["id", "name", "sku"] + _ransackers.keys
|
97
96
|
end
|
98
97
|
|
99
|
-
# Specify which associations can be searched
|
100
98
|
def self.ransackable_associations(auth_object = nil)
|
101
99
|
[]
|
102
100
|
end
|
@@ -106,6 +104,8 @@ module Shoppe
|
|
106
104
|
# For example:
|
107
105
|
#
|
108
106
|
# Shoppe::Product.active.with_attribute('Manufacturer', 'Apple').with_attribute('Model', ['Macbook', 'iPhone'])
|
107
|
+
#
|
108
|
+
# @return [Enumerable]
|
109
109
|
def self.with_attributes(key, values)
|
110
110
|
product_ids = Shoppe::ProductAttribute.searchable.where(:key => key, :value => values).pluck(:product_id).uniq
|
111
111
|
where(:id => product_ids)
|
@@ -1,35 +1,25 @@
|
|
1
|
-
# == Schema Information
|
2
|
-
#
|
3
|
-
# Table name: shoppe_product_attributes
|
4
|
-
#
|
5
|
-
# id :integer not null, primary key
|
6
|
-
# product_id :integer
|
7
|
-
# key :string(255)
|
8
|
-
# value :string(255)
|
9
|
-
# position :integer default(1)
|
10
|
-
# searchable :boolean default(TRUE)
|
11
|
-
# created_at :datetime
|
12
|
-
# updated_at :datetime
|
13
|
-
# public :boolean default(TRUE)
|
14
|
-
#
|
15
|
-
|
16
1
|
module Shoppe
|
17
2
|
class ProductAttribute < ActiveRecord::Base
|
18
3
|
|
19
|
-
# Set the table name
|
20
4
|
self.table_name = 'shoppe_product_attributes'
|
21
|
-
|
5
|
+
|
22
6
|
# Validations
|
23
7
|
validates :key, :presence => true
|
24
8
|
|
25
|
-
#
|
9
|
+
# The associated product
|
10
|
+
#
|
11
|
+
# @return [Shoppe::Product]
|
26
12
|
belongs_to :product, :class_name => 'Shoppe::Product'
|
27
13
|
|
28
|
-
#
|
14
|
+
# All attributes which are searchable
|
29
15
|
scope :searchable, -> { where(:searchable => true) }
|
16
|
+
|
17
|
+
# All attributes which are public
|
30
18
|
scope :public, -> { where(:public => true) }
|
31
19
|
|
32
20
|
# Return the the available options as a hash
|
21
|
+
#
|
22
|
+
# @return [Hash]
|
33
23
|
def self.grouped_hash
|
34
24
|
all.group_by(&:key).inject(Hash.new) do |h, (key, attributes)|
|
35
25
|
h[key] = attributes.map(&:value).uniq
|
@@ -38,7 +28,9 @@ module Shoppe
|
|
38
28
|
end
|
39
29
|
|
40
30
|
# Create/update attributes for a product based on the provided hash of
|
41
|
-
# keys & values
|
31
|
+
# keys & values.
|
32
|
+
#
|
33
|
+
# @param array [Array]
|
42
34
|
def self.update_from_array(array)
|
43
35
|
existing_keys = self.pluck(:key)
|
44
36
|
index = 0
|
@@ -62,6 +54,7 @@ module Shoppe
|
|
62
54
|
end
|
63
55
|
end
|
64
56
|
self.where(:key => existing_keys - array.map { |h| h['key']}).delete_all
|
57
|
+
true
|
65
58
|
end
|
66
59
|
|
67
60
|
end
|
@@ -1,35 +1,22 @@
|
|
1
|
-
# == Schema Information
|
2
|
-
#
|
3
|
-
# Table name: shoppe_product_categories
|
4
|
-
#
|
5
|
-
# id :integer not null, primary key
|
6
|
-
# name :string(255)
|
7
|
-
# permalink :string(255)
|
8
|
-
# description :text
|
9
|
-
# created_at :datetime
|
10
|
-
# updated_at :datetime
|
11
|
-
#
|
12
|
-
|
13
1
|
module Shoppe
|
14
2
|
class ProductCategory < ActiveRecord::Base
|
15
3
|
|
16
|
-
# Set the table name
|
17
4
|
self.table_name = 'shoppe_product_categories'
|
18
5
|
|
19
|
-
#
|
6
|
+
# Categories have an image attachment
|
20
7
|
attachment :image
|
21
8
|
|
22
|
-
#
|
9
|
+
# All products within this category
|
23
10
|
has_many :products, :dependent => :restrict_with_exception, :class_name => 'Shoppe::Product'
|
24
|
-
|
11
|
+
|
25
12
|
# Validations
|
26
13
|
validates :name, :presence => true
|
27
14
|
validates :permalink, :presence => true, :uniqueness => true
|
28
15
|
|
29
|
-
#
|
16
|
+
# All categories ordered by their name ascending
|
30
17
|
scope :ordered, -> { order(:name) }
|
31
|
-
|
32
|
-
# Set the permalink
|
18
|
+
|
19
|
+
# Set the permalink on callback
|
33
20
|
before_validation { self.permalink = self.name.parameterize if self.permalink.blank? && self.name.is_a?(String) }
|
34
21
|
|
35
22
|
end
|
@@ -3,6 +3,7 @@ require 'ostruct'
|
|
3
3
|
module Shoppe
|
4
4
|
class Setting < ActiveRecord::Base
|
5
5
|
|
6
|
+
# Validations
|
6
7
|
validates :key, :presence => true, :uniqueness => true
|
7
8
|
validates :value, :presence => true
|
8
9
|
validates :value_type, :presence => true
|
@@ -13,6 +14,8 @@ module Shoppe
|
|
13
14
|
end
|
14
15
|
|
15
16
|
# The encoded value for saving in the backend (as a string)
|
17
|
+
#
|
18
|
+
# @return [String]
|
16
19
|
def encoded_value
|
17
20
|
case value_type
|
18
21
|
when 'Array', 'Hash' then value.to_json
|
@@ -22,6 +25,8 @@ module Shoppe
|
|
22
25
|
end
|
23
26
|
|
24
27
|
# The decoded value for the setting attribute (in it's native type)
|
28
|
+
#
|
29
|
+
# @return [Object]
|
25
30
|
def decoded_value
|
26
31
|
case value_type
|
27
32
|
when 'Fixnum' then value.to_i
|
@@ -33,6 +38,8 @@ module Shoppe
|
|
33
38
|
end
|
34
39
|
|
35
40
|
# A full hash of all settings available in the current scope
|
41
|
+
#
|
42
|
+
# @return [Hash]
|
36
43
|
def self.to_hash
|
37
44
|
all.inject({}) do |h, setting|
|
38
45
|
h[setting.key.to_s] = setting.decoded_value
|
@@ -42,6 +49,8 @@ module Shoppe
|
|
42
49
|
|
43
50
|
# Update settings from a given hash and persist them. Accepts a
|
44
51
|
# hash of keys (which should be strings).
|
52
|
+
#
|
53
|
+
# @return [Hash]
|
45
54
|
def self.update_from_hash(hash)
|
46
55
|
existing_settings = self.all.to_a
|
47
56
|
hash.each do |key, value|
|
@@ -1,23 +1,10 @@
|
|
1
|
-
# == Schema Information
|
2
|
-
#
|
3
|
-
# Table name: shoppe_stock_level_adjustments
|
4
|
-
#
|
5
|
-
# id :integer not null, primary key
|
6
|
-
# item_id :integer
|
7
|
-
# item_type :string(255)
|
8
|
-
# description :string(255)
|
9
|
-
# adjustment :integer default(0)
|
10
|
-
# parent_type :string(255)
|
11
|
-
# parent_id :integer
|
12
|
-
# created_at :datetime
|
13
|
-
# updated_at :datetime
|
14
|
-
#
|
15
|
-
|
16
1
|
module Shoppe
|
17
2
|
class StockLevelAdjustment < ActiveRecord::Base
|
18
3
|
|
19
|
-
#
|
4
|
+
# The orderable item which the stock level adjustment belongs to
|
20
5
|
belongs_to :item, :polymorphic => true
|
6
|
+
|
7
|
+
# The parent (OrderItem) which the stock level adjustment belongs to
|
21
8
|
belongs_to :parent, :polymorphic => true
|
22
9
|
|
23
10
|
# Validations
|
@@ -25,8 +12,8 @@ module Shoppe
|
|
25
12
|
validates :adjustment, :numericality => true
|
26
13
|
validate { errors.add(:adjustment, "must be greater or less than zero") if adjustment == 0 }
|
27
14
|
|
28
|
-
#
|
29
|
-
scope :ordered, -> { order('
|
15
|
+
# All stock level adjustments ordered by their created date desending
|
16
|
+
scope :ordered, -> { order('id desc') }
|
30
17
|
|
31
18
|
end
|
32
19
|
end
|
@@ -1,45 +1,42 @@
|
|
1
|
-
# == Schema Information
|
2
|
-
#
|
3
|
-
# Table name: shoppe_tax_rates
|
4
|
-
#
|
5
|
-
# id :integer not null, primary key
|
6
|
-
# name :string(255)
|
7
|
-
# rate :decimal(8, 2)
|
8
|
-
# created_at :datetime
|
9
|
-
# updated_at :datetime
|
10
|
-
# country_ids :text
|
11
|
-
#
|
12
|
-
|
13
1
|
module Shoppe
|
14
2
|
class TaxRate < ActiveRecord::Base
|
15
3
|
|
16
|
-
# Set the table name
|
17
4
|
self.table_name = 'shoppe_tax_rates'
|
18
5
|
|
19
|
-
# Tax rates are associated with countries
|
20
6
|
include Shoppe::AssociatedCountries
|
21
7
|
|
8
|
+
# The order address types which may be used when choosing how to apply the tax rate
|
9
|
+
ADDRESS_TYPES = ['billing', 'delivery']
|
10
|
+
|
22
11
|
# Validations
|
23
12
|
validates :name, :presence => true
|
13
|
+
validates :address_type, :inclusion => {:in => ADDRESS_TYPES}
|
24
14
|
validates :rate, :numericality => true
|
25
15
|
|
26
|
-
#
|
16
|
+
# All products which are assigned to this tax rate
|
27
17
|
has_many :products, :dependent => :restrict_with_exception, :class_name => 'Shoppe::Product'
|
18
|
+
|
19
|
+
# All delivery service prices which are assigned to this tax rate
|
28
20
|
has_many :delivery_service_prices, :dependent => :restrict_with_exception, :class_name => 'Shoppe::DeliveryServicePrice'
|
29
21
|
|
30
|
-
#
|
22
|
+
# All tax rates ordered by their ID
|
31
23
|
scope :ordered, -> { order("shoppe_tax_rates.id")}
|
32
24
|
|
25
|
+
# A description of the tax rate including its name & percentage
|
26
|
+
#
|
27
|
+
# @return [String]
|
33
28
|
def description
|
34
29
|
"#{name} (#{rate}%)"
|
35
30
|
end
|
36
31
|
|
32
|
+
# The rate for a given order based on the rules on the tax rate
|
33
|
+
#
|
34
|
+
# @return [BigDecimal]
|
37
35
|
def rate_for(order)
|
38
|
-
if countries.empty?
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
36
|
+
return rate if countries.empty?
|
37
|
+
return rate if address_type == 'billing' && (order.billing_country.nil? || country?(order.billing_country))
|
38
|
+
return rate if address_type == 'delivery' && (order.delivery_country.nil? || country?(order.delivery_country))
|
39
|
+
BigDecimal(0)
|
43
40
|
end
|
44
41
|
|
45
42
|
end
|
data/app/models/shoppe/user.rb
CHANGED
@@ -1,23 +1,8 @@
|
|
1
|
-
# == Schema Information
|
2
|
-
#
|
3
|
-
# Table name: shoppe_users
|
4
|
-
#
|
5
|
-
# id :integer not null, primary key
|
6
|
-
# first_name :string(255)
|
7
|
-
# last_name :string(255)
|
8
|
-
# email_address :string(255)
|
9
|
-
# password_digest :string(255)
|
10
|
-
# created_at :datetime
|
11
|
-
# updated_at :datetime
|
12
|
-
#
|
13
|
-
|
14
1
|
module Shoppe
|
15
2
|
class User < ActiveRecord::Base
|
16
3
|
|
17
|
-
# Set the table name
|
18
4
|
self.table_name = 'shoppe_users'
|
19
5
|
|
20
|
-
# Self explanatory I think!
|
21
6
|
has_secure_password
|
22
7
|
|
23
8
|
# Validations
|
@@ -26,11 +11,15 @@ module Shoppe
|
|
26
11
|
validates :email_address, :presence => true
|
27
12
|
|
28
13
|
# The user's first name & last name concatenated
|
14
|
+
#
|
15
|
+
# @return [String]
|
29
16
|
def full_name
|
30
17
|
"#{first_name} #{last_name}"
|
31
18
|
end
|
32
19
|
|
33
20
|
# The user's first name & initial of last name concatenated
|
21
|
+
#
|
22
|
+
# @return [String]
|
34
23
|
def short_name
|
35
24
|
"#{first_name} #{last_name[0,1]}"
|
36
25
|
end
|
@@ -45,6 +34,10 @@ module Shoppe
|
|
45
34
|
|
46
35
|
# Attempt to authenticate a user based on email & password. Returns the
|
47
36
|
# user if successful otherwise returns false.
|
37
|
+
#
|
38
|
+
# @param email_address [String]
|
39
|
+
# @param paassword [String]
|
40
|
+
# @return [Shoppe::User]
|
48
41
|
def self.authenticate(email_address, password)
|
49
42
|
user = self.where(:email_address => email_address).first
|
50
43
|
return false if user.nil?
|
@@ -4,7 +4,7 @@
|
|
4
4
|
.splitContainer
|
5
5
|
%dl.third
|
6
6
|
%dt= f.label :code
|
7
|
-
%dd= f.text_field :code, :class => 'text'
|
7
|
+
%dd= f.text_field :code, :class => 'focus text'
|
8
8
|
|
9
9
|
%dl.third
|
10
10
|
%dt= f.label :min_weight
|
@@ -17,10 +17,16 @@
|
|
17
17
|
.splitContainer
|
18
18
|
%dl.third
|
19
19
|
%dt= f.label :price
|
20
|
-
%dd
|
20
|
+
%dd
|
21
|
+
.moneyInput
|
22
|
+
.currency= Shoppe.settings.currency_unit.html_safe
|
23
|
+
= f.text_field :price, :class => 'text'
|
21
24
|
%dl.third
|
22
25
|
%dt= f.label :cost_price
|
23
|
-
%dd
|
26
|
+
%dd
|
27
|
+
.moneyInput
|
28
|
+
.currency= Shoppe.settings.currency_unit.html_safe
|
29
|
+
= f.text_field :cost_price, :class => 'text'
|
24
30
|
%dl.third
|
25
31
|
%dt= f.label :tax_rate_id
|
26
32
|
%dd= f.collection_select :tax_rate_id, Shoppe::TaxRate.ordered, :id, :description, {:include_blank => true}, {:class => 'chosen-with-deselect', :data => {:placeholder => "No tax"}}
|
@@ -10,13 +10,15 @@
|
|
10
10
|
%table.data
|
11
11
|
%thead
|
12
12
|
%tr
|
13
|
-
%th Price
|
14
|
-
%th Weight Allowance
|
15
13
|
%th Code
|
14
|
+
%th Weight Allowance
|
15
|
+
%th Price
|
16
|
+
%th Cost
|
16
17
|
%tbody
|
17
18
|
- for price in @delivery_service_prices
|
18
19
|
%tr
|
19
|
-
%td= link_to
|
20
|
+
%td= link_to price.code, [:edit, @delivery_service, price]
|
20
21
|
%td #{number_to_weight price.min_weight} → #{number_to_weight price.max_weight}
|
21
|
-
%td= price.
|
22
|
+
%td= number_to_currency(price.price)
|
23
|
+
%td= number_to_currency price.cost_price
|
22
24
|
|