radiant-shop-extension 0.11.5 → 0.11.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/app/controllers/admin/shop/discounts/discountables_controller.rb +44 -0
- data/app/controllers/admin/shop/discounts_controller.rb +64 -0
- data/app/controllers/admin/shop/orders_controller.rb +19 -3
- data/app/controllers/admin/shop/packages_controller.rb +1 -3
- data/app/controllers/admin/shop/products_controller.rb +1 -2
- data/app/controllers/admin/shop/variants_controller.rb +3 -4
- data/app/models/form_address.rb +4 -2
- data/app/models/form_discount.rb +39 -0
- data/app/models/form_line_item.rb +1 -11
- data/app/models/shop_category.rb +3 -0
- data/app/models/shop_customer.rb +0 -9
- data/app/models/shop_discount.rb +37 -0
- data/app/models/shop_discountable.rb +64 -0
- data/app/models/shop_line_item.rb +24 -1
- data/app/models/shop_order.rb +41 -28
- data/app/models/shop_product.rb +11 -0
- data/app/models/shop_product_variant.rb +3 -0
- data/app/models/shop_variant.rb +1 -3
- data/app/views/admin/shop/discounts/edit.html.haml +11 -0
- data/app/views/admin/shop/discounts/edit/_foot.html.haml +16 -0
- data/app/views/admin/shop/discounts/edit/_form.html.haml +15 -0
- data/app/views/admin/shop/discounts/edit/_head.html.haml +4 -0
- data/app/views/admin/shop/discounts/edit/_inputs.html.haml +2 -0
- data/app/views/admin/shop/discounts/edit/_meta.html.haml +8 -0
- data/app/views/admin/shop/discounts/edit/_parts.html.haml +9 -0
- data/app/views/admin/shop/discounts/edit/_popups.html.haml +4 -0
- data/app/views/admin/shop/discounts/edit/buttons/_browse_categories.html.haml +1 -0
- data/app/views/admin/shop/discounts/edit/buttons/_browse_products.html.haml +1 -0
- data/app/views/admin/shop/discounts/edit/inputs/_amount.html.haml +3 -0
- data/app/views/admin/shop/discounts/edit/inputs/_code.html.haml +3 -0
- data/app/views/admin/shop/discounts/edit/inputs/_name.html.haml +3 -0
- data/app/views/admin/shop/discounts/edit/meta/_finish.html.haml +5 -0
- data/app/views/admin/shop/discounts/edit/meta/_start.html.haml +5 -0
- data/app/views/admin/shop/discounts/edit/parts/_categories.html.haml +3 -0
- data/app/views/admin/shop/discounts/edit/parts/_products.html.haml +3 -0
- data/app/views/admin/shop/discounts/edit/popups/_browse_categories.html.haml +6 -0
- data/app/views/admin/shop/discounts/edit/popups/_browse_products.html.haml +6 -0
- data/app/views/admin/shop/discounts/edit/shared/_category.html.haml +11 -0
- data/app/views/admin/shop/discounts/edit/shared/_product.html.haml +12 -0
- data/app/views/admin/shop/discounts/index.html.haml +13 -0
- data/app/views/admin/shop/discounts/index/_discount.html.haml +13 -0
- data/app/views/admin/shop/discounts/index/_foot.html.haml +5 -0
- data/app/views/admin/shop/discounts/index/_head.html.haml +2 -0
- data/app/views/admin/shop/discounts/index/buttons/_new_discount.html.haml +1 -0
- data/app/views/admin/shop/discounts/new.html.haml +11 -0
- data/app/views/admin/shop/discounts/remove.html.haml +12 -0
- data/app/views/admin/shop/orders/index.html.haml +1 -1
- data/app/views/admin/shop/orders/index/_foot.html.haml +3 -1
- data/app/views/admin/shop/orders/index/_head.html.haml +1 -3
- data/app/views/admin/shop/packages/index.html.haml +1 -1
- data/app/views/admin/shop/packages/index/_foot.html.haml +3 -2
- data/app/views/admin/shop/packages/index/_head.html.haml +1 -3
- data/app/views/admin/shop/packages/index/buttons/_new_package.html.haml +1 -0
- data/app/views/admin/shop/products/index.html.haml +1 -1
- data/app/views/admin/shop/products/index/_foot.html.haml +3 -2
- data/app/views/admin/shop/products/index/_head.html.haml +1 -3
- data/app/views/admin/shop/products/index/buttons/_add_category.html.haml +1 -0
- data/app/views/admin/shop/products/index/buttons/_variants.html.haml +1 -1
- data/app/views/admin/shop/variants/index.html.haml +1 -1
- data/app/views/admin/shop/variants/index/_foot.html.haml +3 -2
- data/app/views/admin/shop/variants/index/_head.html.haml +1 -3
- data/app/views/admin/shop/variants/index/buttons/_add_variant.html.haml +1 -0
- data/app/views/admin/shop/variants/index/buttons/_categories.html.haml +1 -0
- data/config/routes.rb +11 -5
- data/db/migrate/20101010071143_create_discounts.rb +29 -0
- data/db/migrate/20101010072225_modify_categories_remove_variant.rb +9 -0
- data/db/migrate/20101010130034_change_discountables_add_observer.rb +15 -0
- data/db/migrate/20101011063133_change_orders_set_limits_to_null.rb +11 -0
- data/lib/shop/interface/discounts.rb +34 -0
- data/lib/shop/interface/orders.rb +2 -2
- data/lib/shop/interface/packages.rb +2 -2
- data/lib/shop/interface/products.rb +2 -2
- data/lib/shop/interface/variants.rb +2 -2
- data/lib/shop/models/form_extension.rb +12 -1
- data/lib/shop/models/user.rb +18 -0
- data/lib/shop/tags/cart.rb +24 -0
- data/lib/shop/tags/helpers.rb +1 -1
- data/lib/shop/tags/item.rb +26 -11
- data/lib/shop/tags/tax.rb +73 -0
- data/lib/tasks/shop_extension_tasks.rake +1 -1
- data/mockups/discounts/new-edit.bmml +49 -49
- data/public/javascripts/admin/extensions/shop/discounts/edit.js +76 -0
- data/public/stylesheets/sass/admin/extensions/shop/discounts/edit.sass +125 -0
- data/public/stylesheets/sass/admin/extensions/shop/index.sass +14 -40
- data/radiant-shop-extension.gemspec +70 -7
- data/shop_extension.rb +23 -12
- data/spec/controllers/admin/shop/discounts/discountables_controller_spec.rb +73 -0
- data/spec/controllers/admin/shop/discounts_controller_spec.rb +81 -0
- data/spec/controllers/admin/shop/orders_controller_spec.rb +35 -0
- data/spec/datasets/forms.rb +23 -12
- data/spec/datasets/shop_discountables.rb +22 -0
- data/spec/datasets/shop_discounts.rb +29 -0
- data/spec/lib/shop/tags/cart_spec.rb +75 -1
- data/spec/lib/shop/tags/item_spec.rb +4 -7
- data/spec/lib/shop/tags/tax_spec.rb +201 -0
- data/spec/models/form_discount_spec.rb +63 -0
- data/spec/models/shop_category_spec.rb +8 -0
- data/spec/models/shop_discount_spec.rb +130 -0
- data/spec/models/shop_discountable_spec.rb +114 -0
- data/spec/models/shop_line_item_spec.rb +9 -1
- data/spec/models/shop_order_spec.rb +253 -199
- data/spec/models/shop_product_spec.rb +31 -2
- data/spec/models/shop_variant_spec.rb +0 -15
- metadata +97 -21
- data/app/views/admin/shop/products/index/buttons/_discounts.html.haml +0 -1
- data/app/views/admin/shop/products/index/buttons/_packages.html.haml +0 -1
data/app/models/shop_order.rb
CHANGED
@@ -2,8 +2,10 @@ class ShopOrder < ActiveRecord::Base
|
|
2
2
|
|
3
3
|
default_scope :order => 'shop_orders.updated_at DESC'
|
4
4
|
|
5
|
-
has_one
|
6
|
-
has_many
|
5
|
+
has_one :payment, :class_name => 'ShopPayment', :foreign_key => :order_id, :dependent => :destroy
|
6
|
+
has_many :line_items, :class_name => 'ShopLineItem', :foreign_key => :order_id, :dependent => :destroy
|
7
|
+
has_many :discountables, :class_name => 'ShopDiscountable', :foreign_key => :discounted_id
|
8
|
+
has_many :discounts, :class_name => 'ShopDiscount', :through => :discountables
|
7
9
|
|
8
10
|
belongs_to :billing, :class_name => 'ShopAddress'
|
9
11
|
belongs_to :shipping, :class_name => 'ShopAddress'
|
@@ -15,6 +17,34 @@ class ShopOrder < ActiveRecord::Base
|
|
15
17
|
accepts_nested_attributes_for :billing, :reject_if => :all_blank
|
16
18
|
accepts_nested_attributes_for :shipping, :reject_if => :all_blank
|
17
19
|
|
20
|
+
def price
|
21
|
+
price = 0; line_items.map { |l| price += l.price }
|
22
|
+
|
23
|
+
price += tax if Radiant::Config['shop.tax_strategy'] === 'exclusive'
|
24
|
+
|
25
|
+
price
|
26
|
+
end
|
27
|
+
|
28
|
+
def weight
|
29
|
+
weight = 0; line_items.map { |l| weight += l.weight }
|
30
|
+
weight
|
31
|
+
end
|
32
|
+
|
33
|
+
def tax
|
34
|
+
price = 0; line_items.map { |l| price += l.price; }
|
35
|
+
tax = 0
|
36
|
+
percentage = Radiant::Config['shop.tax_percentage'].to_f * 0.01
|
37
|
+
|
38
|
+
case Radiant::Config['shop.tax_strategy']
|
39
|
+
when 'inclusive'
|
40
|
+
tax = price - (price / (1 + percentage))
|
41
|
+
when 'exclusive'
|
42
|
+
tax = price * percentage
|
43
|
+
end
|
44
|
+
|
45
|
+
BigDecimal.new(tax.to_s)
|
46
|
+
end
|
47
|
+
|
18
48
|
def add!(id, quantity = nil, type = nil)
|
19
49
|
result = true
|
20
50
|
|
@@ -86,18 +116,6 @@ class ShopOrder < ActiveRecord::Base
|
|
86
116
|
self.line_items.sum(:quantity)
|
87
117
|
end
|
88
118
|
|
89
|
-
def price
|
90
|
-
price = 0
|
91
|
-
self.line_items.map { |l| price += l.price }
|
92
|
-
price
|
93
|
-
end
|
94
|
-
|
95
|
-
def weight
|
96
|
-
weight = 0
|
97
|
-
self.line_items.map { |l| weight += l.weight }
|
98
|
-
weight
|
99
|
-
end
|
100
|
-
|
101
119
|
def new?
|
102
120
|
self.status === 'new'
|
103
121
|
end
|
@@ -112,22 +130,17 @@ class ShopOrder < ActiveRecord::Base
|
|
112
130
|
end
|
113
131
|
|
114
132
|
class << self
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
# This looks tricky, but not subject to sql injection :-)
|
125
|
-
conditions = [sql, {:term => "%#{search}%" }]
|
133
|
+
|
134
|
+
# Will scope the contained find calls to a specific status
|
135
|
+
def scope_by_status(status)
|
136
|
+
case status
|
137
|
+
when 'new', 'shipped', 'paid'
|
138
|
+
with_scope(:find => { :conditions => {:status => status}}) do
|
139
|
+
yield
|
140
|
+
end
|
126
141
|
else
|
127
|
-
|
142
|
+
yield
|
128
143
|
end
|
129
|
-
|
130
|
-
all(:conditions => conditions)
|
131
144
|
end
|
132
145
|
|
133
146
|
def params
|
data/app/models/shop_product.rb
CHANGED
@@ -14,6 +14,8 @@ class ShopProduct < ActiveRecord::Base
|
|
14
14
|
has_many :packages, :class_name => 'ShopPackage', :foreign_key => :package_id, :through => :packings, :source => :package
|
15
15
|
has_many :related, :class_name => 'ShopProduct', :through => :packings, :source => :product, :uniq => true
|
16
16
|
has_many :variants, :class_name => 'ShopProductVariant', :foreign_key => :product_id, :dependent => :destroy
|
17
|
+
has_many :discountables, :class_name => 'ShopDiscountable', :foreign_key => :discounted_id
|
18
|
+
has_many :discounts, :class_name => 'ShopDiscount', :through => :discountables
|
17
19
|
|
18
20
|
before_validation :assign_slug, :assign_breadcrumb
|
19
21
|
validates_presence_of :page
|
@@ -23,6 +25,8 @@ class ShopProduct < ActiveRecord::Base
|
|
23
25
|
accepts_nested_attributes_for :page
|
24
26
|
accepts_nested_attributes_for :variants
|
25
27
|
|
28
|
+
after_create :assign_discounts
|
29
|
+
|
26
30
|
# Returns the title of the product's page
|
27
31
|
def name; page.title; end
|
28
32
|
|
@@ -122,4 +126,11 @@ class ShopProduct < ActiveRecord::Base
|
|
122
126
|
end
|
123
127
|
end
|
124
128
|
|
129
|
+
# Assigns discounts based off categories discounts
|
130
|
+
def assign_discounts
|
131
|
+
category.discounts.each do |discount|
|
132
|
+
ShopDiscountable.create(:discount => discount, :discounted => self)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
125
136
|
end
|
@@ -9,6 +9,9 @@ class ShopProductVariant < ActiveRecord::Base
|
|
9
9
|
belongs_to :created_by, :class_name => 'User'
|
10
10
|
belongs_to :updated_by, :class_name => 'User'
|
11
11
|
|
12
|
+
has_many :line_items, :class_name => 'ShopLineItem', :foreign_key => :item_id, :dependent => :destroy
|
13
|
+
has_many :orders, :class_name => 'ShopOrder', :through => :line_items, :uniq => true
|
14
|
+
|
12
15
|
# Returns the price of the variant plus the product price
|
13
16
|
def price
|
14
17
|
price = product.price
|
data/app/models/shop_variant.rb
CHANGED
@@ -2,9 +2,7 @@ class ShopVariant < ActiveRecord::Base
|
|
2
2
|
|
3
3
|
belongs_to :created_by, :class_name => 'User'
|
4
4
|
belongs_to :updated_by, :class_name => 'User'
|
5
|
-
|
6
|
-
has_many :categories, :class_name => 'ShopCategory', :foreign_key => 'variant_id'
|
7
|
-
|
5
|
+
|
8
6
|
validates_presence_of :name
|
9
7
|
validates_uniqueness_of :name
|
10
8
|
validates_presence_of :options_json
|
@@ -0,0 +1,11 @@
|
|
1
|
+
- @page_title = @shop_discount.name
|
2
|
+
|
3
|
+
- render_region :main do |main|
|
4
|
+
- main.head do
|
5
|
+
= render 'admin/shop/discounts/edit/head'
|
6
|
+
|
7
|
+
- main.popups do
|
8
|
+
= render 'admin/shop/discounts/edit/popups'
|
9
|
+
|
10
|
+
- main.form do
|
11
|
+
= render 'admin/shop/discounts/edit/form'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
- render_region :foot do |foot|
|
2
|
+
- foot.buttons do
|
3
|
+
%p.buttons
|
4
|
+
= save_model_button(@shop_discount)
|
5
|
+
= save_model_and_continue_editing_button(@shop_discount)
|
6
|
+
= t('or')
|
7
|
+
= link_to t('cancel'), admin_shop_discounts_path
|
8
|
+
- foot.timestamp do
|
9
|
+
= updated_stamp @shop_discount
|
10
|
+
|
11
|
+
- unless @shop_discount.new_record?
|
12
|
+
:javascript
|
13
|
+
if(typeof(ROUTES) === 'undefined') ROUTES = new Array();
|
14
|
+
|
15
|
+
ROUTES['admin_shop_discount_discountables_path'] = "#{admin_shop_discount_discountables_path(@shop_discount)}";
|
16
|
+
ROUTES['admin_shop_discount_discountable_path'] = "#{admin_shop_discount_discountable_path(@shop_discount, ':id')}";
|
@@ -0,0 +1,15 @@
|
|
1
|
+
- form_for [:admin, @shop_discount], :html => {'data-onsubmit_status'=>"Saving Changes…"} do |f|
|
2
|
+
= hidden_field_tag 'shop_discount_id',@shop_discount.id
|
3
|
+
|
4
|
+
- render_region :form do |form|
|
5
|
+
- form.inputs do
|
6
|
+
= render :partial => '/admin/shop/discounts/edit/inputs', :locals => { :f => f }
|
7
|
+
|
8
|
+
- form.meta do
|
9
|
+
= render :partial => '/admin/shop/discounts/edit/meta', :locals => { :f => f }
|
10
|
+
|
11
|
+
- form.parts do
|
12
|
+
= render :partial => '/admin/shop/discounts/edit/parts', :locals => { :f => f }
|
13
|
+
|
14
|
+
- form.foot do
|
15
|
+
= render :partial => 'admin/shop/discounts/edit/foot', :locals => { :f => f }
|
@@ -0,0 +1,8 @@
|
|
1
|
+
- if @meta.present?
|
2
|
+
.drawer
|
3
|
+
.drawer_contents#attributes
|
4
|
+
%table.fieldset
|
5
|
+
- @meta.each do |meta|
|
6
|
+
= render :partial => "admin/shop/discounts/edit/meta/#{meta}", :locals => { :f => f }
|
7
|
+
.drawer_handle
|
8
|
+
%a.toggle{:href=>'#attributes', :rel=>"toggle[attributes]", :class=>"#{(meta_errors? ? 'less' : 'more')}"}= meta_label
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#tab_control
|
2
|
+
#tabs.tabs
|
3
|
+
#tab_toolbar
|
4
|
+
#pages.pages
|
5
|
+
= hidden_field_tag 'page_part_index_field' #important
|
6
|
+
- @parts.each do |part|
|
7
|
+
.page{:id => "product_page_#{part}", 'data-caption'=>h(part)}
|
8
|
+
.part{:id => "product_part_#{part}"}
|
9
|
+
= render :partial => "admin/shop/discounts/edit/parts/#{part}", :locals => { :f => f }
|
@@ -0,0 +1 @@
|
|
1
|
+
= link_to t('browse_categories'), '#browse_categories_popup', :class => 'popup button'
|
@@ -0,0 +1 @@
|
|
1
|
+
= link_to t('browse_products'), '#browse_products_popup', :class => 'popup button'
|
@@ -0,0 +1,6 @@
|
|
1
|
+
%h3.title Browse Categories
|
2
|
+
|
3
|
+
%ul#available_categories.availables
|
4
|
+
= render :partial => '/admin/shop/discounts/edit/shared/category', :collection => @shop_discount.available_categories
|
5
|
+
|
6
|
+
%a#browse_categories_popup_close.close{ :href => '#browse_categories_popup_close' }
|
@@ -0,0 +1,11 @@
|
|
1
|
+
- discounted_type = category.class.to_s
|
2
|
+
- discounted_id = discounted_type == "ShopCategory" ? category.id : category.discounted_id
|
3
|
+
- discountable_id = discounted_type == "ShopCategory" ? nil : category.id
|
4
|
+
- success_element = discounted_type == "ShopCategory" ? 'discount_categories' : 'available_categories'
|
5
|
+
- class_type = discounted_type == "ShopCategory" ? "available" : "discounted"
|
6
|
+
|
7
|
+
%li.category{:class => "discountable #{class_type} category", :id => "#{discounted_type}_#{category.id}", :'data-discounted_id' => discounted_id, :'data-discounted_type' => discounted_type, :'data-discounted_type_human' => 'Category', :'data-discountable_id' => discountable_id, :'data-success_element' => success_element }
|
8
|
+
%span.name= truncate((category.name rescue category.discounted.name), 30, '...')
|
9
|
+
%span.handle= (category.handle rescue category.discounted.handle)
|
10
|
+
.actions
|
11
|
+
%span.delete
|
@@ -0,0 +1,12 @@
|
|
1
|
+
- discounted_type = product.class.to_s
|
2
|
+
|
3
|
+
- discounted_id = discounted_type == "ShopProduct" ? product.id : product.discounted_id
|
4
|
+
- discountable_id = discounted_type == "ShopProduct" ? nil : product.id
|
5
|
+
- success_element = discounted_type == "ShopProduct" ? 'discount_products' : 'available_products'
|
6
|
+
- class_type = discounted_type == "ShopProduct" ? "available" : "discounted"
|
7
|
+
|
8
|
+
%li.product{:class => "discountable #{class_type} product", :id => "#{class_type}_#{product.id}", :'data-discounted_id' => discounted_id, :'data-discounted_type' => discounted_type, :'data-discounted_type_human' => 'Product', :'data-discountable_id' => discountable_id, :'data-success_element' => success_element }
|
9
|
+
%span.name= truncate((product.name rescue product.discounted.name), 30, '...')
|
10
|
+
%span.sku= (product.sku rescue product.discounted.sku)
|
11
|
+
.actions
|
12
|
+
%span.delete
|
@@ -0,0 +1,13 @@
|
|
1
|
+
- @page_title = t('discounts') + ' - ' + default_page_title
|
2
|
+
|
3
|
+
.outset
|
4
|
+
|
5
|
+
#head.min
|
6
|
+
= render :partial => '/admin/shop/discounts/index/head'
|
7
|
+
|
8
|
+
#discounts_map.map
|
9
|
+
%ul#discounts
|
10
|
+
= render :partial => '/admin/shop/discounts/index/discount', :collection => @shop_discounts
|
11
|
+
|
12
|
+
#actions
|
13
|
+
= render :partial => '/admin/shop/discounts/index/foot'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
%li.discount.object{:id => "discount_#{discount.id}", :'data-id' => discount.id}
|
2
|
+
.attributes
|
3
|
+
%span.name.attribute
|
4
|
+
- render_region :body do |body|
|
5
|
+
- body.name do
|
6
|
+
%span.name.attribute= link_to discount.name, [:edit_admin, discount]
|
7
|
+
- body.code do
|
8
|
+
%span.code.attribute= discount.code
|
9
|
+
- body.amount do
|
10
|
+
%span.amount.attribute= discount.amount
|
11
|
+
- body.modify do
|
12
|
+
.modify
|
13
|
+
%span.remove= link_to t('remove'), [:remove_admin, discount]
|
@@ -0,0 +1 @@
|
|
1
|
+
= link_to image('plus') + " " + t("add_discount"), new_admin_shop_discount_path
|
@@ -0,0 +1,11 @@
|
|
1
|
+
- @page_title = t('new_discount')
|
2
|
+
|
3
|
+
- render_region :main do |main|
|
4
|
+
- main.head do
|
5
|
+
= render 'admin/shop/discounts/edit/head'
|
6
|
+
|
7
|
+
- main.popups do
|
8
|
+
= render 'admin/shop/discounts/edit/popups'
|
9
|
+
|
10
|
+
- main.form do
|
11
|
+
= render 'admin/shop/discounts/edit/form'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
%h1
|
2
|
+
= t('remove_discount')
|
3
|
+
|
4
|
+
%p
|
5
|
+
= t('text.remove_discount.warning')
|
6
|
+
|
7
|
+
- form_for [:admin, @shop_discount.becomes(ShopDiscount)], :html => {:method => :delete, 'data-onsubmit_status'=>"Removing Discount…"} do
|
8
|
+
|
9
|
+
%p.buttons
|
10
|
+
%input.button{:type=>"submit", :value => t('delete_variant') }/
|
11
|
+
= t('or')
|
12
|
+
= link_to t('cancel'), admin_shop_discounts_path
|
@@ -1,7 +1,8 @@
|
|
1
1
|
%ul
|
2
2
|
- render_region :foot do |foot|
|
3
|
-
- foot.
|
4
|
-
|
3
|
+
- foot.buttons do
|
4
|
+
- @buttons.each do |button|
|
5
|
+
%li= render :partial => "admin/shop/packages/index/buttons/#{button}"
|
5
6
|
|
6
7
|
:javascript
|
7
8
|
if(typeof(ROUTES) === 'undefined') ROUTES = new Array();
|