post_clerk 0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2547bc9e7d5898e2d20e5a7216d0a20c731311c3
4
+ data.tar.gz: 8719f905321df84630e7aa5436bca8d18cb2b643
5
+ SHA512:
6
+ metadata.gz: a2fd111ec8c4a03fa8635c3574d2a5ba6bd0d4a027ffc6d55445d70286a12be53b649f8326636645e9b109f64e1f74e9a09709652f29d9a488da06f9975bd3c8
7
+ data.tar.gz: 108a6308e74cc9d81cf8a13fe46204f517c886fbf1fd247540ca8edb43b1aaf01a61f097f27d02a1b92671ac3e4d2b3dee9813789914698f2370c5fdf818208b
data/.gitignore ADDED
@@ -0,0 +1,31 @@
1
+ *.rbc
2
+ capybara-*.html
3
+ .rspec
4
+ /log
5
+ /tmp
6
+ /db/*.sqlite3
7
+ /public/system
8
+ /coverage/
9
+ /spec/tmp
10
+ **.orig
11
+ rerun.txt
12
+ pickle-email-*.html
13
+
14
+ # TODO Comment out these rules if you are OK with secrets being uploaded to the repo
15
+ config/initializers/secret_token.rb
16
+ config/secrets.yml
17
+
18
+ ## Environment normalisation:
19
+ /.bundle
20
+ /vendor/bundle
21
+
22
+ # these should all be checked in to normalise the environment:
23
+ # Gemfile.lock, .ruby-version, .ruby-gemset
24
+
25
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
26
+ .rvmrc
27
+
28
+ # if using bower-rails ignore default bower_components path bower.json files
29
+ /vendor/assets/bower_components
30
+ *.bowerrc
31
+ bower.json
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gem 'office_clerk', github: 'rubyclerks/office_clerk'
4
+
5
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ group :red_green_refactor, halt_on_fail: true do
2
+
3
+ guard 'rspec', cmd: 'bundle exec rspec' do
4
+ watch('spec/spec_helper.rb') { 'spec' }
5
+ watch(%r{^spec/(.+)_spec\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ end
8
+
9
+ guard :rubocop, all_on_start: false, cli: ['--format', 'clang', '--rails'] do
10
+ watch(%r{.+\.rb$})
11
+ watch(%r{(?:.+/)?\.(rubocop|hound)\.yml$}) { |m| File.dirname(m[0]) }
12
+ end
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2011-2014 Torsten Rüger, Tobias Bohwalli and other contributors. All rights reserved.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # Post Clerk
2
+
3
+ A postal service is delivers based on weight only. Like most post services in Europe will.
4
+
5
+ This extension adds a office_clerk/shipping_method model to do this.
6
+
7
+ **Features:**
8
+
9
+ - Weight restrictions can be specified.
10
+ - You specify a weight/price table.
11
+ - Handling fee may be added ( with a maximum when it won't be applied anymore).
12
+ - Multi-parcel shipments are automatically created.
13
+ - You can specify a maximum order price, orders over this will not be charged.
14
+
15
+ Off course this relies on your weight data to be correct. Use the same measurements as in the product info page consistantly (either gram or kilogram).
16
+
17
+ ## Usage
18
+
19
+ Add to your `Gemfile`:
20
+ ```ruby
21
+ gem 'post_clerk', github: 'ruby_clerks_/post_clerk'
22
+ ```
23
+
24
+ Add a config/locale/config.yml with the data for the calculator (s)
25
+
26
+
27
+ ---
28
+
29
+ ## Example
30
+
31
+ With the default settings (measurements in kg and cm):
32
+
33
+ - Max weight of one item: 18
34
+ - Default weight: 1kg (applies when product weight is 0)
35
+ - Handling fee: 0
36
+ - Amount, over which handling fee won't be applied: 50
37
+ - Max total of the order: 120.0
38
+ - Weights (space separated): 1 2 5 10 20
39
+ - Prices (space separated): 2 5 10 15 18
40
+
41
+
42
+ The Shipping method does not apply to the order if any items weighs more than 18 kg.
43
+
44
+ ## Cost examples
45
+
46
+ - Items weighing 10 kg of worth 100 Euros will cost 15 Euros.
47
+ - Items weighing 10 kg of worth 40 Euros will cost 25 Euros (15 + 10 handling).
48
+ - Items weighing less than 1 kg of worth 60 Euros will cost 6 Euros.
49
+ - Items weighing less than 1 kg of worth 40 Euros will cost 16 Euros (6 + 10).
50
+ - Items weighing 25 kg of worth 200 Euros will cost 30 Euros (2 packages, 18 + 12 Euro).
51
+ - 3 items without weight information of worth 100 euros will cost 12 Euro.
52
+ - Any amount of items costing more than the max_price will cost 0 Euro.
53
+
54
+ ---
55
+
56
+
57
+ ## License
58
+
59
+ Copyright (c) 2011-2014 [Torsten Rüger][1], [Tobias Bohwalli][2] , released under the [MIT License][3].
60
+
61
+ [1]: https://github.com/dancinglightning
62
+ [2]: https://github.com/futhr
63
+ [3]: https://github.com/ruby_clerks/post_clerk/blob/master/LICENSE
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+ Bundler.setup
4
+
5
+ require 'rspec/core/rake_task'
6
+ require 'office_clerk/testing_support/common_rake'
7
+
8
+ RSpec::Core::RakeTask.new
9
+
10
+ task default: [:spec]
11
+
12
+ desc 'Generates a dummy app for testing'
13
+ task :test_app do
14
+ ENV['LIB_NAME'] = 'post_clerk'
15
+ Rake::Task['common:test_app'].invoke
16
+ end
data/bin/rails ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/post_clerk/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,12 @@
1
+ ---
2
+ en:
3
+ office_clerk:
4
+ price_table: Prices (space seperated)
5
+ weight_table: Weights (space seperated)
6
+ postal_service: Postal Service
7
+ max_item_weight: Max weight of one item
8
+ handling_fee: Handling fee
9
+ handling_max: "Amount, over which handling fee won't be applied"
10
+ min_price: Minimum price
11
+ default_weight: Default weight
12
+ max_price: Maximum price
@@ -0,0 +1,68 @@
1
+ module OfficeClerk
2
+ class Post < ShippingMethod
3
+
4
+ DEFAULTS ={ :weight_table => '1 2 5 10 20' ,
5
+ :price_table => '2 5 10 15 18' ,
6
+ :max_item_weight => "18" ,
7
+ :max_price => "120" ,
8
+ :handling_max => "20" ,
9
+ :handling_fee => "2" ,
10
+ :default_weight => "1" }
11
+
12
+ def initialize data
13
+ super DEFAULTS.merge(data)
14
+ @prices = @data[:price_table].split.map(&:to_f)
15
+ @weights = @data[:weight_table].split.map(&:to_f)
16
+ @max_item_weight = @data[:max_item_weight].to_f || 18.0
17
+ @max_price = @data[:max_price].to_f || 120.0
18
+ @handling_max = @data[:handling_max].to_f || 20.0
19
+ @handling_fee = @data[:handling_fee].to_f || 2.0
20
+ @default_weight = @data[:default_weight].to_f || 1.0
21
+ raise "Could not parse weights #{@data[:weight_table]} for #{key}" if @weights.empty?
22
+ raise "Could not parse weights #{@data[:weight_table]} for #{key}" if @weights.include?(nil)
23
+ raise "Could not parse prices #{@data[:price_table]} for #{key}" if @prices.empty?
24
+ raise "Could not parse prices #{@data[:price_table]} for #{key}" if @prices.include?(nil)
25
+ end
26
+ attr_reader :prices , :weights , :max_item_weight , :max_price , :handling_fee , :handling_max , :default_weight
27
+
28
+ # Determine if weight or size goes over bounds.
29
+ def available?(basket)
30
+ basket.items.each do |item|
31
+ if( weight = item.product.weight)
32
+ return false if weight > max_item_weight
33
+ end
34
+ end
35
+ true
36
+ end
37
+
38
+ def price_for(basket)
39
+ total_price = basket.total_price
40
+ return 0.0 if total_price > max_price
41
+
42
+ total_weight = basket.items.map {|item| item.quantity * (item.product.weight || default_weight) }.reduce(:+) || 0.0
43
+ shipping = 0
44
+
45
+ while total_weight > weights.last # In several packets if need be.
46
+ total_weight -= weights.last
47
+ shipping += prices.last
48
+ end
49
+
50
+ [shipping, prices[compute_index(total_weight)], calc_handling_fee(total_price)].compact.sum
51
+ end
52
+
53
+ private
54
+
55
+ def compute_index(total_weight)
56
+ index = weights.length - 2
57
+ while index >= 0
58
+ break if total_weight > weights[index]
59
+ index -= 1
60
+ end
61
+ index + 1
62
+ end
63
+
64
+ def calc_handling_fee(total_price)
65
+ handling_max < total_price ? 0 : handling_fee
66
+ end
67
+ end
68
+ end
data/lib/post_clerk.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'office_clerk'
2
+ module PostClerk
3
+ class Engine < Rails::Engine
4
+ engine_name 'post_clerk'
5
+
6
+ config.autoload_paths += %W(#{config.root}/lib)
7
+
8
+ initializer 'post_clerk' do |app|
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.platform = Gem::Platform::RUBY
7
+ s.name = 'post_clerk'
8
+ s.version = "0.4"
9
+ s.summary = 'Calculate weight based charges for a OfficeClerk basket'
10
+ s.description = s.summary
11
+ s.required_ruby_version = '>= 1.9.3'
12
+
13
+ s.authors = ['Torsten Rüger', 'Tobias Bohwalli']
14
+ s.email = ['torsten@villataika.fi', 'hi@futhr.io']
15
+ s.homepage = 'https://github.com/ruby_clerks/post_clerk'
16
+ s.license = 'MIT'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- spec/*`.split("\n")
20
+ s.require_path = 'lib'
21
+ s.requirements << 'none'
22
+
23
+ s.has_rdoc = false
24
+
25
+ s.add_runtime_dependency 'office_clerk', '~> 0.1'
26
+
27
+ end
@@ -0,0 +1,115 @@
1
+ RSpec.describe OfficeClerk::Post do
2
+
3
+ subject { described_class.new }
4
+
5
+ context 'returns description' do
6
+ %w(en fi).each do |locale|
7
+ it "in supported language: #{locale}" do
8
+ I18n.with_locale(locale.to_sym) do
9
+ expect(described_class.description).to eq I18n.t(:postal_service)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ describe 'using the default weight-price table: [1 2 5 10 20] => [6 9 12 15 18]' do
16
+ context '.compute(package)' do
17
+ it 'gives 15.0 when total price is 100 and weight is 10kg' do
18
+ create_our_package(weight: 10.0, price: 100.0, quantity: 1)
19
+ result = subject.compute(@basket)
20
+ expect(result).to eq(15.0)
21
+ end
22
+
23
+ it 'gives 25.0 when total price is 40 and weight is 10kg' do
24
+ create_our_package(weight: 10.0, price: 40.0, quantity: 1)
25
+ result = subject.compute(@basket)
26
+ expect(result).to eq(25.0)
27
+ end
28
+
29
+ it 'gives 6 when total price is 60 and weight is less than 1kg' do
30
+ create_our_package(weight: 0.5, price: 60.0, quantity: 1)
31
+ result = subject.compute(@basket)
32
+ expect(result).to eq(6.0)
33
+ end
34
+
35
+ it 'gives 16 when total price is 40 and weight is less than 1kg' do
36
+ create_our_package(weight: 0.5, price: 40.0, quantity: 1)
37
+ result = subject.compute(@basket)
38
+ expect(result).to eq(16.0)
39
+ end
40
+
41
+ it 'gives 30 when total price is 200 and weight is 25kg (split into two)' do
42
+ create_our_package(weight: 25.0, price: 200.0, quantity: 1)
43
+ subject.preferred_max_price = 250
44
+ result = subject.compute(@basket)
45
+ expect(result).to eq(30.0)
46
+ end
47
+
48
+ it 'gives 12 when total price is 100, there are three items and their weight is unknown' do
49
+ order = create(:order)
50
+ variant = create(:base_variant, weight: nil)
51
+ [30.0, 40.0, 30.0].each { |price| create_line_item(order, variant, price: price) }
52
+ order.line_items.reload
53
+ package = create(:shipment, order: order)
54
+ result = subject.compute(package)
55
+ expect(result).to eq(12.0)
56
+ end
57
+
58
+ it 'gives 0 when total price is more than the MAX, for any number of items' do
59
+ create_our_package(weight: 25.0, price: 350.0, quantity: 1)
60
+ subject.preferred_max_price = 300
61
+ result = subject.compute(@basket)
62
+ expect(result).to eq(0.0)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe 'when preferred max weight, length and width are 18 kg, 120 cm and 60 cm' do
68
+ context '.available?(package)' do
69
+ it 'is false when item weighs more than 18kg' do
70
+ create_our_package(weight: 20, height: 70, width: 30, depth: 30)
71
+ expect(subject.available?(@basket)).to be(false)
72
+ end
73
+
74
+ it 'is false when item is longer than 120cm' do
75
+ create_our_package(weight: 10, height: 130, width: 30, depth: 30)
76
+ expect(subject.available?(@basket)).to be(false)
77
+ end
78
+
79
+ it 'is false when item is wider than 60cm' do
80
+ create_our_package(weight: 10, height: 80, width: 70, depth: 30)
81
+ expect(subject.available?(@basket)).to be(false)
82
+ end
83
+ end
84
+
85
+ context '.item_oversized?(variant)' do
86
+ it 'is true if the longest side is more than 120cm' do
87
+ create_our_package(weight: 10, height: 130, width: 40, depth: 30)
88
+ expect(subject.item_oversized?(@variant)).to be(true)
89
+ end
90
+
91
+ it 'is true if the second longest side is more than 60cm' do
92
+ create_our_package(weight: 10, height: 80, width: 70, depth: 30)
93
+ expect(subject.item_oversized?(@variant)).to be(true)
94
+ end
95
+ end
96
+ end
97
+
98
+ def create_our_package(args = {})
99
+ @variant = create :base_variant, args.except!(:quantity)
100
+ order = create :order
101
+ create_line_item(order, @variant, args)
102
+ order.line_items.reload
103
+ @basket = create :shipment, order: order
104
+ end
105
+
106
+ def create_line_item(order, variant, args = {})
107
+ create(
108
+ :line_item,
109
+ price: args[:price] || BigDecimal.new('10.00'),
110
+ quantity: args[:quantity] || 1,
111
+ order: order,
112
+ variant: variant
113
+ )
114
+ end
115
+ end
@@ -0,0 +1,40 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ Coveralls::SimpleCov::Formatter
6
+ ]
7
+ SimpleCov.start do
8
+ add_filter '/.bundle'
9
+ add_filter '/spec/'
10
+ add_filter '/lib/post_clerk/engine'
11
+ add_group 'Libraries', 'lib'
12
+ end
13
+
14
+ ENV['RAILS_ENV'] ||= 'test'
15
+
16
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
17
+
18
+ require 'pry'
19
+ require 'ffaker'
20
+ require 'rspec/rails'
21
+
22
+ ActiveRecord::Migration.maintain_test_schema!
23
+ ActiveRecord::Migration.check_pending!
24
+
25
+ RSpec.configure do |config|
26
+
27
+ config.fail_fast = false
28
+ config.filter_run focus: true
29
+ config.run_all_when_everything_filtered = true
30
+
31
+ config.mock_with :rspec
32
+ config.raise_errors_for_deprecations!
33
+ config.infer_spec_type_from_file_location!
34
+
35
+ config.expect_with :rspec do |expectations|
36
+ expectations.syntax = :expect
37
+ end
38
+ end
39
+
40
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |file| require file }
@@ -0,0 +1,18 @@
1
+ require 'database_cleaner'
2
+
3
+ RSpec.configure do |config|
4
+
5
+ config.before(:suite) do
6
+ DatabaseCleaner.strategy = :transaction
7
+ DatabaseCleaner.clean_with :truncation
8
+ end
9
+
10
+ config.before do
11
+ DatabaseCleaner.strategy = :transaction
12
+ DatabaseCleaner.start
13
+ end
14
+
15
+ config.after do
16
+ DatabaseCleaner.clean
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ require 'factory_girl'
2
+
3
+ RSpec.configure do |config|
4
+ config.include FactoryGirl::Syntax::Methods
5
+ end
@@ -0,0 +1,9 @@
1
+ require 'rspec'
2
+ require 'i18n-spec'
3
+
4
+ RSpec.configure do |config|
5
+
6
+ config.before(:suite) do
7
+ I18n.locale = :en
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe 'locale:' do
2
+
3
+ Dir.glob('config/locales/*.yml') do |locale_file|
4
+ context locale_file do
5
+ it { is_expected.to be_parseable }
6
+ it { is_expected.to have_valid_pluralization_keys }
7
+ it { is_expected.not_to have_missing_pluralization_keys }
8
+ it { is_expected.to have_one_top_level_namespace }
9
+ it { is_expected.not_to have_legacy_interpolations }
10
+ it { is_expected.to have_a_valid_locale }
11
+ it { is_expected.to be_a_complete_translation_of 'config/locales/en.yml' }
12
+ end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: post_clerk
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.4'
5
+ platform: ruby
6
+ authors:
7
+ - Torsten Rüger
8
+ - Tobias Bohwalli
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-11-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: office_clerk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '0.1'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '0.1'
28
+ description: Calculate weight based charges for a OfficeClerk basket
29
+ email:
30
+ - torsten@villataika.fi
31
+ - hi@futhr.io
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - Gemfile
38
+ - Guardfile
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - bin/rails
43
+ - config/locales/en.yml
44
+ - lib/office_clerk/post.rb
45
+ - lib/post_clerk.rb
46
+ - post_clerk.gemspec
47
+ - spec/lib/post_spec.rb
48
+ - spec/spec_helper.rb
49
+ - spec/support/database_cleaner.rb
50
+ - spec/support/factory_girl.rb
51
+ - spec/support/i18n.rb
52
+ - spec/translations/locale_spec.rb
53
+ homepage: https://github.com/ruby_clerks/post_clerk
54
+ licenses:
55
+ - MIT
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 1.9.3
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements:
72
+ - none
73
+ rubyforge_project:
74
+ rubygems_version: 2.2.2
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: Calculate weight based charges for a OfficeClerk basket
78
+ test_files:
79
+ - spec/lib/post_spec.rb
80
+ - spec/spec_helper.rb
81
+ - spec/support/database_cleaner.rb
82
+ - spec/support/factory_girl.rb
83
+ - spec/support/i18n.rb
84
+ - spec/translations/locale_spec.rb