friendly_shipping 0.2.3 → 0.2.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 +4 -4
- data/.circleci/config.yml +4 -0
- data/.rubocop-relaxed.yml +170 -0
- data/.rubocop.yml +8 -0
- data/Gemfile +7 -4
- data/Rakefile +3 -1
- data/bin/console +1 -0
- data/friendly_shipping.gemspec +6 -4
- data/lib/friendly_shipping.rb +5 -1
- data/lib/friendly_shipping/bad_request.rb +3 -1
- data/lib/friendly_shipping/carrier.rb +6 -0
- data/lib/friendly_shipping/label.rb +14 -12
- data/lib/friendly_shipping/rate.rb +3 -0
- data/lib/friendly_shipping/request.rb +2 -0
- data/lib/friendly_shipping/response.rb +2 -0
- data/lib/friendly_shipping/services/ship_engine.rb +6 -5
- data/lib/friendly_shipping/services/ship_engine/client.rb +12 -10
- data/lib/friendly_shipping/services/ship_engine/parse_carrier_response.rb +10 -8
- data/lib/friendly_shipping/services/ship_engine/parse_label_response.rb +2 -0
- data/lib/friendly_shipping/services/ship_engine/parse_rate_estimate_response.rb +4 -0
- data/lib/friendly_shipping/services/ship_engine/parse_void_response.rb +2 -0
- data/lib/friendly_shipping/services/ship_engine/serialize_label_shipment.rb +12 -12
- data/lib/friendly_shipping/services/ship_engine/serialize_rate_estimate_request.rb +2 -0
- data/lib/friendly_shipping/services/ups.rb +67 -0
- data/lib/friendly_shipping/services/ups/client.rb +38 -0
- data/lib/friendly_shipping/services/ups/parse_rate_response.rb +44 -0
- data/lib/friendly_shipping/services/ups/parse_xml_response.rb +40 -0
- data/lib/friendly_shipping/services/ups/serialize_access_request.rb +22 -0
- data/lib/friendly_shipping/services/ups/serialize_address_snippet.rb +52 -0
- data/lib/friendly_shipping/services/ups/serialize_package_node.rb +49 -0
- data/lib/friendly_shipping/services/ups/serialize_rating_service_selection_request.rb +87 -0
- data/lib/friendly_shipping/services/ups/shipping_methods.rb +107 -0
- data/lib/friendly_shipping/shipping_method.rb +13 -2
- data/lib/friendly_shipping/version.rb +3 -1
- metadata +46 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8c1f8749c1f3c1199a3ff41ec10d3a1d92f78dd
|
4
|
+
data.tar.gz: f22645471497c2e0350659b4d1dd9cb35e799f22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc5b536db417d7d087ae26a3121f61438b510b14b4fc8d94b13d3e25397f7e8f1e1b4c2cdaa18f13a4265c9aad1f6cdd1dcd6a3424bd6822d2ef8dc72a78cb7b
|
7
|
+
data.tar.gz: f65dd002fe51c318074e6d4ae841f56d80637f7997d1becfe935e4d75f0ccc9da0d7df8746abbc38d6851068490b21d8182e67d26b912f3a5cc0995855f2fde9
|
data/.circleci/config.yml
CHANGED
@@ -0,0 +1,170 @@
|
|
1
|
+
# Relaxed.Ruby.Style
|
2
|
+
## Version 2.2
|
3
|
+
|
4
|
+
Style/Alias:
|
5
|
+
Enabled: false
|
6
|
+
StyleGuide: https://relaxed.ruby.style/#stylealias
|
7
|
+
|
8
|
+
Style/AsciiComments:
|
9
|
+
Enabled: false
|
10
|
+
StyleGuide: https://relaxed.ruby.style/#styleasciicomments
|
11
|
+
|
12
|
+
Style/BeginBlock:
|
13
|
+
Enabled: false
|
14
|
+
StyleGuide: https://relaxed.ruby.style/#stylebeginblock
|
15
|
+
|
16
|
+
Style/BlockDelimiters:
|
17
|
+
Enabled: false
|
18
|
+
StyleGuide: https://relaxed.ruby.style/#styleblockdelimiters
|
19
|
+
|
20
|
+
Style/CommentAnnotation:
|
21
|
+
Enabled: false
|
22
|
+
StyleGuide: https://relaxed.ruby.style/#stylecommentannotation
|
23
|
+
|
24
|
+
Style/Documentation:
|
25
|
+
Enabled: false
|
26
|
+
StyleGuide: https://relaxed.ruby.style/#styledocumentation
|
27
|
+
|
28
|
+
Layout/DotPosition:
|
29
|
+
Enabled: false
|
30
|
+
StyleGuide: https://relaxed.ruby.style/#layoutdotposition
|
31
|
+
|
32
|
+
Style/DoubleNegation:
|
33
|
+
Enabled: false
|
34
|
+
StyleGuide: https://relaxed.ruby.style/#styledoublenegation
|
35
|
+
|
36
|
+
Style/EndBlock:
|
37
|
+
Enabled: false
|
38
|
+
StyleGuide: https://relaxed.ruby.style/#styleendblock
|
39
|
+
|
40
|
+
Style/FormatString:
|
41
|
+
Enabled: false
|
42
|
+
StyleGuide: https://relaxed.ruby.style/#styleformatstring
|
43
|
+
|
44
|
+
Style/IfUnlessModifier:
|
45
|
+
Enabled: false
|
46
|
+
StyleGuide: https://relaxed.ruby.style/#styleifunlessmodifier
|
47
|
+
|
48
|
+
Style/Lambda:
|
49
|
+
Enabled: false
|
50
|
+
StyleGuide: https://relaxed.ruby.style/#stylelambda
|
51
|
+
|
52
|
+
Style/ModuleFunction:
|
53
|
+
Enabled: false
|
54
|
+
StyleGuide: https://relaxed.ruby.style/#stylemodulefunction
|
55
|
+
|
56
|
+
Style/MultilineBlockChain:
|
57
|
+
Enabled: false
|
58
|
+
StyleGuide: https://relaxed.ruby.style/#stylemultilineblockchain
|
59
|
+
|
60
|
+
Style/NegatedIf:
|
61
|
+
Enabled: false
|
62
|
+
StyleGuide: https://relaxed.ruby.style/#stylenegatedif
|
63
|
+
|
64
|
+
Style/NegatedWhile:
|
65
|
+
Enabled: false
|
66
|
+
StyleGuide: https://relaxed.ruby.style/#stylenegatedwhile
|
67
|
+
|
68
|
+
Style/ParallelAssignment:
|
69
|
+
Enabled: false
|
70
|
+
StyleGuide: https://relaxed.ruby.style/#styleparallelassignment
|
71
|
+
|
72
|
+
Style/PercentLiteralDelimiters:
|
73
|
+
Enabled: false
|
74
|
+
StyleGuide: https://relaxed.ruby.style/#stylepercentliteraldelimiters
|
75
|
+
|
76
|
+
Style/PerlBackrefs:
|
77
|
+
Enabled: false
|
78
|
+
StyleGuide: https://relaxed.ruby.style/#styleperlbackrefs
|
79
|
+
|
80
|
+
Style/Semicolon:
|
81
|
+
Enabled: false
|
82
|
+
StyleGuide: https://relaxed.ruby.style/#stylesemicolon
|
83
|
+
|
84
|
+
Style/SignalException:
|
85
|
+
Enabled: false
|
86
|
+
StyleGuide: https://relaxed.ruby.style/#stylesignalexception
|
87
|
+
|
88
|
+
Style/SingleLineBlockParams:
|
89
|
+
Enabled: false
|
90
|
+
StyleGuide: https://relaxed.ruby.style/#stylesinglelineblockparams
|
91
|
+
|
92
|
+
Style/SingleLineMethods:
|
93
|
+
Enabled: false
|
94
|
+
StyleGuide: https://relaxed.ruby.style/#stylesinglelinemethods
|
95
|
+
|
96
|
+
Layout/SpaceBeforeBlockBraces:
|
97
|
+
Enabled: false
|
98
|
+
StyleGuide: https://relaxed.ruby.style/#layoutspacebeforeblockbraces
|
99
|
+
|
100
|
+
Layout/SpaceInsideParens:
|
101
|
+
Enabled: false
|
102
|
+
StyleGuide: https://relaxed.ruby.style/#layoutspaceinsideparens
|
103
|
+
|
104
|
+
Style/SpecialGlobalVars:
|
105
|
+
Enabled: false
|
106
|
+
StyleGuide: https://relaxed.ruby.style/#stylespecialglobalvars
|
107
|
+
|
108
|
+
Style/StringLiterals:
|
109
|
+
Enabled: false
|
110
|
+
StyleGuide: https://relaxed.ruby.style/#stylestringliterals
|
111
|
+
|
112
|
+
Style/TrailingCommaInArguments:
|
113
|
+
Enabled: false
|
114
|
+
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarguments
|
115
|
+
|
116
|
+
Style/TrailingCommaInArrayLiteral:
|
117
|
+
Enabled: false
|
118
|
+
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarrayliteral
|
119
|
+
|
120
|
+
Style/TrailingCommaInHashLiteral:
|
121
|
+
Enabled: false
|
122
|
+
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainhashliteral
|
123
|
+
|
124
|
+
Style/SymbolArray:
|
125
|
+
Enabled: false
|
126
|
+
StyleGuide: http://relaxed.ruby.style/#stylesymbolarray
|
127
|
+
|
128
|
+
Style/WhileUntilModifier:
|
129
|
+
Enabled: false
|
130
|
+
StyleGuide: https://relaxed.ruby.style/#stylewhileuntilmodifier
|
131
|
+
|
132
|
+
Style/WordArray:
|
133
|
+
Enabled: false
|
134
|
+
StyleGuide: https://relaxed.ruby.style/#stylewordarray
|
135
|
+
|
136
|
+
Lint/AmbiguousRegexpLiteral:
|
137
|
+
Enabled: false
|
138
|
+
StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
|
139
|
+
|
140
|
+
Lint/AssignmentInCondition:
|
141
|
+
Enabled: false
|
142
|
+
StyleGuide: https://relaxed.ruby.style/#lintassignmentincondition
|
143
|
+
|
144
|
+
Metrics/AbcSize:
|
145
|
+
Enabled: false
|
146
|
+
|
147
|
+
Metrics/BlockNesting:
|
148
|
+
Enabled: false
|
149
|
+
|
150
|
+
Metrics/ClassLength:
|
151
|
+
Enabled: false
|
152
|
+
|
153
|
+
Metrics/ModuleLength:
|
154
|
+
Enabled: false
|
155
|
+
|
156
|
+
Metrics/CyclomaticComplexity:
|
157
|
+
Enabled: false
|
158
|
+
|
159
|
+
Metrics/LineLength:
|
160
|
+
Enabled: false
|
161
|
+
|
162
|
+
Metrics/MethodLength:
|
163
|
+
Enabled: false
|
164
|
+
|
165
|
+
Metrics/ParameterLists:
|
166
|
+
Enabled: false
|
167
|
+
|
168
|
+
Metrics/PerceivedComplexity:
|
169
|
+
Enabled: false
|
170
|
+
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
5
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
6
|
|
5
7
|
# Specify your gem's dependencies in friendly_shipping.gemspec
|
6
8
|
gemspec
|
7
9
|
|
8
|
-
gem 'vcr'
|
9
|
-
gem 'webmock'
|
10
|
-
gem 'pry'
|
11
10
|
gem 'dotenv'
|
12
11
|
gem 'factory_bot'
|
12
|
+
gem 'pry'
|
13
13
|
gem 'rspec_junit_formatter'
|
14
|
+
gem 'rubocop'
|
15
|
+
gem 'vcr'
|
16
|
+
gem 'webmock'
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
data/friendly_shipping.gemspec
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require "friendly_shipping/version"
|
5
6
|
|
@@ -21,11 +22,12 @@ Gem::Specification.new do |spec|
|
|
21
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
23
|
spec.require_paths = ["lib"]
|
23
24
|
|
24
|
-
spec.add_runtime_dependency "physical", "~> 0.3.0"
|
25
|
-
spec.add_runtime_dependency "rest-client", "~> 2.0"
|
26
|
-
spec.add_runtime_dependency "dry-monads", "~> 1.0"
|
27
25
|
spec.add_runtime_dependency "data_uri", "~> 0.0.3"
|
26
|
+
spec.add_runtime_dependency "dry-monads", "~> 1.0"
|
28
27
|
spec.add_runtime_dependency "money", ">= 6.0.0"
|
28
|
+
spec.add_runtime_dependency "nokogiri", ">= 1.6"
|
29
|
+
spec.add_runtime_dependency "physical", "~> 0.3.0"
|
30
|
+
spec.add_runtime_dependency "rest-client", "~> 2.0"
|
29
31
|
spec.required_ruby_version = '>= 2.4'
|
30
32
|
|
31
33
|
spec.add_development_dependency "bundler"
|
data/lib/friendly_shipping.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "physical"
|
2
4
|
|
3
5
|
require "friendly_shipping/version"
|
4
6
|
require "friendly_shipping/request"
|
5
7
|
require "friendly_shipping/response"
|
6
8
|
require "friendly_shipping/carrier"
|
7
|
-
require "friendly_shipping/services/ship_engine"
|
8
9
|
require "friendly_shipping/shipping_method"
|
9
10
|
require "friendly_shipping/label"
|
10
11
|
require "friendly_shipping/rate"
|
11
12
|
|
13
|
+
require "friendly_shipping/services/ship_engine"
|
14
|
+
require "friendly_shipping/services/ups"
|
15
|
+
|
12
16
|
module FriendlyShipping
|
13
17
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
|
3
5
|
module FriendlyShipping
|
@@ -16,7 +18,7 @@ module FriendlyShipping
|
|
16
18
|
parsed_body = JSON.parse(response.body)
|
17
19
|
messages = parsed_body.fetch('errors')&.map { |e| e.fetch('message') }
|
18
20
|
messages&.join(', ')
|
19
|
-
rescue JSON::ParserError, KeyError =>
|
21
|
+
rescue JSON::ParserError, KeyError => _e
|
20
22
|
nil
|
21
23
|
end
|
22
24
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FriendlyShipping
|
2
4
|
class Carrier
|
3
5
|
attr_reader :id, :name, :code, :shipping_methods, :balance, :data
|
@@ -10,5 +12,9 @@ module FriendlyShipping
|
|
10
12
|
@balance = balance
|
11
13
|
@data = data
|
12
14
|
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
id == other.id
|
18
|
+
end
|
13
19
|
end
|
14
20
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FriendlyShipping
|
2
4
|
class Label
|
3
5
|
attr_reader :id,
|
@@ -13,18 +15,18 @@ module FriendlyShipping
|
|
13
15
|
:original_response
|
14
16
|
|
15
17
|
def initialize(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
18
|
+
id: nil,
|
19
|
+
shipment_id: nil,
|
20
|
+
tracking_number: nil,
|
21
|
+
service_code: nil,
|
22
|
+
label_href: nil,
|
23
|
+
label_format: nil,
|
24
|
+
label_data: nil,
|
25
|
+
shipment_cost: nil,
|
26
|
+
data: {},
|
27
|
+
original_request: nil,
|
28
|
+
original_response: nil
|
29
|
+
)
|
28
30
|
@id = id
|
29
31
|
@shipment_id = shipment_id
|
30
32
|
@tracking_number = tracking_number
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FriendlyShipping
|
2
4
|
class Rate
|
3
5
|
class NoAmountsGiven < StandardError; end
|
@@ -35,6 +37,7 @@ module FriendlyShipping
|
|
35
37
|
|
36
38
|
def total_amount
|
37
39
|
raise NoAmountsGiven if amounts.empty?
|
40
|
+
|
38
41
|
amounts.map { |_name, amount| amount }.sum
|
39
42
|
end
|
40
43
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
require 'dry/monads/result'
|
3
4
|
require 'friendly_shipping/services/ship_engine/client'
|
@@ -15,9 +16,9 @@ module FriendlyShipping
|
|
15
16
|
API_PATHS = {
|
16
17
|
carriers: "carriers",
|
17
18
|
labels: "labels"
|
18
|
-
}
|
19
|
+
}.freeze
|
19
20
|
|
20
|
-
def initialize(token:, test: true, client:
|
21
|
+
def initialize(token:, test: true, client: Client)
|
21
22
|
@token = token
|
22
23
|
@test = test
|
23
24
|
@client = client
|
@@ -36,11 +37,11 @@ module FriendlyShipping
|
|
36
37
|
def rate_estimates(shipment, carriers)
|
37
38
|
request = FriendlyShipping::Request.new(
|
38
39
|
url: API_BASE + 'rates/estimate',
|
39
|
-
body: SerializeRateEstimateRequest.(shipment: shipment, carriers: carriers).to_json,
|
40
|
+
body: SerializeRateEstimateRequest.call(shipment: shipment, carriers: carriers).to_json,
|
40
41
|
headers: request_headers
|
41
42
|
)
|
42
43
|
client.post(request).fmap do |response|
|
43
|
-
ParseRateEstimateResponse.(response: response, request: request, carriers: carriers)
|
44
|
+
ParseRateEstimateResponse.call(response: response, request: request, carriers: carriers)
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
@@ -51,7 +52,7 @@ module FriendlyShipping
|
|
51
52
|
headers: request_headers
|
52
53
|
)
|
53
54
|
client.post(request).fmap do |response|
|
54
|
-
ParseLabelResponse.(request: request, response: response)
|
55
|
+
ParseLabelResponse.call(request: request, response: response)
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/monads/result'
|
2
4
|
require 'friendly_shipping/bad_request'
|
3
5
|
require 'rest-client'
|
@@ -14,8 +16,8 @@ module FriendlyShipping
|
|
14
16
|
)
|
15
17
|
|
16
18
|
Success(convert_to_friendly_response(http_response))
|
17
|
-
rescue ::RestClient::Exception =>
|
18
|
-
Failure(
|
19
|
+
rescue ::RestClient::Exception => e
|
20
|
+
Failure(e)
|
19
21
|
end
|
20
22
|
|
21
23
|
def post(friendly_shipping_request)
|
@@ -26,11 +28,11 @@ module FriendlyShipping
|
|
26
28
|
)
|
27
29
|
|
28
30
|
Success(convert_to_friendly_response(http_response))
|
29
|
-
rescue ::RestClient::Exception =>
|
30
|
-
if
|
31
|
-
Failure(BadRequest.new(
|
31
|
+
rescue ::RestClient::Exception => e
|
32
|
+
if e.http_code == 400
|
33
|
+
Failure(BadRequest.new(e))
|
32
34
|
else
|
33
|
-
Failure(
|
35
|
+
Failure(e)
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
@@ -42,11 +44,11 @@ module FriendlyShipping
|
|
42
44
|
)
|
43
45
|
|
44
46
|
Success(convert_to_friendly_response(http_response))
|
45
|
-
rescue ::RestClient::Exception =>
|
46
|
-
if
|
47
|
-
Failure(BadRequest.new(
|
47
|
+
rescue ::RestClient::Exception => e
|
48
|
+
if e.http_code == 400
|
49
|
+
Failure(BadRequest.new(e))
|
48
50
|
else
|
49
|
-
Failure(
|
51
|
+
Failure(e)
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
|
3
5
|
module FriendlyShipping
|
@@ -31,14 +33,14 @@ module FriendlyShipping
|
|
31
33
|
private
|
32
34
|
|
33
35
|
def parse_shipping_method(carrier, shipping_method_data)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
FriendlyShipping::ShippingMethod.new(
|
37
|
+
carrier: carrier,
|
38
|
+
name: shipping_method_data["name"],
|
39
|
+
service_code: shipping_method_data["service_code"],
|
40
|
+
domestic: shipping_method_data["domestic"],
|
41
|
+
international: shipping_method_data["international"],
|
42
|
+
multi_package: shipping_method_data["is_multi_package_supported"]
|
43
|
+
)
|
42
44
|
end
|
43
45
|
end
|
44
46
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'money'
|
3
5
|
|
@@ -11,8 +13,10 @@ module FriendlyShipping
|
|
11
13
|
parsed_json.map do |rate|
|
12
14
|
carrier = carriers.detect { |c| c.id == rate['carrier_id'] }
|
13
15
|
next unless carrier
|
16
|
+
|
14
17
|
shipping_method = carrier.shipping_methods.detect { |sm| sm.service_code == rate['service_code'] }
|
15
18
|
next unless shipping_method
|
19
|
+
|
16
20
|
amounts = get_amounts(rate)
|
17
21
|
FriendlyShipping::Rate.new(
|
18
22
|
shipping_method: shipping_method,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FriendlyShipping
|
2
4
|
module Services
|
3
5
|
class ShipEngine
|
@@ -20,7 +22,7 @@ module FriendlyShipping
|
|
20
22
|
}
|
21
23
|
}
|
22
24
|
if shipment.options[:carrier_id]
|
23
|
-
shipment_hash[:shipment]
|
25
|
+
shipment_hash[:shipment][:carrier_id] = shipment.options[:carrier_id]
|
24
26
|
end
|
25
27
|
shipment_hash
|
26
28
|
end
|
@@ -46,25 +48,23 @@ module FriendlyShipping
|
|
46
48
|
packages.map do |package|
|
47
49
|
package_hash = serialize_weight(package.weight)
|
48
50
|
if package.container.properties[:usps_label_messages]
|
49
|
-
package_hash
|
51
|
+
package_hash[:label_messages] = package.container.properties[:usps_label_messages]
|
50
52
|
end
|
51
53
|
package_code = package.container.properties[:usps_package_code]
|
52
54
|
if package_code
|
53
|
-
package_hash
|
55
|
+
package_hash[:package_code] = package_code
|
54
56
|
else
|
55
|
-
package_hash
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
}
|
62
|
-
)
|
57
|
+
package_hash[:dimensions] = {
|
58
|
+
unit: 'inch',
|
59
|
+
width: package.container.width.convert_to(:inches).value.to_f.round(2),
|
60
|
+
length: package.container.length.convert_to(:inches).value.to_f.round(2),
|
61
|
+
height: package.container.height.convert_to(:inches).value.to_f.round(2)
|
62
|
+
}
|
63
63
|
end
|
64
64
|
package_hash
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
def serialize_weight(weight)
|
69
69
|
ounces = weight.convert_to(:ounce).value.to_f
|
70
70
|
{
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/monads/result'
|
4
|
+
require 'friendly_shipping/services/ups/client'
|
5
|
+
require 'friendly_shipping/services/ups/serialize_access_request'
|
6
|
+
require 'friendly_shipping/services/ups/serialize_rating_service_selection_request'
|
7
|
+
require 'friendly_shipping/services/ups/parse_rate_response'
|
8
|
+
require 'friendly_shipping/services/ups/shipping_methods'
|
9
|
+
|
10
|
+
module FriendlyShipping
|
11
|
+
module Services
|
12
|
+
class Ups
|
13
|
+
include Dry::Monads::Result::Mixin
|
14
|
+
|
15
|
+
attr_reader :test, :key, :login, :password, :client
|
16
|
+
|
17
|
+
CARRIER = FriendlyShipping::Carrier.new(
|
18
|
+
id: 'ups',
|
19
|
+
name: 'United Parcel Service',
|
20
|
+
code: 'ups',
|
21
|
+
shipping_methods: SHIPPING_METHODS
|
22
|
+
)
|
23
|
+
|
24
|
+
TEST_URL = 'https://wwwcie.ups.com'
|
25
|
+
LIVE_URL = 'https://onlinetools.ups.com'
|
26
|
+
|
27
|
+
RESOURCES = {
|
28
|
+
rates: '/ups.app/xml/Rate'
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
def initialize(key:, login:, password:, test: true, client: Client)
|
32
|
+
@key = key
|
33
|
+
@login = login
|
34
|
+
@password = password
|
35
|
+
@test = test
|
36
|
+
@client = client
|
37
|
+
end
|
38
|
+
|
39
|
+
def carriers
|
40
|
+
Success([CARRIER])
|
41
|
+
end
|
42
|
+
|
43
|
+
def rate_estimates(shipment, _carriers)
|
44
|
+
rate_request_xml = SerializeRatingServiceSelectionRequest.call(shipment: shipment)
|
45
|
+
url = base_url + RESOURCES[:rates]
|
46
|
+
request = FriendlyShipping::Request.new(
|
47
|
+
url: url,
|
48
|
+
body: access_request_xml + rate_request_xml
|
49
|
+
)
|
50
|
+
|
51
|
+
client.post(request).bind do |response|
|
52
|
+
ParseRateResponse.call(response: response, request: request, shipment: shipment)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def access_request_xml
|
59
|
+
SerializeAccessRequest.call(key: key, login: login, password: password)
|
60
|
+
end
|
61
|
+
|
62
|
+
def base_url
|
63
|
+
test ? TEST_URL : LIVE_URL
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/monads/result'
|
4
|
+
require 'friendly_shipping/bad_request'
|
5
|
+
require 'rest-client'
|
6
|
+
|
7
|
+
module FriendlyShipping
|
8
|
+
module Services
|
9
|
+
class Ups
|
10
|
+
class Client
|
11
|
+
extend Dry::Monads::Result::Mixin
|
12
|
+
class << self
|
13
|
+
def post(friendly_shipping_request)
|
14
|
+
http_response = ::RestClient.post(
|
15
|
+
friendly_shipping_request.url,
|
16
|
+
friendly_shipping_request.body,
|
17
|
+
friendly_shipping_request.headers
|
18
|
+
)
|
19
|
+
|
20
|
+
Success(convert_to_friendly_response(http_response))
|
21
|
+
rescue ::RestClient::Exception => e
|
22
|
+
Failure(e)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def convert_to_friendly_response(http_response)
|
28
|
+
FriendlyShipping::Response.new(
|
29
|
+
status: http_response.code,
|
30
|
+
body: http_response.body,
|
31
|
+
headers: http_response.headers
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'friendly_shipping/services/ups/parse_xml_response'
|
4
|
+
|
5
|
+
module FriendlyShipping
|
6
|
+
module Services
|
7
|
+
class Ups
|
8
|
+
class ParseRateResponse
|
9
|
+
def self.call(request:, response:, shipment:)
|
10
|
+
parsing_result = ParseXMLResponse.call(response.body, 'RatingServiceSelectionResponse')
|
11
|
+
parsing_result.fmap do |xml|
|
12
|
+
xml.root.css('> RatedShipment').map do |rated_shipment|
|
13
|
+
service_code = rated_shipment.at('Service/Code').text
|
14
|
+
shipping_method = CARRIER.shipping_methods.detect do |sm|
|
15
|
+
sm.service_code == service_code && shipment.origin.country.in?(sm.origin_countries)
|
16
|
+
end
|
17
|
+
days_to_delivery = rated_shipment.at('GuaranteedDaysToDelivery').text.to_i
|
18
|
+
currency = Money::Currency.new(rated_shipment.at('TotalCharges/CurrencyCode').text)
|
19
|
+
total_cents = rated_shipment.at('TotalCharges/MonetaryValue').text.to_d * currency.subunit_to_unit
|
20
|
+
insurance_price = rated_shipment.at('ServiceOptionsCharges/MonetaryValue').text.to_f
|
21
|
+
negotiated_rate = rated_shipment.at(
|
22
|
+
'NegotiatedRates/NetSummaryCharges/GrandTotal/MonetaryValue'
|
23
|
+
)&.text.to_f
|
24
|
+
|
25
|
+
FriendlyShipping::Rate.new(
|
26
|
+
shipping_method: shipping_method,
|
27
|
+
amounts: { total: Money.new(total_cents, currency) },
|
28
|
+
warnings: [rated_shipment.at("RatedShipmentWarning")&.text].compact,
|
29
|
+
errors: [],
|
30
|
+
data: {
|
31
|
+
insurance_price: insurance_price,
|
32
|
+
negotiated_rate: negotiated_rate,
|
33
|
+
days_to_delivery: days_to_delivery
|
34
|
+
},
|
35
|
+
original_request: request,
|
36
|
+
original_response: response
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Ups
|
6
|
+
class ParseXMLResponse
|
7
|
+
extend Dry::Monads::Result::Mixin
|
8
|
+
SUCCESSFUL_RESPONSE_STATUS_CODE = '1'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def call(response_body, expected_root_tag)
|
12
|
+
xml = Nokogiri.XML(response_body)
|
13
|
+
if xml.root.nil? || xml.root.name != expected_root_tag
|
14
|
+
Failure('Invalid document')
|
15
|
+
end
|
16
|
+
if request_successful?(xml)
|
17
|
+
Success(xml)
|
18
|
+
else
|
19
|
+
Failure(error_message(xml))
|
20
|
+
end
|
21
|
+
rescue Nokogiri::XML::SyntaxError => e
|
22
|
+
Failure(e)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def request_successful?(xml)
|
28
|
+
xml.root.at('Response/ResponseStatusCode').text == SUCCESSFUL_RESPONSE_STATUS_CODE
|
29
|
+
end
|
30
|
+
|
31
|
+
def error_message(xml)
|
32
|
+
status = xml.root.at_xpath('Response/ResponseStatusDescription')&.text
|
33
|
+
desc = xml.root.at_xpath('Response/Error/ErrorDescription')&.text
|
34
|
+
[status, desc].compact.join(": ").presence || 'UPS could not process the request.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module FriendlyShipping
|
6
|
+
module Services
|
7
|
+
class Ups
|
8
|
+
class SerializeAccessRequest
|
9
|
+
def self.call(login:, password:, key:)
|
10
|
+
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
11
|
+
xml.AccessRequest do
|
12
|
+
xml.AccessLicenseNumber(key)
|
13
|
+
xml.UserId(login)
|
14
|
+
xml.Password(password)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
xml_builder.to_xml
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Ups
|
6
|
+
class SerializeAddressSnippet
|
7
|
+
class << self
|
8
|
+
def call(xml:, location:)
|
9
|
+
if location.company_name # Is this a business address?
|
10
|
+
name = location.company_name[0..34]
|
11
|
+
attention_name = location.name
|
12
|
+
else
|
13
|
+
name = location.name
|
14
|
+
attention_name = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# UPS wants a different main Name tag when it's the shipper
|
18
|
+
if xml.parent.name == "Shipper"
|
19
|
+
xml.Name(name)
|
20
|
+
else
|
21
|
+
xml.CompanyName(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
if attention_name
|
25
|
+
xml.AttentionName(attention_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
xml.PhoneNumber(location.phone) if location.phone
|
29
|
+
|
30
|
+
xml.Address do
|
31
|
+
xml.AddressLine1(location.address1) if location.address1
|
32
|
+
xml.AddressLine2(location.address2) if location.address2
|
33
|
+
|
34
|
+
xml.City(location.city) if location.city
|
35
|
+
xml.PostalCode(location.zip) if location.zip
|
36
|
+
|
37
|
+
# StateProvinceCode required for negotiated rates but not otherwise, for some reason
|
38
|
+
xml.StateProvinceCode(location.region.code) if location.region
|
39
|
+
xml.CountryCode(location.country.code) if location.country
|
40
|
+
|
41
|
+
# Quote residential rates by default. If UPS doesn't know if the address is residential or
|
42
|
+
# commercial, it will quote a residential rate by default. Even with this flag being set,
|
43
|
+
# if UPS knows the address is commercial it will quote a commercial rate.
|
44
|
+
#
|
45
|
+
xml.ResidentialAddressIndicator
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Ups
|
6
|
+
class SerializePackageNode
|
7
|
+
def self.call(xml:, package:)
|
8
|
+
xml.Package do
|
9
|
+
xml.PackagingType do
|
10
|
+
xml.Code('02')
|
11
|
+
end
|
12
|
+
|
13
|
+
if package.dimensions.all? { |dim| !dim.value.zero? && !dim.value.infinite? }
|
14
|
+
xml.Dimensions do
|
15
|
+
xml.UnitOfMeasurement do
|
16
|
+
xml.Code('IN')
|
17
|
+
end
|
18
|
+
xml.Length(package.length.convert_to(:inches).value.to_f.round(3))
|
19
|
+
xml.Width(package.width.convert_to(:inches).value.to_f.round(3))
|
20
|
+
xml.Height(package.height.convert_to(:inches).value.to_f.round(3))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
xml.PackageWeight do
|
25
|
+
xml.UnitOfMeasurement do
|
26
|
+
xml.Code('LBS')
|
27
|
+
end
|
28
|
+
|
29
|
+
xml.Weight([package.weight.convert_to(:pounds).value.to_f.round(2).ceil, 1].max)
|
30
|
+
end
|
31
|
+
|
32
|
+
if package.properties[:shipper_release]
|
33
|
+
xml.PackageServiceOptions do
|
34
|
+
xml.ShipperReleaseIndicator
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Array.wrap(package.properties[:reference_numbers]).each do |reference_number_info|
|
39
|
+
xml.ReferenceNumber do
|
40
|
+
xml.Code(reference_number_info[:code] || "")
|
41
|
+
xml.Value(reference_number_info[:value])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'friendly_shipping/services/ups/serialize_address_snippet'
|
5
|
+
require 'friendly_shipping/services/ups/serialize_package_node'
|
6
|
+
|
7
|
+
module FriendlyShipping
|
8
|
+
module Services
|
9
|
+
class Ups
|
10
|
+
class SerializeRatingServiceSelectionRequest
|
11
|
+
PICKUP_CODES = HashWithIndifferentAccess.new(
|
12
|
+
daily_pickup: "01",
|
13
|
+
customer_counter: "03",
|
14
|
+
one_time_pickup: "06",
|
15
|
+
on_call_air: "07",
|
16
|
+
suggested_retail_rates: "11",
|
17
|
+
letter_center: "19",
|
18
|
+
air_service_center: "20"
|
19
|
+
)
|
20
|
+
|
21
|
+
CUSTOMER_CLASSIFICATIONS = HashWithIndifferentAccess.new(
|
22
|
+
shipper_number: "00",
|
23
|
+
daily_rates: "01",
|
24
|
+
retail_rates: "04",
|
25
|
+
regional_rates: "05",
|
26
|
+
general_rates: "06",
|
27
|
+
standard_rates: "53"
|
28
|
+
)
|
29
|
+
|
30
|
+
def self.call(shipment:)
|
31
|
+
shipper = shipment.options[:shipper] || shipment.origin
|
32
|
+
pickup_type = PICKUP_CODES[shipment.options[:pickup_type] || :daily_pickup]
|
33
|
+
customer_classification = CUSTOMER_CLASSIFICATIONS[
|
34
|
+
shipment.options[:customer_classification] || :daily_rates
|
35
|
+
]
|
36
|
+
origin_account = shipment.options[:origin_account]
|
37
|
+
destination_account = shipment.options[:destination_account]
|
38
|
+
|
39
|
+
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
40
|
+
xml.RatingServiceSelectionRequest do
|
41
|
+
xml.Request do
|
42
|
+
xml.RequestAction('Rate')
|
43
|
+
xml.RequestOption('Shop')
|
44
|
+
xml.SubVersion('1707')
|
45
|
+
end
|
46
|
+
|
47
|
+
xml.PickupType do
|
48
|
+
xml.Code(pickup_type)
|
49
|
+
end
|
50
|
+
xml.CustomerClassification do
|
51
|
+
xml.Code(customer_classification)
|
52
|
+
end
|
53
|
+
|
54
|
+
xml.Shipment do
|
55
|
+
# not implemented: Shipment/Description element
|
56
|
+
xml.Shipper do
|
57
|
+
SerializeAddressSnippet.call(xml: xml, location: shipper)
|
58
|
+
|
59
|
+
xml.ShipperNumber(origin_account)
|
60
|
+
end
|
61
|
+
|
62
|
+
xml.ShipTo do
|
63
|
+
SerializeAddressSnippet.call(xml: xml, location: shipment.destination)
|
64
|
+
|
65
|
+
if destination_account
|
66
|
+
xml.ShipperAssignedIdentificationNumber(destination_account)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if shipper != shipment.origin
|
71
|
+
xml.ShipFrom do
|
72
|
+
SerializeAddressSnippet.call(xml: xml, location: shipment.origin)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
shipment.packages.each do |package|
|
77
|
+
SerializePackageNode.call(xml: xml, package: package)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
xml_builder.to_xml
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Ups
|
6
|
+
EU_COUNTRIES = %w(
|
7
|
+
AT BE BG CY CZ DE DK EE ES FI FR GB GR HR HU IE IT LT LU LV MT NL PO PT RO SE SI SK
|
8
|
+
).map { |country_code| Carmen::Country.coded(country_code) }
|
9
|
+
|
10
|
+
class << self
|
11
|
+
private
|
12
|
+
|
13
|
+
def countries_by_code(code)
|
14
|
+
all_countries = Carmen::Country.all
|
15
|
+
covered_countries = EU_COUNTRIES + %w(US PR CA PL MX).map do |country_code|
|
16
|
+
Carmen::Country.coded(country_code)
|
17
|
+
end
|
18
|
+
other_countries = Carmen::Country.all - covered_countries
|
19
|
+
case code
|
20
|
+
when 'EU' then EU_COUNTRIES
|
21
|
+
when 'OTHER' then other_countries
|
22
|
+
when 'ALL' then all_countries
|
23
|
+
else
|
24
|
+
[Carmen::Country.coded(code)]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
SHIPPING_METHODS = [
|
30
|
+
['US', 'international', 'UPS Standard', '11'],
|
31
|
+
['US', 'international', 'UPS Worldwide Express', '07'],
|
32
|
+
['US', 'international', 'UPS Worldwide Expedited', '08'],
|
33
|
+
['US', 'international', 'UPS Worldwide Express Plus', '54'],
|
34
|
+
['US', 'international', 'UPS Worldwide Saver', '65'],
|
35
|
+
['US', 'domestic', 'UPS 2nd Day Air', '02'],
|
36
|
+
['US', 'domestic', 'UPS 2nd Day Air A.M.', '59'],
|
37
|
+
['US', 'domestic', 'UPS 3 Day Select', '12'],
|
38
|
+
['US', 'domestic', 'UPS Ground', '03'],
|
39
|
+
['US', 'domestic', 'UPS Next Day Air', '01'],
|
40
|
+
['US', 'domestic', 'UPS Next Day Air Early', '14'],
|
41
|
+
['US', 'domestic', 'UPS Next Day Air Saver', '13'],
|
42
|
+
['CA', 'domestic', 'UPS Expedited Canadian domestic shipments', '02'],
|
43
|
+
['CA', 'domestic', 'UPS Express Saver Canadian domestic shipments', '13'],
|
44
|
+
['CA', 'domestic', 'UPS 3 Day Select Shipments originating in Canada to CA and US 48', '12'],
|
45
|
+
['CA', 'international', 'UPS 3 Day Select Shipments originating in Canada to CA and US 48', '12'],
|
46
|
+
['CA', 'domestic', 'UPS Access Point Economy Canadian domestic shipments', '70'],
|
47
|
+
['CA', 'domestic', 'UPS Express Canadian domestic shipments', '01'],
|
48
|
+
['CA', 'domestic', 'UPS Express Early Canadian domestic shipments', '14'],
|
49
|
+
['CA', 'international', 'UPS Express Saver International shipments originating in Canada', '65'],
|
50
|
+
['CA', 'international', 'UPS Standard Shipments originating in Canada (Domestic and Int’l)', '11'],
|
51
|
+
['CA', 'domestic', 'UPS Standard Shipments originating in Canada (Domestic and Int’l)', '11'],
|
52
|
+
['CA', 'international', 'UPS Worldwide Expedited International shipments originating in Canada', '08'],
|
53
|
+
['CA', 'international', 'UPS Worldwide Express International shipments originating in Canada', '07'],
|
54
|
+
['CA', 'international', 'UPS Worldwide Express Plus International shipments originating in Canada', '54'],
|
55
|
+
['CA', 'international', 'UPS Express Early Shipments originating in Canada to CA and US 48', '54'],
|
56
|
+
['CA', 'domestic', 'UPS Express Early Shipments originating in Canada to CA and US 48', '54'],
|
57
|
+
['EU', 'domestic', 'UPS Access Point Economy Shipments within the European Union', '70'],
|
58
|
+
['EU', 'international', 'UPS Expedited Shipments originating in the European Union', '08'],
|
59
|
+
['EU', 'international', 'UPS Express Shipments originating in the European Union', '07'],
|
60
|
+
['EU', 'international', 'UPS Standard Shipments originating in the European Union', '11'],
|
61
|
+
['EU', 'international', 'UPS Worldwide Express Plus Shipments originating in the European Union', '54'],
|
62
|
+
['EU', 'international', 'UPS Worldwide Saver Shipments originating in the European Union', '65'],
|
63
|
+
['MX', 'domestic', 'UPS Access Point Economy Shipments within Mexico', '70'],
|
64
|
+
['MX', 'international', 'UPS Expedited Shipments originating in Mexico', '08'],
|
65
|
+
['MX', 'international', 'UPS Express Shipments originating in Mexico', '07'],
|
66
|
+
['MX', 'international', 'UPS Standard Shipments originating in Mexico', '11'],
|
67
|
+
['MX', 'international', 'UPS Worldwide Express Plus Shipments originating in Mexico', '54'],
|
68
|
+
['MX', 'international', 'UPS Worldwide Saver Shipments originating in Mexico', '65'],
|
69
|
+
['PL', 'domestic', 'UPS Access Point Economy Polish domestic shipments', '70'],
|
70
|
+
['PL', 'domestic', 'UPS Today Dedicated Courier Polish domestic shipments', '83'],
|
71
|
+
['PL', 'domestic', 'UPS Today Express Polish domestic shipments', '85'],
|
72
|
+
['PL', 'domestic', 'UPS Today Express Saver Polish domestic shipments', '86'],
|
73
|
+
['PL', 'domestic', 'UPS Today Standard Polish domestic shipments', '82'],
|
74
|
+
['PL', 'international', 'UPS Expedited Shipments originating in Poland', '08'],
|
75
|
+
['PL', 'international', 'UPS Express Shipments originating in Poland', '07'],
|
76
|
+
['PL', 'international', 'UPS Express Plus Shipments originating in Poland', '54'],
|
77
|
+
['PL', 'international', 'UPS Express Saver Shipments originating in Poland', '65'],
|
78
|
+
['PL', 'international', 'UPS Standard Shipments originating in Poland', '11'],
|
79
|
+
['PR', 'domestic', 'UPS 2nd Day Air', '02'],
|
80
|
+
['PR', 'domestic', 'UPS Ground', '03'],
|
81
|
+
['PR', 'domestic', 'UPS Next Day Air', '01'],
|
82
|
+
['PR', 'domestic', 'UPS Next Day Air Early', '14'],
|
83
|
+
['PR', 'international', 'UPS Worldwide Expedited', '08'],
|
84
|
+
['PR', 'international', 'UPS Worldwide Express', '07'],
|
85
|
+
['PR', 'international', 'UPS Worldwide Express Plus', '54'],
|
86
|
+
['PR', 'international', 'UPS Worldwide Saver', '65'],
|
87
|
+
['DE', 'domestic', 'UPS Express 12:00 German domestic shipments', '74'],
|
88
|
+
['OTHER', 'domestic', 'UPS Express', '07'],
|
89
|
+
['OTHER', 'domestic', 'UPS Standard', '11'],
|
90
|
+
['OTHER', 'international', 'UPS Worldwide Expedited', '08'],
|
91
|
+
['OTHER', 'international', 'UPS Worldwide Express Plus', '54'],
|
92
|
+
['OTHER', 'international', 'UPS Worldwide Saver', '65'],
|
93
|
+
['ALL', 'international', 'UPS Worldwide Express Freight', '96'],
|
94
|
+
['ALL', 'international', 'UPS Worldwide Express Freight Midday', '71']
|
95
|
+
].freeze.map do |origin_country_code, dom_or_intl, name, code|
|
96
|
+
FriendlyShipping::ShippingMethod.new(
|
97
|
+
name: name,
|
98
|
+
service_code: code,
|
99
|
+
domestic: dom_or_intl == 'domestic',
|
100
|
+
international: dom_or_intl == 'international',
|
101
|
+
origin_countries: countries_by_code(origin_country_code),
|
102
|
+
multi_package: true
|
103
|
+
).freeze
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -1,14 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FriendlyShipping
|
2
4
|
class ShippingMethod
|
3
|
-
attr_reader :name, :service_code, :carrier
|
5
|
+
attr_reader :name, :service_code, :carrier, :origin_countries
|
4
6
|
|
5
|
-
def initialize(
|
7
|
+
def initialize(
|
8
|
+
name: nil,
|
9
|
+
service_code: nil,
|
10
|
+
domestic: nil,
|
11
|
+
international: nil,
|
12
|
+
multi_package: nil,
|
13
|
+
carrier: nil,
|
14
|
+
origin_countries: []
|
15
|
+
)
|
6
16
|
@name = name
|
7
17
|
@service_code = service_code
|
8
18
|
@domestic = domestic
|
9
19
|
@international = international
|
10
20
|
@multi_package = multi_package
|
11
21
|
@carrier = carrier
|
22
|
+
@origin_countries = origin_countries
|
12
23
|
end
|
13
24
|
|
14
25
|
def domestic?
|
metadata
CHANGED
@@ -1,85 +1,99 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: friendly_shipping
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Meyerhoff
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: data_uri
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.3
|
19
|
+
version: 0.0.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.3
|
26
|
+
version: 0.0.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: dry-monads
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: money
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 6.0.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 6.0.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: nokogiri
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: physical
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.0
|
75
|
+
version: 0.3.0
|
62
76
|
type: :runtime
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.0
|
82
|
+
version: 0.3.0
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: rest-client
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
89
|
+
version: '2.0'
|
76
90
|
type: :runtime
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
96
|
+
version: '2.0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: bundler
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,6 +146,8 @@ files:
|
|
132
146
|
- ".circleci/config.yml"
|
133
147
|
- ".gitignore"
|
134
148
|
- ".rspec"
|
149
|
+
- ".rubocop-relaxed.yml"
|
150
|
+
- ".rubocop.yml"
|
135
151
|
- ".travis.yml"
|
136
152
|
- CODE_OF_CONDUCT.md
|
137
153
|
- Gemfile
|
@@ -156,6 +172,15 @@ files:
|
|
156
172
|
- lib/friendly_shipping/services/ship_engine/parse_void_response.rb
|
157
173
|
- lib/friendly_shipping/services/ship_engine/serialize_label_shipment.rb
|
158
174
|
- lib/friendly_shipping/services/ship_engine/serialize_rate_estimate_request.rb
|
175
|
+
- lib/friendly_shipping/services/ups.rb
|
176
|
+
- lib/friendly_shipping/services/ups/client.rb
|
177
|
+
- lib/friendly_shipping/services/ups/parse_rate_response.rb
|
178
|
+
- lib/friendly_shipping/services/ups/parse_xml_response.rb
|
179
|
+
- lib/friendly_shipping/services/ups/serialize_access_request.rb
|
180
|
+
- lib/friendly_shipping/services/ups/serialize_address_snippet.rb
|
181
|
+
- lib/friendly_shipping/services/ups/serialize_package_node.rb
|
182
|
+
- lib/friendly_shipping/services/ups/serialize_rating_service_selection_request.rb
|
183
|
+
- lib/friendly_shipping/services/ups/shipping_methods.rb
|
159
184
|
- lib/friendly_shipping/shipping_method.rb
|
160
185
|
- lib/friendly_shipping/version.rb
|
161
186
|
homepage: https://github.com/friendly_cart/friendly_shipping
|