post_clerk 0.4

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 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