solidus_volume_pricing 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.hound.yml +40 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +81 -0
- data/Gemfile +13 -0
- data/Guardfile +9 -0
- data/LICENSE.md +26 -0
- data/README.md +112 -0
- data/Rakefile +21 -0
- data/app/assets/javascripts/spree/backend/solidus_volume_pricing.js +16 -0
- data/app/controllers/spree/admin/variants_controller_decorator.rb +32 -0
- data/app/controllers/spree/admin/volume_price_models_controller.rb +27 -0
- data/app/controllers/spree/admin/volume_prices_controller.rb +11 -0
- data/app/helpers/spree/base_helper_decorator.rb +19 -0
- data/app/models/spree/line_item_decorator.rb +27 -0
- data/app/models/spree/user_decorator.rb +10 -0
- data/app/models/spree/variant_decorator.rb +104 -0
- data/app/models/spree/volume_price.rb +35 -0
- data/app/models/spree/volume_price_model.rb +8 -0
- data/app/overrides/spree/admin/shared/sub_menu/_configuration/add_volume_price_model_admin_menu_links.html.erb.deface +3 -0
- data/app/overrides/views_decorator.rb +13 -0
- data/app/views/spree/admin/shared/_vp_product_tab.html.erb +3 -0
- data/app/views/spree/admin/variants/_edit_fields.html.erb +33 -0
- data/app/views/spree/admin/variants/volume_prices.html.erb +39 -0
- data/app/views/spree/admin/volume_price_models/_form.html.erb +9 -0
- data/app/views/spree/admin/volume_price_models/_list.html.erb +23 -0
- data/app/views/spree/admin/volume_price_models/edit.html.erb +12 -0
- data/app/views/spree/admin/volume_price_models/index.html.erb +18 -0
- data/app/views/spree/admin/volume_price_models/new.html.erb +12 -0
- data/app/views/spree/admin/volume_prices/_edit_fields.html.erb +31 -0
- data/app/views/spree/admin/volume_prices/_volume_price_fields.html.erb +33 -0
- data/app/views/spree/products/_volume_pricing.html.erb +15 -0
- data/bin/rails +7 -0
- data/config/locales/de.yml +18 -0
- data/config/locales/en.yml +18 -0
- data/config/locales/pt.yml +18 -0
- data/config/locales/ru.yml +18 -0
- data/config/locales/sv.yml +18 -0
- data/config/locales/tr.yml +18 -0
- data/config/routes.rb +12 -0
- data/db/migrate/20081119145604_create_volume_prices.rb +16 -0
- data/db/migrate/20110203174010_change_display_name_for_volume_prices.rb +9 -0
- data/db/migrate/20111206173307_prefix_volume_pricing_table_names.rb +5 -0
- data/db/migrate/20121115043422_add_discount_type_column.rb +5 -0
- data/db/migrate/20150513200904_add_role_to_volume_price.rb +5 -0
- data/db/migrate/20150603143015_create_spree_volume_price_models.rb +18 -0
- data/lib/generators/solidus_volume_pricing/install/install_generator.rb +24 -0
- data/lib/solidus_volume_pricing.rb +6 -0
- data/lib/solidus_volume_pricing/engine.rb +42 -0
- data/lib/solidus_volume_pricing/version.rb +18 -0
- data/solidus_volume_pricing.gemspec +40 -0
- data/spec/controllers/spree/admin/variants_controller_spec.rb +27 -0
- data/spec/factories/volume_price_factory.rb +12 -0
- data/spec/helpers/base_helper_spec.rb +22 -0
- data/spec/models/spree/line_item_spec.rb +34 -0
- data/spec/models/spree/order_spec.rb +49 -0
- data/spec/models/spree/variant_spec.rb +306 -0
- data/spec/models/spree/volume_price_spec.rb +121 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/capybara.rb +12 -0
- data/spec/support/database_cleaner.rb +24 -0
- data/spec/support/factory_girl.rb +7 -0
- data/spec/support/spree.rb +10 -0
- metadata +337 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
<% display_percent ||= false %>
|
2
|
+
<% if product.price > 0 and product.master.volume_prices.present? %>
|
3
|
+
<div id="bulk-discount">
|
4
|
+
<h6><%= Spree.t(:bulk_discount) %></h6>
|
5
|
+
<table class="table">
|
6
|
+
<% product.master.volume_prices.each do |v| %>
|
7
|
+
<%= content_tag(:tr) do %>
|
8
|
+
<%= content_tag :td, (v.open_ended? ? v.range : "#{v.range.to_range.begin}–#{v.range.to_range.end}") %>
|
9
|
+
<%= content_tag :td, Spree::Money.new(v.amount).to_s %>
|
10
|
+
<%= content_tag(:td, '%i%' % ((1.0 - v.amount / product.master.price) * 100.0).round) if display_percent %>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
</table>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
data/bin/rails
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
de:
|
3
|
+
spree:
|
4
|
+
add_volume_price: Neuen Staffelpreis
|
5
|
+
position: Position
|
6
|
+
discount_type:
|
7
|
+
range: Bereich
|
8
|
+
volume_prices: Staffelpreise
|
9
|
+
volume_pricing: Staffelpreis
|
10
|
+
price_discount:
|
11
|
+
percent_discount:
|
12
|
+
bulk_discount:
|
13
|
+
activerecord:
|
14
|
+
errors:
|
15
|
+
messages:
|
16
|
+
could_not_conver_to_range:
|
17
|
+
is_not_a_valid_volume_price_type:
|
18
|
+
must_be_in_format:
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
spree:
|
4
|
+
add_volume_price: Add Volume Price
|
5
|
+
position: Position
|
6
|
+
discount_type: Discount Type
|
7
|
+
range: Range
|
8
|
+
volume_prices: Volume Prices
|
9
|
+
volume_pricing: Volume Pricing
|
10
|
+
price_discount: Price Discount
|
11
|
+
percent_discount: Percent Discount
|
12
|
+
bulk_discount: Bulk Discount
|
13
|
+
activerecord:
|
14
|
+
errors:
|
15
|
+
messages:
|
16
|
+
could_not_conver_to_range: "Couldn't convert to Range: %{number}"
|
17
|
+
is_not_a_valid_volume_price_type: '%{value} is not a valid Volume Price Type'
|
18
|
+
must_be_in_format: 'must be in one of the following formats: (a..b), (a...b), (a+)'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
pt:
|
3
|
+
spree:
|
4
|
+
add_volume_price: Adicionar Preço Volume
|
5
|
+
position: Posição
|
6
|
+
discount_type: Tipo Desconto
|
7
|
+
range: Intervalo
|
8
|
+
volume_prices: Preços Volume
|
9
|
+
volume_pricing: Preçário Volume
|
10
|
+
price_discount: Desconto no Preço
|
11
|
+
percent_discount: Desconto em Percentagem
|
12
|
+
bulk_discount: Desconto por Quantidade
|
13
|
+
activerecord:
|
14
|
+
errors:
|
15
|
+
messages:
|
16
|
+
could_not_conver_to_range:
|
17
|
+
is_not_a_valid_volume_price_type:
|
18
|
+
must_be_in_format:
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
ru:
|
3
|
+
spree:
|
4
|
+
add_volume_price: Добавить цену в зависимости от количества
|
5
|
+
position: Позиция в списке
|
6
|
+
discount_type:
|
7
|
+
range: Диапазон
|
8
|
+
volume_prices: Цены в зависимости от количества
|
9
|
+
volume_pricing: Цена в зависимости от количества
|
10
|
+
price_discount:
|
11
|
+
percent_discount:
|
12
|
+
bulk_discount:
|
13
|
+
activerecord:
|
14
|
+
errors:
|
15
|
+
messages:
|
16
|
+
could_not_conver_to_range:
|
17
|
+
is_not_a_valid_volume_price_type:
|
18
|
+
must_be_in_format:
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
sv:
|
3
|
+
spree:
|
4
|
+
add_volume_price: Lägg till volympris
|
5
|
+
position: Position
|
6
|
+
discount_type: Rabatt typ
|
7
|
+
range: Omfång
|
8
|
+
volume_prices: Volympriser
|
9
|
+
volume_pricing: Volympris
|
10
|
+
price_discount: Rabatt pris
|
11
|
+
percent_discount: Procent rabatt
|
12
|
+
bulk_discount: Bulk rabatt
|
13
|
+
activerecord:
|
14
|
+
errors:
|
15
|
+
messages:
|
16
|
+
could_not_conver_to_range: "Det gick inte att konvertera till omfång: %{number}"
|
17
|
+
is_not_a_valid_volume_price_type: '%{value} är inte en giltligt typ för volympris'
|
18
|
+
must_be_in_format: 'måste vara i ett av följande format: (a..b), (a...b), (a+)'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
tr:
|
3
|
+
spree:
|
4
|
+
add_volume_price: Toplu Fiyat Ekle
|
5
|
+
position: Konum
|
6
|
+
discount_type: İndirim Türü
|
7
|
+
range: Aralık
|
8
|
+
volume_prices: Toplu Fiyatlar
|
9
|
+
volume_pricing: Toplu Fiyatlandırma
|
10
|
+
price_discount: Fiyat İndirimi
|
11
|
+
percent_discount: Yüzdelik İndirim
|
12
|
+
bulk_discount:
|
13
|
+
activerecord:
|
14
|
+
errors:
|
15
|
+
messages:
|
16
|
+
could_not_conver_to_range:
|
17
|
+
is_not_a_valid_volume_price_type:
|
18
|
+
must_be_in_format:
|
data/config/routes.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Spree::Core::Engine.add_routes do
|
2
|
+
namespace :admin do
|
3
|
+
resources :products do
|
4
|
+
resources :variants do
|
5
|
+
get :volume_prices, on: :member
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
delete '/volume_prices/:id', to: 'volume_prices#destroy', as: :volume_price
|
10
|
+
resources :volume_price_models
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateVolumePrices < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :volume_prices do |t|
|
4
|
+
t.references :variant
|
5
|
+
t.string :display
|
6
|
+
t.string :range
|
7
|
+
t.decimal :amount, precision: 8, scale: 2
|
8
|
+
t.integer :position
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :volume_prices
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateSpreeVolumePriceModels < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :spree_volume_price_models do |t|
|
4
|
+
t.string :name
|
5
|
+
t.timestamps
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :spree_variants_volume_price_models do |t|
|
9
|
+
t.belongs_to :volume_price_model
|
10
|
+
t.belongs_to :variant
|
11
|
+
end
|
12
|
+
|
13
|
+
add_reference :spree_volume_prices, :volume_price_model
|
14
|
+
|
15
|
+
add_index :spree_variants_volume_price_models, :volume_price_model_id, name: 'volume_price_model_id'
|
16
|
+
add_index :spree_variants_volume_price_models, :variant_id, name: 'variant_id'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SolidusVolumePricing
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
5
|
+
|
6
|
+
def add_javascripts
|
7
|
+
append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_volume_pricing\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_migrations
|
11
|
+
run 'bundle exec rake railties:install:migrations FROM=solidus_volume_pricing'
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_migrations
|
15
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
|
16
|
+
if run_migrations
|
17
|
+
run 'bundle exec rake db:migrate'
|
18
|
+
else
|
19
|
+
puts 'Skipping rake db:migrate, don\'t forget to run it!'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module SolidusVolumePricing
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
isolate_namespace Spree
|
4
|
+
engine_name 'solidus_volume_pricing'
|
5
|
+
|
6
|
+
initializer 'solidus_volume_pricing.preferences', before: 'spree.environment' do
|
7
|
+
Spree::AppConfiguration.class_eval do
|
8
|
+
preference :use_master_variant_volume_pricing, :boolean, default: false
|
9
|
+
preference :volume_pricing_role, :string, default: 'wholesale'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.activate
|
14
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
|
15
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
16
|
+
end
|
17
|
+
|
18
|
+
String.class_eval do
|
19
|
+
def to_range
|
20
|
+
case count('.')
|
21
|
+
when 2
|
22
|
+
elements = split('..')
|
23
|
+
return Range.new(elements[0].delete('(').to_i, elements[1].to_i)
|
24
|
+
when 3
|
25
|
+
elements = split('...')
|
26
|
+
return Range.new(elements[0].delete('(').to_i, elements[1].to_i - 1)
|
27
|
+
else
|
28
|
+
raise ArgumentError.new(
|
29
|
+
I18n.t(
|
30
|
+
:'activerecord.errors.messages.could_not_conver_to_range',
|
31
|
+
number: self
|
32
|
+
)
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
config.autoload_paths += %W(#{config.root}/lib)
|
40
|
+
config.to_prepare(&method(:activate).to_proc)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SolidusVolumePricing
|
2
|
+
module_function
|
3
|
+
|
4
|
+
# Returns the version of the currently loaded SolidusVolumePricing as a
|
5
|
+
# <tt>Gem::Version</tt>.
|
6
|
+
def version
|
7
|
+
Gem::Version.new VERSION::STRING
|
8
|
+
end
|
9
|
+
|
10
|
+
module VERSION
|
11
|
+
MAJOR = 0
|
12
|
+
MINOR = 1
|
13
|
+
TINY = 0
|
14
|
+
PRE = nil
|
15
|
+
|
16
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
require 'solidus_volume_pricing/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.name = 'solidus_volume_pricing'
|
9
|
+
s.version = SolidusVolumePricing.version
|
10
|
+
s.summary = 'Allow prices to be configured in quantity ranges for each variant'
|
11
|
+
s.description = s.summary
|
12
|
+
s.required_ruby_version = '>= 2.2.3'
|
13
|
+
|
14
|
+
s.author = 'Sean Schofield'
|
15
|
+
s.email = 'sean@railsdog.com'
|
16
|
+
s.homepage = 'https://github.com/solidusio-contrib/solidus_volume_pricing'
|
17
|
+
s.license = 'BSD-3'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.requirements << 'none'
|
23
|
+
|
24
|
+
s.add_runtime_dependency 'solidus_core', '~> 1.0', '< 1.3'
|
25
|
+
s.add_runtime_dependency 'deface', '~> 1.0'
|
26
|
+
|
27
|
+
s.add_development_dependency 'sqlite3', '>= 1.3.10'
|
28
|
+
s.add_development_dependency 'capybara', '~> 2.4'
|
29
|
+
s.add_development_dependency 'ffaker', '>= 1.32.1'
|
30
|
+
s.add_development_dependency 'shoulda-matchers'
|
31
|
+
s.add_development_dependency 'rspec-rails', '~> 3.2'
|
32
|
+
s.add_development_dependency 'simplecov', '~> 0.9'
|
33
|
+
s.add_development_dependency 'factory_girl', '~> 4.5'
|
34
|
+
s.add_development_dependency 'pry-rails', '>= 0.3'
|
35
|
+
s.add_development_dependency 'poltergeist', '~> 1.6'
|
36
|
+
s.add_development_dependency 'database_cleaner', '~> 1.4'
|
37
|
+
s.add_development_dependency 'coffee-rails', '~> 4.0'
|
38
|
+
s.add_development_dependency 'sass-rails', '~> 5.0'
|
39
|
+
s.add_development_dependency 'rubocop', '>= 0.24.1'
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
RSpec.describe Spree::Admin::VariantsController, type: :controller do
|
2
|
+
stub_authorization!
|
3
|
+
|
4
|
+
context 'PUT #update' do
|
5
|
+
it 'creates a volume price' do
|
6
|
+
variant = create :variant
|
7
|
+
|
8
|
+
expect do
|
9
|
+
spree_put :update,
|
10
|
+
product_id: variant.product.slug,
|
11
|
+
id: variant.id,
|
12
|
+
variant: {
|
13
|
+
'volume_prices_attributes' => {
|
14
|
+
'1335830259720' => {
|
15
|
+
'name' => '5-10',
|
16
|
+
'discount_type' => 'price',
|
17
|
+
'range' => '5..10',
|
18
|
+
'amount' => '90',
|
19
|
+
'position' => '1',
|
20
|
+
'_destroy' => 'false'
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end.to change(variant.volume_prices, :count).by(1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec.describe Spree::BaseHelper, type: :helper do
|
2
|
+
include Spree::BaseHelper
|
3
|
+
|
4
|
+
context 'volume pricing' do
|
5
|
+
before do
|
6
|
+
@variant = create :variant, price: 10
|
7
|
+
@variant.volume_prices.create! amount: 1, discount_type: 'dollar', range: '(10+)'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'gives discounted price' do
|
11
|
+
expect(display_volume_price(@variant, 10)).to eq '$9.00'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'gives discount percent' do
|
15
|
+
expect(display_volume_price_earning_percent(@variant, 10)).to eq '10'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'gives discount amount' do
|
19
|
+
expect(display_volume_price_earning_amount(@variant, 10)).to eq '$1.00'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
RSpec.describe Spree::LineItem, type: :model do
|
2
|
+
before do
|
3
|
+
@order = create(:order)
|
4
|
+
@variant = create(:variant, price: 10)
|
5
|
+
@variant.volume_prices.create! amount: 9, discount_type: 'price', range: '(2+)'
|
6
|
+
@order.contents.add(@variant, 1)
|
7
|
+
@line_item = @order.line_items.first
|
8
|
+
@role = create(:role)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'updates the line item price when the quantity changes to match a range and has no role' do
|
12
|
+
expect(@line_item.price.to_f).to be(10.00)
|
13
|
+
@order.contents.add(@variant, 1)
|
14
|
+
expect(@order.line_items.first.price.to_f).to be(9.00)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'updates the line item price when the quantity changes to match a range and role matches' do
|
18
|
+
@order.user.spree_roles << @role
|
19
|
+
Spree::Config.volume_pricing_role = @role.name
|
20
|
+
expect(@order.user.has_spree_role? @role.name.to_sym).to be(true)
|
21
|
+
@variant.volume_prices.first.update(role_id: @role.id)
|
22
|
+
expect(@line_item.price.to_f).to be(10.00)
|
23
|
+
@order.contents.add(@variant, 1)
|
24
|
+
expect(@order.line_items.first.price.to_f).to be(9.00)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does not update the line item price when the variant role and order role don`t match' do
|
28
|
+
expect(@order.user.has_spree_role? @role.name.to_sym).to be(false)
|
29
|
+
@variant.volume_prices.first.update(role_id: @role.id)
|
30
|
+
expect(@line_item.price.to_f).to be(10.00)
|
31
|
+
@order.contents.add(@variant, 1)
|
32
|
+
expect(@order.line_items.first.price.to_f).to be(10.00)
|
33
|
+
end
|
34
|
+
end
|