glysellin 0.4.1 → 0.4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/app/helpers/glysellin/cart_helper.rb +4 -4
- data/app/helpers/glysellin/products_helper.rb +19 -0
- data/app/models/glysellin/{order_item.rb → line_item.rb} +17 -7
- data/app/models/glysellin/order.rb +2 -2
- data/app/models/glysellin/product.rb +12 -114
- data/app/models/glysellin/product_property.rb +10 -3
- data/app/models/glysellin/product_property_type.rb +6 -4
- data/app/models/glysellin/variant.rb +25 -23
- data/app/views/glysellin/cart/_added_to_cart_warning.html.erb +11 -4
- data/app/views/glysellin/cart/_products.html.erb +1 -0
- data/app/views/glysellin/products/_add_to_cart.html.haml +13 -0
- data/config/locales/glysellin.en.yml +5 -4
- data/config/locales/glysellin.fr.yml +3 -0
- data/db/migrate/20130609013900_add_sellable_references_to_glysellin_products.rb +6 -0
- data/db/migrate/20130609023300_remove_unused_fields_from_glysellin_products.rb +18 -0
- data/db/migrate/20130609025800_remove_taxonomies_from_system.rb +21 -0
- data/db/migrate/20130609040600_rename_order_items_to_line_items.rb +6 -0
- data/db/migrate/20130609142100_add_variant_id_to_glysellin_line_items.rb +5 -0
- data/db/migrate/20130609155600_make_variants_belong_to_sellables_and_not_products.rb +16 -0
- data/db/migrate/20130609193600_drop_product_types.rb +22 -0
- data/db/migrate/20130609200000_remove_unused_product_properties_columns.rb +15 -0
- data/db/migrate/20130609200700_remove_position_from_glysellin_variants.rb +9 -0
- data/lib/glysellin.rb +0 -1
- data/lib/glysellin/engine.rb +7 -0
- data/lib/glysellin/engine/routes.rb +0 -11
- data/lib/glysellin/property_finder.rb +2 -2
- data/lib/glysellin/sellable.rb +69 -0
- data/lib/glysellin/sellable/multi.rb +24 -0
- data/lib/glysellin/sellable/simple.rb +30 -0
- data/lib/glysellin/version.rb +1 -1
- data/lib/tasks/glysellin_tasks.rake +1 -1
- metadata +16 -13
- data/app/controllers/glysellin/products_controller.rb +0 -12
- data/app/models/glysellin/product_type.rb +0 -11
- data/app/models/glysellin/taxonomy.rb +0 -35
- data/app/views/glysellin/products/_add_to_cart.html.erb +0 -16
- data/app/views/glysellin/products/_item.html.erb +0 -21
- data/app/views/glysellin/products/filter.html.erb +0 -0
- data/app/views/glysellin/products/index.html.erb +0 -3
- data/app/views/glysellin/products/show.html.erb +0 -3
- data/lib/glysellin/acts_as_sellable.rb +0 -14
- data/lib/glysellin/product_methods.rb +0 -66
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OGJiYTNiMzg1M2E3MTAxZjkyMzJkMjQzMWUxY2MwYzg3NDg2NTY1Zg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Zjg4ZTQ2ZGQzMjk1ODNiMmFkZDAwZDg4ZTllZDhiYjE4ZjRhMjk1OA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MTU5ZjJhNTcxZTNiNTJkZmEwODQwNDczMTZlZWM1M2MwZjI5MTk5OWJkMjMx
|
10
|
+
MTE0OTRiZjU0ZTBlNWIwNzY4NWQyYzU4YTNlNzllNmYwMDI1NzkwMjY3NjY0
|
11
|
+
YzBjZGUxZDgzY2NjOTc5MTBkN2VhZjVmYjExYTFkNDNjZDZjNmI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OGMwZDVjNDk1ZGU0NzAyNDM5YzBmYzlmMzZkOTBmMTlmYTY1NjBlZGZlODRl
|
14
|
+
YmZjNTc2Y2ZkODc0MTVhOWY0NDU5NzVkNmIzNmNmZThmZWE3NjJkZjU5YTEy
|
15
|
+
NWJlZGRiOWU2YmVkZDRmZGYyNGVjNmE5NzljNDA5YjZiMGJlOGM=
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module Glysellin
|
2
2
|
module CartHelper
|
3
|
-
def add_to_cart_form
|
3
|
+
def add_to_cart_form sellable, options = {}
|
4
4
|
# Default to remote form
|
5
5
|
options[:remote] = true unless options[:remote] == false
|
6
6
|
# Render actual form
|
7
7
|
render partial: 'glysellin/products/add_to_cart', locals: {
|
8
|
-
|
8
|
+
sellable: sellable,
|
9
9
|
options: options
|
10
10
|
}
|
11
11
|
end
|
@@ -14,8 +14,8 @@ module Glysellin
|
|
14
14
|
render partial: 'glysellin/cart/added_to_cart_warning'
|
15
15
|
end
|
16
16
|
|
17
|
-
def render_cart
|
18
|
-
render partial: "glysellin/cart/cart", locals: { cart:
|
17
|
+
def render_cart
|
18
|
+
render partial: "glysellin/cart/cart", locals: { cart: current_cart }
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -1,4 +1,23 @@
|
|
1
1
|
module Glysellin
|
2
2
|
module ProductsHelper
|
3
|
+
def variants_options_for sellable
|
4
|
+
# Prepare ou arguments arrays
|
5
|
+
all_variants, disabled_variants = [], []
|
6
|
+
# Iterate on each variant to find which are enabled and disabled
|
7
|
+
sellable.published_variants.each do |variant|
|
8
|
+
next unless variant.published?
|
9
|
+
if variant.in_stock?
|
10
|
+
name = variant.name
|
11
|
+
else
|
12
|
+
disabled_variants << variant.id
|
13
|
+
name = variant.name +
|
14
|
+
" (#{ t('glysellin.labels.sellables.out_of_stock') })"
|
15
|
+
end
|
16
|
+
|
17
|
+
all_variants << [name, variant.id]
|
18
|
+
end
|
19
|
+
|
20
|
+
options_for_select(all_variants, disabled: disabled_variants)
|
21
|
+
end
|
3
22
|
end
|
4
23
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module Glysellin
|
2
|
-
class
|
3
|
-
self.table_name =
|
2
|
+
class LineItem < ActiveRecord::Base
|
3
|
+
self.table_name = "glysellin_line_items"
|
4
4
|
belongs_to :order, inverse_of: :products
|
5
5
|
|
6
|
+
belongs_to :variant, class_name: "Glysellin::Variant"
|
7
|
+
|
6
8
|
attr_accessible :sku, :name, :eot_price, :vat_rate, :bundle, :price,
|
7
|
-
:quantity, :weight
|
9
|
+
:quantity, :weight, :variant_id
|
8
10
|
|
9
11
|
# The attributes we getch from a product to build our order item
|
10
12
|
PRODUCT_ATTRIBUTES_FOR_ITEM = %w(sku name eot_price vat_rate price weight)
|
@@ -15,15 +17,19 @@ module Glysellin
|
|
15
17
|
# @param [String] id The id string of the item
|
16
18
|
# @param [Boolean] bundle If it's a bundle or just one product
|
17
19
|
#
|
18
|
-
# @return [
|
20
|
+
# @return [LineItem] The created order item
|
21
|
+
#
|
19
22
|
def build_from_product id, quantity
|
20
|
-
|
23
|
+
variant = Glysellin::Variant.find_by_id(id)
|
21
24
|
|
22
25
|
attrs = PRODUCT_ATTRIBUTES_FOR_ITEM.map do |key|
|
23
|
-
[key,
|
26
|
+
[key, variant.public_send(key)]
|
24
27
|
end
|
25
28
|
|
26
|
-
|
29
|
+
self.new Hash[attrs].merge(
|
30
|
+
"quantity" => quantity,
|
31
|
+
"variant_id" => variant.id
|
32
|
+
)
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
@@ -34,5 +40,9 @@ module Glysellin
|
|
34
40
|
def total_price
|
35
41
|
quantity * price
|
36
42
|
end
|
43
|
+
|
44
|
+
def sellable
|
45
|
+
variant && variant.product && variant.product.sellable
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
@@ -27,7 +27,7 @@ module Glysellin
|
|
27
27
|
#
|
28
28
|
# Order products are used to map order to cloned and simplified products
|
29
29
|
# so the Order propererties can't be affected by product updates
|
30
|
-
has_many :products, class_name: 'Glysellin::
|
30
|
+
has_many :products, class_name: 'Glysellin::LineItem',
|
31
31
|
foreign_key: 'order_id', dependent: :destroy
|
32
32
|
|
33
33
|
# The actual buyer
|
@@ -158,7 +158,7 @@ module Glysellin
|
|
158
158
|
|
159
159
|
def products=(attributes)
|
160
160
|
products = attributes.reduce([]) do |list, product|
|
161
|
-
item =
|
161
|
+
item = LineItem.build_from_product(product[:id], product[:quantity])
|
162
162
|
item ? (list << item) : list
|
163
163
|
end
|
164
164
|
|
@@ -3,134 +3,32 @@ require 'friendly_id'
|
|
3
3
|
|
4
4
|
module Glysellin
|
5
5
|
class Product < ActiveRecord::Base
|
6
|
-
include ProductMethods
|
7
|
-
extend FriendlyId
|
8
|
-
|
9
|
-
friendly_id :name, use: :slugged
|
10
6
|
|
11
7
|
self.table_name = 'glysellin_products'
|
12
8
|
|
13
|
-
|
14
|
-
#
|
15
|
-
# The ProductImage model is used for products and bundles with the same
|
16
|
-
has_many :images, as: :imageable, class_name: 'Glysellin::ProductImage',
|
17
|
-
inverse_of: :imageable, dependent: :destroy
|
18
|
-
|
19
|
-
# Can have multiple taxonomies
|
20
|
-
has_and_belongs_to_many :taxonomies, :class_name => 'Glysellin::Taxonomy',
|
21
|
-
join_table: 'glysellin_products_taxonomies', :foreign_key => 'product_id'
|
22
|
-
|
23
|
-
# N..N relation between bundles and products
|
24
|
-
# has_many :bundle_products, class_name: 'Glysellin::BundleProduct',
|
25
|
-
# foreign_key: 'product_id'
|
26
|
-
# has_many :bundles, class_name: 'Glysellin::Product',
|
27
|
-
# through: :bundle_products
|
28
|
-
|
29
|
-
# # Bundled products in current_product
|
30
|
-
# has_many :product_bundles, class_name: 'Glysellin::BundleProduct',
|
31
|
-
# foreign_key: 'bundle_id'
|
32
|
-
# has_many :bundled_products, class_name: 'Glysellin::Product',
|
33
|
-
# through: :product_bundles
|
9
|
+
belongs_to :sellable, polymorphic: true, inverse_of: :product
|
34
10
|
|
35
11
|
# Products can belong to a brand
|
36
|
-
belongs_to :brand, :
|
37
|
-
|
38
|
-
belongs_to :product_type
|
39
|
-
|
40
|
-
has_many :variants, class_name: 'Glysellin::Variant',
|
41
|
-
before_add: :set_product_on_variant, dependent: :destroy
|
42
|
-
|
43
|
-
accepts_nested_attributes_for :images, allow_destroy: true, reject_if: :all_blank
|
44
|
-
accepts_nested_attributes_for :variants, allow_destroy: true, reject_if: :all_blank
|
45
|
-
|
46
|
-
# accepts_nested_attributes_for :bundled_products, allow_destroy: true, reject_if: :all_blank
|
47
|
-
|
48
|
-
attr_accessible :description, :name, :sku, :slug, :vat_rate,
|
49
|
-
:brand, :taxonomies, :images, :published,
|
50
|
-
:display_priority, :images_attributes, :taxonomy_ids, :unlimited_stock,
|
51
|
-
:position, :brand_id, :variants_attributes, :variants, :product_type_id
|
52
|
-
|
53
|
-
# :bundled_products_attributes
|
54
|
-
|
55
|
-
# Validations
|
56
|
-
#
|
57
|
-
validates_presence_of :name, :slug
|
58
|
-
# Validates price related attributes only unless we have bundled products
|
59
|
-
# so we can defer validations to them
|
60
|
-
validates :vat_rate, presence: true,
|
61
|
-
numericality: true # , unless: proc { |p| p.bundled_products.length > 0 }
|
62
|
-
|
63
|
-
# We check presence of sku if set in global config
|
64
|
-
validates :sku, presence: true, if: proc { Glysellin.autoset_sku }
|
12
|
+
belongs_to :brand, class_name: "Glysellin::Brand", inverse_of: :products
|
65
13
|
|
66
|
-
|
67
|
-
#
|
68
|
-
before_validation :set_sku, :set_vat_rate, :ensure_variant
|
14
|
+
attr_accessible :vat_rate, :brand, :brand_id
|
69
15
|
|
70
|
-
|
71
|
-
scope :with_taxonomy, lambda { |*taxonomies|
|
72
|
-
# Ensure we only have slugs so we got a string vector
|
73
|
-
taxonomies.map! { |t| t.kind_of?(Glysellin::Taxonomy) ? t.slug : t }
|
74
|
-
# Get products with those taxonomies
|
75
|
-
includes(:taxonomies).where('glysellin_taxonomies.slug IN (?)', taxonomies)
|
76
|
-
}
|
16
|
+
validates :vat_rate, presence: true, numericality: true
|
77
17
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
# And as for the validation, if the SKU is configured to be autoset,
|
83
|
-
# we check generate it
|
84
|
-
def set_sku
|
85
|
-
unless (sku && sku.length > 0) || !Glysellin.autoset_sku
|
86
|
-
self.sku = generate_sku
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def set_vat_rate
|
91
|
-
if !vat_rate
|
92
|
-
self.vat_rate = Glysellin.default_vat_rate
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def ensure_variant
|
97
|
-
variants.build(product: self) unless variants.length > 0
|
18
|
+
def name
|
19
|
+
sellable && sellable.name
|
98
20
|
end
|
99
21
|
|
100
|
-
|
101
|
-
|
102
|
-
def available_variants
|
103
|
-
variants.available
|
22
|
+
def vat_rate
|
23
|
+
super.presence || Glysellin.default_vat_rate
|
104
24
|
end
|
105
25
|
|
106
|
-
def
|
107
|
-
|
26
|
+
def vat_ratio
|
27
|
+
1 + vat_rate / 100
|
108
28
|
end
|
109
29
|
|
110
|
-
def
|
111
|
-
|
30
|
+
def variants
|
31
|
+
sellable.published_variants
|
112
32
|
end
|
113
|
-
|
114
|
-
def set_product_on_variant variant
|
115
|
-
variant.product = self
|
116
|
-
end
|
117
|
-
|
118
|
-
# bundle_attribute :price do |product|
|
119
|
-
# product.bundled_products.reduce(0) do |total, product|
|
120
|
-
# total + product.price
|
121
|
-
# end
|
122
|
-
# end
|
123
|
-
|
124
|
-
# bundle_attribute :eot_price do |product, att|
|
125
|
-
# product.bundled_products.reduce(0) do |total, product|
|
126
|
-
# total + product.eot_price
|
127
|
-
# end
|
128
|
-
# end
|
129
|
-
|
130
|
-
# bundle_attribute :vat_rate do |product|
|
131
|
-
# product.bundled_products.reduce(0) do |total, product|
|
132
|
-
# total + (product.vat_rate * product.price)
|
133
|
-
# end / product.price
|
134
|
-
# end
|
135
33
|
end
|
136
34
|
end
|
@@ -1,10 +1,13 @@
|
|
1
1
|
module Glysellin
|
2
2
|
class ProductProperty < ActiveRecord::Base
|
3
3
|
self.table_name = 'glysellin_product_properties'
|
4
|
-
attr_accessible :
|
4
|
+
attr_accessible :value, :variant_id, :type_id
|
5
5
|
|
6
|
-
belongs_to :variant,
|
7
|
-
|
6
|
+
belongs_to :variant, class_name: "Glysellin::Variant",
|
7
|
+
inverse_of: :properties
|
8
|
+
|
9
|
+
belongs_to :type, class_name: 'Glysellin::ProductPropertyType',
|
10
|
+
inverse_of: :properties
|
8
11
|
|
9
12
|
# validate :check_uniqueness_of_type
|
10
13
|
|
@@ -13,6 +16,10 @@ module Glysellin
|
|
13
16
|
|
14
17
|
private
|
15
18
|
|
19
|
+
def name
|
20
|
+
type && type.name
|
21
|
+
end
|
22
|
+
|
16
23
|
def check_uniqueness_of_type
|
17
24
|
if self.variant.properties.map(&:type).include? self.type
|
18
25
|
errors.add(:type, I18n.t("glysellin.controllers.errors.double_property"))
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module Glysellin
|
2
2
|
class ProductPropertyType < ActiveRecord::Base
|
3
3
|
self.table_name = 'glysellin_product_property_types'
|
4
|
-
attr_accessible :
|
4
|
+
attr_accessible :name
|
5
5
|
|
6
|
-
has_many :properties, class_name: "Glysellin::ProductProperty"
|
6
|
+
has_many :properties, class_name: "Glysellin::ProductProperty",
|
7
|
+
foreign_key: 'type_id', inverse_of: :type
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
def name
|
10
|
+
"hoo"
|
11
|
+
end
|
10
12
|
end
|
11
13
|
end
|
@@ -2,7 +2,6 @@ require 'friendly_id'
|
|
2
2
|
|
3
3
|
module Glysellin
|
4
4
|
class Variant < ActiveRecord::Base
|
5
|
-
include ProductMethods
|
6
5
|
extend FriendlyId
|
7
6
|
|
8
7
|
friendly_id :name, use: :slugged
|
@@ -10,24 +9,28 @@ module Glysellin
|
|
10
9
|
self.table_name = 'glysellin_variants'
|
11
10
|
|
12
11
|
attr_accessible :eot_price, :in_stock, :name, :position, :price,
|
13
|
-
:published, :sku, :slug, :unlimited_stock, :
|
14
|
-
:
|
12
|
+
:published, :sku, :slug, :unlimited_stock, :sellable_id,
|
13
|
+
:sellable_type, :properties_attributes, :weight,
|
14
|
+
:unmarked_price
|
15
15
|
|
16
|
-
belongs_to :
|
17
|
-
foreign_key: 'product_id'
|
16
|
+
belongs_to :sellable, polymorphic: true
|
18
17
|
|
19
18
|
has_many :properties, class_name: 'Glysellin::ProductProperty',
|
20
|
-
|
19
|
+
extend: Glysellin::PropertyFinder, dependent: :destroy,
|
20
|
+
inverse_of: :variant
|
21
21
|
|
22
22
|
accepts_nested_attributes_for :properties, allow_destroy: true
|
23
23
|
|
24
|
-
validates_presence_of :name, if: proc { |
|
24
|
+
validates_presence_of :name, if: proc { |variant|
|
25
|
+
variant.sellable.variants.length > 1
|
26
|
+
}
|
27
|
+
|
25
28
|
validates_numericality_of :price
|
26
29
|
validates_numericality_of :in_stock, if: proc { |v| v.in_stock.presence }
|
27
30
|
|
28
31
|
before_validation :check_prices
|
29
32
|
|
30
|
-
after_initialize :prepare_properties
|
33
|
+
# after_initialize :prepare_properties
|
31
34
|
|
32
35
|
AVAILABLE_QUERY = <<-SQL
|
33
36
|
glysellin_variants.published = ? AND (
|
@@ -37,25 +40,24 @@ module Glysellin
|
|
37
40
|
SQL
|
38
41
|
|
39
42
|
scope :available, where(AVAILABLE_QUERY, true, true, 0)
|
43
|
+
scope :published, where(published: true)
|
40
44
|
|
41
|
-
def prepare_properties
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
45
|
+
# def prepare_properties
|
46
|
+
# if product && product.product_type
|
47
|
+
# product.product_type.property_types.each do |type|
|
48
|
+
# properties.build(type: type) if properties.send(type.name) == false
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
# end
|
48
52
|
|
49
53
|
def description
|
50
|
-
|
54
|
+
sellable.description if sellable.respond_to?(:description)
|
51
55
|
end
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
end
|
57
|
+
delegate :vat_rate, :vat_ratio, to: :sellable
|
58
|
+
|
56
59
|
|
57
60
|
def check_prices
|
58
|
-
vat_ratio = self.product.vat_ratio rescue Glysellin.default_vat_rate
|
59
61
|
# If we have to fill one of the prices when changed
|
60
62
|
if eot_changed_alone?
|
61
63
|
self.price = (self.eot_price * vat_ratio).round(2)
|
@@ -84,12 +86,12 @@ module Glysellin
|
|
84
86
|
end
|
85
87
|
|
86
88
|
def name fullname = true
|
87
|
-
variant_name,
|
89
|
+
variant_name, sellable_name = super().presence, (sellable && sellable.name)
|
88
90
|
|
89
91
|
if fullname
|
90
|
-
variant_name ? "#{
|
92
|
+
variant_name ? "#{ sellable_name } - #{ variant_name }" : sellable_name
|
91
93
|
else
|
92
|
-
variant_name ? variant_name :
|
94
|
+
variant_name ? variant_name : sellable_name
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
@@ -2,12 +2,19 @@
|
|
2
2
|
To implement custom behavior, just replace this view, keeping the .added-to-cart-warning container
|
3
3
|
Default behavior is triggered when [data-warning] attribute is present on the container
|
4
4
|
%>
|
5
|
-
|
5
|
+
|
6
|
+
<% product = current_cart.product(params[:cart][:product_id]).variant %>
|
7
|
+
|
8
|
+
<div class="added-to-cart-warning" style="position:fixed;width:400px;height:200px;top:50%;left:50%;margin-left:-200px;margin-top:-140px;border:1px solid #CCC;border-radius:2px;padding:40px;box-shadow:0 0 15px #DDD;z-index:100;background:white;" data-warning>
|
6
9
|
<h3><%= t('glysellin.labels.cart.added_to_cart') %></h3>
|
7
10
|
<hr>
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
+
<strong>
|
12
|
+
<%= (quantity = params[:cart][:quantity].to_i) %> ×
|
13
|
+
</strong>
|
14
|
+
<%= product.name %> - <%= number_to_currency product.price %>
|
15
|
+
<strong style="float:right;">
|
16
|
+
<%= number_to_currency (product.price * quantity) %>
|
17
|
+
</strong>
|
11
18
|
<br>
|
12
19
|
|
13
20
|
<hr>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
- if sellable.published_variants.length > 0
|
2
|
+
= form_tag cart_products_path, class: 'add-to-cart-form', remote: options[:remote], data: { "add-to-cart-form" => true } do
|
3
|
+
.product-quantity
|
4
|
+
= number_field :cart, :quantity, value: 1
|
5
|
+
|
6
|
+
.submit-container
|
7
|
+
- if sellable.published_variants.length > 1
|
8
|
+
= select_tag 'cart[product_id]', variants_options_for(sellable), prompt: t("glysellin.labels.sellables.choose_your_variant"), required: "required"
|
9
|
+
- else
|
10
|
+
= hidden_field_tag 'cart[product_id]', sellable.published_variants.first.id
|
11
|
+
|
12
|
+
%button.submit{ type: "submit" }
|
13
|
+
= t('glysellin.labels.cart.add_to_cart')
|
@@ -78,6 +78,7 @@ en:
|
|
78
78
|
back_to_shop: "Back to shop"
|
79
79
|
remove_from_cart: "Delete"
|
80
80
|
update: "Update cart"
|
81
|
+
added_to_cart: "Product added to cart"
|
81
82
|
update_discount: "Update discount code"
|
82
83
|
create_order_form: "Submit order"
|
83
84
|
follow: "Go to order"
|
@@ -86,12 +87,12 @@ en:
|
|
86
87
|
informations: "Your informations"
|
87
88
|
shipping_method: "Shipping method"
|
88
89
|
payment_method: "Payment method"
|
90
|
+
sellables:
|
91
|
+
out_of_stock: "Out of stock"
|
92
|
+
choose_your_variant: "--- Choose your product ---"
|
89
93
|
orders:
|
90
94
|
states:
|
91
|
-
|
92
|
-
filling_address: "Addresses not filled"
|
93
|
-
address: "Addresses filled"
|
94
|
-
payment: "Payment method chosen"
|
95
|
+
ready: "Payment pending"
|
95
96
|
paid: "Paid"
|
96
97
|
shipped: "Shipped"
|
97
98
|
cart:
|
@@ -97,6 +97,9 @@ fr:
|
|
97
97
|
informations: "Vos informations"
|
98
98
|
shipping_method: "Mode de livraison"
|
99
99
|
payment_method: "Mode de paiement"
|
100
|
+
sellables:
|
101
|
+
out_of_stock: "Épuisé"
|
102
|
+
choose_your_variant: "--- Choisissez votre produit ---"
|
100
103
|
orders:
|
101
104
|
states:
|
102
105
|
ready: "Paiement en attente"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class RemoveUnusedFieldsFromGlysellinProducts < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
remove_column :glysellin_products, :name, :description, :display_priority,
|
4
|
+
:position, :slug, :sku, :published
|
5
|
+
end
|
6
|
+
|
7
|
+
def down
|
8
|
+
change_table :glysellin_products do |t|
|
9
|
+
t.string :name
|
10
|
+
t.text :description
|
11
|
+
t.integer :display_priority
|
12
|
+
t.integer :position
|
13
|
+
t.string :slug
|
14
|
+
t.string :sku
|
15
|
+
t.boolean :published, default: true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class RemoveTaxonomiesFromSystem < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
drop_table :glysellin_taxonomies
|
4
|
+
drop_table :glysellin_products_taxonomies
|
5
|
+
end
|
6
|
+
|
7
|
+
def down
|
8
|
+
create_table :glysellin_taxonomies do |t|
|
9
|
+
t.string :name
|
10
|
+
t.string :slug
|
11
|
+
t.integer :parent_taxonomy_id
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :glysellin_products_taxonomies, :id => false do |t|
|
17
|
+
t.integer :product_id
|
18
|
+
t.integer :taxonomy_id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class MakeVariantsBelongToSellablesAndNotProducts < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
change_table :glysellin_variants do |t|
|
4
|
+
t.integer :sellable_id
|
5
|
+
t.string :sellable_type
|
6
|
+
t.remove :product_id
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def down
|
11
|
+
change_table :glysellin_variants do |t|
|
12
|
+
t.integer :product_id
|
13
|
+
t.remove :sellable_id, :sellable_type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class DropProductTypes < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
drop_table :glysellin_product_types
|
4
|
+
drop_table :glysellin_product_types_properties
|
5
|
+
|
6
|
+
remove_column :glysellin_products, :product_type_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def down
|
10
|
+
create_table :glysellin_product_types_properties, id: false do |t|
|
11
|
+
t.references :product_type, :product_property
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :glysellin_product_types do |t|
|
15
|
+
t.string :name
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
|
19
|
+
add_column :glysellin_products, :product_type_id, :integer
|
20
|
+
add_column :glysellin_product_types_properties, :product_type_id, :integer
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class RemoveUnusedProductPropertiesColumns < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
change_table :glysellin_product_properties do |t|
|
4
|
+
t.remove :name, :adjustement, :variant_type
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def down
|
9
|
+
change_table :glysellin_product_properties do |t|
|
10
|
+
t.string :name
|
11
|
+
t.decimal :adjustement, :precision => 11, :scale => 2
|
12
|
+
t.string :variant_type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/glysellin.rb
CHANGED
@@ -5,7 +5,6 @@ require "glysellin/engine"
|
|
5
5
|
require "glysellin/helpers"
|
6
6
|
require "glysellin/gateway"
|
7
7
|
require "glysellin/discount_type_calculator"
|
8
|
-
require "glysellin/product_methods"
|
9
8
|
require "glysellin/property_finder"
|
10
9
|
require "glysellin/products_list"
|
11
10
|
require "glysellin/shipping_carrier"
|
data/lib/glysellin/engine.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'glysellin/helpers'
|
2
|
+
require 'glysellin/sellable'
|
2
3
|
require 'glysellin/engine/routes'
|
3
4
|
|
4
5
|
module Glysellin
|
@@ -10,5 +11,11 @@ module Glysellin
|
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
14
|
+
initializer "Mix acts_as_sellable into ActiveRecord::Base" do
|
15
|
+
ActiveSupport.on_load :active_record do
|
16
|
+
ActiveRecord::Base.send(:include, Sellable)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
13
20
|
end
|
14
21
|
end
|
@@ -18,16 +18,6 @@ module ActionDispatch::Routing
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
resources :products, controller: controllers[:products] do
|
22
|
-
collection do
|
23
|
-
get 'taxonomy/:id', action: "filter", as: "filter"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
resources :taxonomies, only: [] do
|
28
|
-
resources :products, only: [:index]
|
29
|
-
end
|
30
|
-
|
31
21
|
resource :cart, controller: controllers[:cart], only: [:show, :destroy] do
|
32
22
|
resources :products, controller: "glysellin/cart/products", only: [:create, :update, :destroy] do
|
33
23
|
collection do
|
@@ -51,7 +41,6 @@ module ActionDispatch::Routing
|
|
51
41
|
def parse_controllers options
|
52
42
|
defaults = {
|
53
43
|
orders: 'glysellin/orders',
|
54
|
-
products: 'glysellin/products',
|
55
44
|
cart: 'glysellin/cart'
|
56
45
|
}
|
57
46
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Glysellin
|
2
2
|
module PropertyFinder
|
3
|
-
def method_missing method, *args
|
3
|
+
def method_missing method, *args
|
4
4
|
if (prop = find { |prop| prop.type.name == method.to_s })
|
5
5
|
prop.value
|
6
6
|
elsif Glysellin::ProductPropertyType.select('1').find_by_name(method)
|
7
7
|
false
|
8
8
|
else
|
9
|
-
super
|
9
|
+
super
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "glysellin/sellable/simple"
|
2
|
+
require "glysellin/sellable/multi"
|
3
|
+
|
4
|
+
module Glysellin
|
5
|
+
module Sellable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
# Macro to include sellable behaviour in side any model
|
11
|
+
#
|
12
|
+
# @param [Hash] options An option hash to configure the mixin
|
13
|
+
#
|
14
|
+
# @option options [Symbol] :simple If set to true, sellable will only have
|
15
|
+
# one variant, a has_one relation is defined between sellable and the
|
16
|
+
# Variant model
|
17
|
+
# @option options [Boolean] :stock If set to false, consider every itmes
|
18
|
+
# should have unlimited stock. Defaults to true
|
19
|
+
#
|
20
|
+
def acts_as_sellable options = {}
|
21
|
+
# Merge options with defaults and store them for further use
|
22
|
+
cattr_accessor :sellable_options
|
23
|
+
self.sellable_options = options.reverse_merge(
|
24
|
+
simple: false,
|
25
|
+
stock: true
|
26
|
+
)
|
27
|
+
|
28
|
+
if sellable_options[:simple]
|
29
|
+
include Sellable::Simple
|
30
|
+
else
|
31
|
+
include Sellable::Multi
|
32
|
+
end
|
33
|
+
|
34
|
+
# Defines the polymorphic has_one association with the
|
35
|
+
# Glysellin::Product model, holding product wide configurations
|
36
|
+
has_one :product, as: :sellable, class_name: "Glysellin::Product",
|
37
|
+
inverse_of: :sellable, dependent: :destroy
|
38
|
+
accepts_nested_attributes_for :product, allow_destroy: true
|
39
|
+
attr_accessible :product_attributes
|
40
|
+
|
41
|
+
unless sellable_options[:stock]
|
42
|
+
before_validation :ensure_unlimited_stock
|
43
|
+
end
|
44
|
+
|
45
|
+
# Attributes delegation. Allowing to consider Product as a simple
|
46
|
+
# configuration model for the sellable one.
|
47
|
+
#
|
48
|
+
delegate :vat_rate, :vat_ratio, to: :product
|
49
|
+
|
50
|
+
# delegate :price, :unmarked_price, :marked_down?, to: :master_variant
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# If acts_as_sellable is called with the option `stock: false`, we admit
|
55
|
+
# every variant must be unlimited stock, so admins never have to bother
|
56
|
+
# filling in stock or selecting `unlimited_stock: true`
|
57
|
+
#
|
58
|
+
def ensure_unlimited_stock
|
59
|
+
variants.each do |variant|
|
60
|
+
variant.unlimited_stock = true
|
61
|
+
variant.save unless variant.new_record?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def published_variants
|
66
|
+
variants.published
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Glysellin
|
2
|
+
module Sellable
|
3
|
+
module Multi
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
has_many :variants, as: :sellable, class_name: "Glysellin::Variant",
|
8
|
+
inverse_of: :sellable, dependent: :destroy
|
9
|
+
accepts_nested_attributes_for :variants, allow_destroy: true,
|
10
|
+
reject_if: :all_blank
|
11
|
+
attr_accessible :variants_attributes
|
12
|
+
|
13
|
+
# Published sellables are the ones that have at least one variant
|
14
|
+
# published.
|
15
|
+
#
|
16
|
+
# This behaviour can be overriden inside the sellable's model if the
|
17
|
+
# scope is declared after the `acts_as_sellable` call
|
18
|
+
scope :published, -> {
|
19
|
+
includes(:variants).where(glysellin_variants: { published: true })
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Glysellin
|
2
|
+
module Sellable
|
3
|
+
module Simple
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
has_one :variant, as: :sellable, class_name: "Glysellin::Variant",
|
8
|
+
inverse_of: :sellable, dependent: :destroy
|
9
|
+
accepts_nested_attributes_for :variant, allow_destroy: true,
|
10
|
+
reject_if: :all_blank
|
11
|
+
attr_accessible :variant_attributes
|
12
|
+
|
13
|
+
# Published sellables are the ones that have at least one variant
|
14
|
+
# published.
|
15
|
+
#
|
16
|
+
# This behaviour can be overriden inside the sellable's model if the
|
17
|
+
# scope is declared after the `acts_as_sellable` call
|
18
|
+
scope :published, -> {
|
19
|
+
includes(:variant).where( glysellin_variants: { published: true })
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Fetches variant wrapping it in an ActiveRecord::Relation object to
|
24
|
+
# make behavior consistent with sellables in Multi mode
|
25
|
+
def variants
|
26
|
+
Variant.where(sellable_id: id, sellable_type: self.class.to_s)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/glysellin/version.rb
CHANGED
@@ -5,7 +5,7 @@ namespace :glysellin do
|
|
5
5
|
task :seed => :environment do
|
6
6
|
[['Chèque', 'check'],
|
7
7
|
['Paypal', 'paypal-integral'],
|
8
|
-
['
|
8
|
+
['Carte bancaire', 'atos']].each do |payment_method|
|
9
9
|
attributes = Hash[[:name, :slug].zip(payment_method)]
|
10
10
|
Glysellin::PaymentMethod.create(attributes)
|
11
11
|
puts "Created payment method : #{ attributes[:name] }"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glysellin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.1
|
4
|
+
version: 0.4.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Valentin Ballestrino
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -161,7 +161,6 @@ files:
|
|
161
161
|
- app/controllers/glysellin/cart/state_controller.rb
|
162
162
|
- app/controllers/glysellin/cart_controller.rb
|
163
163
|
- app/controllers/glysellin/orders_controller.rb
|
164
|
-
- app/controllers/glysellin/products_controller.rb
|
165
164
|
- app/helpers/glysellin/application_helper.rb
|
166
165
|
- app/helpers/glysellin/cart_helper.rb
|
167
166
|
- app/helpers/glysellin/orders_helper.rb
|
@@ -172,9 +171,9 @@ files:
|
|
172
171
|
- app/models/glysellin/brand.rb
|
173
172
|
- app/models/glysellin/discount_code.rb
|
174
173
|
- app/models/glysellin/discount_type.rb
|
174
|
+
- app/models/glysellin/line_item.rb
|
175
175
|
- app/models/glysellin/order.rb
|
176
176
|
- app/models/glysellin/order_adjustment.rb
|
177
|
-
- app/models/glysellin/order_item.rb
|
178
177
|
- app/models/glysellin/orderer.rb
|
179
178
|
- app/models/glysellin/payment.rb
|
180
179
|
- app/models/glysellin/payment_method.rb
|
@@ -182,9 +181,7 @@ files:
|
|
182
181
|
- app/models/glysellin/product_image.rb
|
183
182
|
- app/models/glysellin/product_property.rb
|
184
183
|
- app/models/glysellin/product_property_type.rb
|
185
|
-
- app/models/glysellin/product_type.rb
|
186
184
|
- app/models/glysellin/shipping_method.rb
|
187
|
-
- app/models/glysellin/taxonomy.rb
|
188
185
|
- app/models/glysellin/variant.rb
|
189
186
|
- app/views/glysellin/cart/_added_to_cart_warning.html.erb
|
190
187
|
- app/views/glysellin/cart/_cart.html.erb
|
@@ -214,11 +211,7 @@ files:
|
|
214
211
|
- app/views/glysellin/orders/_payment_successful.html.erb
|
215
212
|
- app/views/glysellin/orders/payment_response.html.erb
|
216
213
|
- app/views/glysellin/payment_methods/_paypal-integral.html.erb
|
217
|
-
- app/views/glysellin/products/_add_to_cart.html.
|
218
|
-
- app/views/glysellin/products/_item.html.erb
|
219
|
-
- app/views/glysellin/products/filter.html.erb
|
220
|
-
- app/views/glysellin/products/index.html.erb
|
221
|
-
- app/views/glysellin/products/show.html.erb
|
214
|
+
- app/views/glysellin/products/_add_to_cart.html.haml
|
222
215
|
- app/views/glysellin/shared/_address.html.erb
|
223
216
|
- app/views/glysellin/shared/_address.text.erb
|
224
217
|
- app/views/glysellin/shared/_order.text.erb
|
@@ -261,6 +254,15 @@ files:
|
|
261
254
|
- db/migrate/20130311135842_add_addressable_to_glysellin_addresses.rb
|
262
255
|
- db/migrate/20130314114048_change_addressable_associations_in_glysellin_addresses.rb
|
263
256
|
- db/migrate/20130325113903_remove_address_ids_from_glysellin_orders.rb
|
257
|
+
- db/migrate/20130609013900_add_sellable_references_to_glysellin_products.rb
|
258
|
+
- db/migrate/20130609023300_remove_unused_fields_from_glysellin_products.rb
|
259
|
+
- db/migrate/20130609025800_remove_taxonomies_from_system.rb
|
260
|
+
- db/migrate/20130609040600_rename_order_items_to_line_items.rb
|
261
|
+
- db/migrate/20130609142100_add_variant_id_to_glysellin_line_items.rb
|
262
|
+
- db/migrate/20130609155600_make_variants_belong_to_sellables_and_not_products.rb
|
263
|
+
- db/migrate/20130609193600_drop_product_types.rb
|
264
|
+
- db/migrate/20130609200000_remove_unused_product_properties_columns.rb
|
265
|
+
- db/migrate/20130609200700_remove_position_from_glysellin_variants.rb
|
264
266
|
- db/seeds/shipping_carrier/rates/colissimo.csv
|
265
267
|
- db/seeds/shipping_carrier/rates/lettre-max.csv
|
266
268
|
- lib/active_model/model.rb
|
@@ -269,7 +271,6 @@ files:
|
|
269
271
|
- lib/generators/glysellin/install/templates/initializer.rb
|
270
272
|
- lib/generators/glysellin/install/templates/order_observer.rb
|
271
273
|
- lib/generators/glysellin/install/utils.rb
|
272
|
-
- lib/glysellin/acts_as_sellable.rb
|
273
274
|
- lib/glysellin/cart/address.rb
|
274
275
|
- lib/glysellin/cart/adjustment/base.rb
|
275
276
|
- lib/glysellin/cart/adjustment/discount_code.rb
|
@@ -299,9 +300,11 @@ files:
|
|
299
300
|
- lib/glysellin/helpers/countries.rb
|
300
301
|
- lib/glysellin/helpers/views.rb
|
301
302
|
- lib/glysellin/helpers.rb
|
302
|
-
- lib/glysellin/product_methods.rb
|
303
303
|
- lib/glysellin/products_list.rb
|
304
304
|
- lib/glysellin/property_finder.rb
|
305
|
+
- lib/glysellin/sellable/multi.rb
|
306
|
+
- lib/glysellin/sellable/simple.rb
|
307
|
+
- lib/glysellin/sellable.rb
|
305
308
|
- lib/glysellin/shipping_carrier/base.rb
|
306
309
|
- lib/glysellin/shipping_carrier/colissimo.rb
|
307
310
|
- lib/glysellin/shipping_carrier/flat_rate.rb
|
@@ -1,12 +0,0 @@
|
|
1
|
-
module Glysellin
|
2
|
-
class ProductsController < ApplicationController
|
3
|
-
def index
|
4
|
-
@products = Glysellin::Product.order('created_at DESC').limit(10)
|
5
|
-
@products = @products.with_taxonomy(params[:taxonomy_id]) if params[:taxonomy_id]
|
6
|
-
end
|
7
|
-
|
8
|
-
def show
|
9
|
-
@product = Glysellin::Product.find(params[:id])
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
class Glysellin::ProductType < ActiveRecord::Base
|
2
|
-
self.table_name = 'glysellin_product_types'
|
3
|
-
|
4
|
-
has_and_belongs_to_many :property_types,
|
5
|
-
class_name: 'Glysellin::ProductPropertyType',
|
6
|
-
join_table: 'glysellin_product_types_property_types'
|
7
|
-
|
8
|
-
has_many :products
|
9
|
-
|
10
|
-
attr_accessible :name, :product_ids, :property_type_ids
|
11
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Glysellin
|
2
|
-
class Taxonomy < ActiveRecord::Base
|
3
|
-
extend FriendlyId
|
4
|
-
|
5
|
-
self.table_name = 'glysellin_taxonomies'
|
6
|
-
|
7
|
-
friendly_id :name, use: :slugged
|
8
|
-
|
9
|
-
has_and_belongs_to_many :products,
|
10
|
-
join_table: 'glysellin_products_taxonomies'
|
11
|
-
has_many :sub_taxonomies, class_name: 'Taxonomy',
|
12
|
-
foreign_key: 'parent_taxonomy_id'
|
13
|
-
belongs_to :parent_taxonomy, foreign_key: 'parent_taxonomy_id',
|
14
|
-
class_name: 'Taxonomy'
|
15
|
-
|
16
|
-
attr_accessible :name, :parent_taxonomy, :sub_taxonomies, :products,
|
17
|
-
:product_ids, :sub_taxonomy_ids, :parent_taxonomy_id
|
18
|
-
|
19
|
-
def all_products
|
20
|
-
Product.includes(:taxonomies).where(
|
21
|
-
'glysellin_products_taxonomies.taxonomy_id IN (?)',
|
22
|
-
taxonomy_tree_ids
|
23
|
-
)
|
24
|
-
end
|
25
|
-
|
26
|
-
def taxonomy_tree_ids
|
27
|
-
if sub_taxonomies.length > 0
|
28
|
-
ts = sub_taxonomies.includes(:sub_taxonomies).map(&:taxonomy_tree_ids)
|
29
|
-
ts.flatten << self.id
|
30
|
-
else
|
31
|
-
self.id
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
<%= form_tag cart_products_path, class: 'add-to-cart-form', remote: options[:remote], data: { "add-to-cart-form" => true } do %>
|
2
|
-
<div class="product-quantity">
|
3
|
-
<%= number_field :cart, :quantity, value: 1 %>
|
4
|
-
</div>
|
5
|
-
<div class="submit-container">
|
6
|
-
<% if product.variants.length > 1 %>
|
7
|
-
<%= select_tag 'cart[product_id]', options_for_select(product.variants.collect {|v| [ v.name, v.id ] }) %>
|
8
|
-
<% else %>
|
9
|
-
<%= hidden_field_tag 'cart[product_id]', product.variants.first.id %>
|
10
|
-
<% end %>
|
11
|
-
|
12
|
-
<button class="submit" type="submit">
|
13
|
-
<%= t('glysellin.labels.cart.add_to_cart') %>
|
14
|
-
</button>
|
15
|
-
</div>
|
16
|
-
<% end %>
|
@@ -1,21 +0,0 @@
|
|
1
|
-
<div class="product-container">
|
2
|
-
<span class="name">
|
3
|
-
<%= link_to product.name, product_path(product) %>
|
4
|
-
</span>
|
5
|
-
<% if product.image %>
|
6
|
-
<div class="image">
|
7
|
-
<%= image_tag product.image.url(:thumb) %>
|
8
|
-
</div>
|
9
|
-
<% end %>
|
10
|
-
<div class="description">
|
11
|
-
<%= product.description %>
|
12
|
-
</div>
|
13
|
-
|
14
|
-
<div class="buying-infos">
|
15
|
-
<span class="price">
|
16
|
-
<%= number_to_currency(product.price) %>
|
17
|
-
</span>
|
18
|
-
|
19
|
-
<%= add_to_cart_form(product) %>
|
20
|
-
</div>
|
21
|
-
</div>
|
File without changes
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module Glysellin
|
2
|
-
module ActsAsSellable
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
module ClassMethods
|
6
|
-
def acts_as_sellable
|
7
|
-
has_one :product, :class_name => "::Glysellin::Product", :inverse_of => :item
|
8
|
-
accepts_nested_attributes_for :product, :allow_destroy => true
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
::ActiveRecord::Base.send :include, Glysellin::ActsAsSellable
|
@@ -1,66 +0,0 @@
|
|
1
|
-
module Glysellin
|
2
|
-
module ProductMethods
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
module ClassMethods
|
6
|
-
attr_writer :bundle_attributes
|
7
|
-
|
8
|
-
def bundle_attributes
|
9
|
-
@bundle_attributes ||= {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def bundle_attribute attribute, &block
|
13
|
-
# Store block to be called to retrieve
|
14
|
-
self.bundle_attributes[attribute.to_sym] = block
|
15
|
-
|
16
|
-
class_eval <<-CLASS, __FILE__, __LINE__
|
17
|
-
def bundle_#{ attribute }
|
18
|
-
if bundled_products.length > 0
|
19
|
-
self.class.bundle_attributes[:#{ attribute }].call(self)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def #{ attribute }
|
24
|
-
value = super
|
25
|
-
value = bundle_#{ attribute } if value == nil
|
26
|
-
value
|
27
|
-
end
|
28
|
-
|
29
|
-
def #{ attribute }=(value)
|
30
|
-
super(value) if value != bundle_#{ attribute }
|
31
|
-
end
|
32
|
-
CLASS
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# SKU generation helper
|
37
|
-
#
|
38
|
-
# @return [String] The generated SKU
|
39
|
-
def generate_sku
|
40
|
-
# Get last product only selecting id
|
41
|
-
last_item = self.class.select(:id).order('id DESC').first
|
42
|
-
# Generate SKU from the last product id we got,
|
43
|
-
# or appending "1" if there's no product
|
44
|
-
sku = (last_item ? (last_item.id + 1).to_s : '1')
|
45
|
-
sku += self.name.parameterize
|
46
|
-
end
|
47
|
-
|
48
|
-
# Checks if a product is a bundle of other sub products
|
49
|
-
#
|
50
|
-
# @return [true, false]
|
51
|
-
def bundle?
|
52
|
-
false
|
53
|
-
# bundled_products.length > 0
|
54
|
-
end
|
55
|
-
|
56
|
-
# Gives calculable vat rate
|
57
|
-
#
|
58
|
-
# Example :
|
59
|
-
# If vat_rate is set to 19.6%, returns back 1.196
|
60
|
-
#
|
61
|
-
# @return [Float] The processed VAT rate
|
62
|
-
def vat_ratio
|
63
|
-
1 + vat_rate / 100
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|