solidus_volume_pricing 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|