dhl-get_quote 0.4.25

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.
@@ -0,0 +1,183 @@
1
+ require 'rubygems'
2
+ require 'httparty'
3
+ require 'erb'
4
+ require 'set'
5
+
6
+ class Dhl::GetQuote::Request
7
+ attr_reader :site_id, :password, :from_country_code, :from_postal_code, :to_country_code, :to_postal_code
8
+ attr_accessor :pieces
9
+
10
+ URLS = {
11
+ :production => 'https://xmlpi-ea.dhl.com/XMLShippingServlet',
12
+ :test => 'https://xmlpitest-ea.dhl.com/XMLShippingServlet'
13
+ }
14
+
15
+ def initialize(options = {})
16
+ @test_mode = !!options[:test_mode] || Dhl::GetQuote.test_mode?
17
+
18
+ @site_id = options[:site_id] || Dhl::GetQuote.site_id
19
+ @password = options[:password] || Dhl::GetQuote.password
20
+
21
+ [ :site_id, :password ].each do |req|
22
+ unless instance_variable_get("@#{req}").to_s.size > 0
23
+ raise Dhl::GetQuote::OptionsError, ":#{req} is a required option"
24
+ end
25
+ end
26
+
27
+ @special_services_list = Set.new
28
+
29
+ @is_dutiable = Dhl::GetQuote.dutiable?
30
+
31
+ @pieces = []
32
+ end
33
+
34
+ def test_mode?
35
+ !!@test_mode
36
+ end
37
+
38
+ def test_mode!
39
+ @test_mode = true
40
+ end
41
+
42
+ def production_mode!
43
+ @test_mode = false
44
+ end
45
+
46
+ def from(country_code, postal_code)
47
+ @from_postal_code = postal_code.to_s
48
+ validate_country_code!(country_code)
49
+ @from_country_code = country_code
50
+ end
51
+
52
+ def to(country_code, postal_code)
53
+ @to_postal_code = postal_code.to_s
54
+ validate_country_code!(country_code)
55
+ @to_country_code = country_code
56
+ end
57
+
58
+ def dutiable?
59
+ !!@is_dutiable
60
+ end
61
+
62
+ def dutiable(val)
63
+ @is_dutiable = !!val
64
+ end
65
+
66
+ def dutiable!
67
+ dutiable(true)
68
+ end
69
+
70
+ def not_dutiable!
71
+ dutiable(false)
72
+ end
73
+
74
+ def dimensions_unit
75
+ @dimensions_unit ||= Dhl::GetQuote.dimensions_unit
76
+ end
77
+
78
+ def weight_unit
79
+ @weight_unit ||= Dhl::GetQuote.weight_unit
80
+ end
81
+
82
+ def centimeters!
83
+ @dimensions_unit = Dhl::GetQuote::DIMENSIONS_UNIT_CODES[:centimeters]
84
+ end
85
+ alias :centimetres! :centimeters!
86
+
87
+ def inches!
88
+ @dimensions_unit = Dhl::GetQuote::DIMENSIONS_UNIT_CODES[:inches]
89
+ end
90
+
91
+ def centimeters?
92
+ dimensions_unit == Dhl::GetQuote::DIMENSIONS_UNIT_CODES[:centimeters]
93
+ end
94
+ alias :centimetres? :centimeters?
95
+
96
+ def inches?
97
+ dimensions_unit == Dhl::GetQuote::DIMENSIONS_UNIT_CODES[:inches]
98
+ end
99
+
100
+ def kilograms!
101
+ @weight_unit = Dhl::GetQuote::WEIGHT_UNIT_CODES[:kilograms]
102
+ end
103
+ alias :kilogrammes! :kilograms!
104
+
105
+ def pounds!
106
+ @weight_unit = Dhl::GetQuote::WEIGHT_UNIT_CODES[:pounds]
107
+ end
108
+
109
+ def pounds?
110
+ weight_unit == Dhl::GetQuote::WEIGHT_UNIT_CODES[:pounds]
111
+ end
112
+
113
+ def kilograms?
114
+ weight_unit == Dhl::GetQuote::WEIGHT_UNIT_CODES[:kilograms]
115
+ end
116
+ alias :kilogrammes? :kilograms?
117
+
118
+ def to_xml
119
+ validate!
120
+ ERB.new(File.new(xml_template_path).read, nil,'%<>-').result(binding)
121
+ end
122
+
123
+ def ready_time(time=Time.now)
124
+ time.strftime("PT%HH%MM")
125
+ end
126
+
127
+ def post
128
+ validate!
129
+ response = HTTParty.post(servlet_url,
130
+ :body => to_xml,
131
+ :headers => { 'Content-Type' => 'application/xml' }
132
+ ).response
133
+
134
+ Dhl::GetQuote::Response.new(response.body)
135
+ end
136
+
137
+ def special_services
138
+ @special_services_list.to_a.sort
139
+ end
140
+
141
+ def add_special_service(special_service_type)
142
+ return if special_service_type.to_s.size < 1
143
+ @special_services_list << special_service_type
144
+ end
145
+
146
+ def remove_special_service(special_service_type)
147
+ return if special_service_type.to_s.size < 1
148
+ @special_services_list.delete_if{|x| x == special_service_type}
149
+ end
150
+
151
+ protected
152
+
153
+ def servlet_url
154
+ test_mode? ? URLS[:test] : URLS[:production]
155
+ end
156
+
157
+ def validate!
158
+ raise Dhl::GetQuote::FromNotSetError, "#from() is not set" unless (@from_country_code && @from_postal_code)
159
+ raise Dhl::GetQuote::ToNotSetError, "#to() is not set" unless (@to_country_code && @to_postal_code)
160
+ validate_pieces!
161
+ end
162
+
163
+ def validate_pieces!
164
+ pieces.each do |piece|
165
+ klass_name = "Dhl::GetQuote::Piece"
166
+ if piece.class.to_s != klass_name
167
+ raise Dhl::GetQuote::PieceError, "entry in #pieces is not a #{klass_name} object!"
168
+ end
169
+ end
170
+ end
171
+
172
+ def validate_country_code!(country_code)
173
+ unless country_code =~ /^[A-Z]{2}$/
174
+ raise Dhl::GetQuote::CountryCodeError, 'country code must be upper-case, two letters (A-Z)'
175
+ end
176
+ end
177
+
178
+ def xml_template_path
179
+ spec = Gem::Specification.find_by_name("dhl-get_quote")
180
+ gem_root = spec.gem_dir
181
+ gem_root + "/tpl/request.xml.erb"
182
+ end
183
+ end
@@ -0,0 +1,94 @@
1
+ class Dhl::GetQuote::Response
2
+ include Dhl::GetQuote::Helper
3
+
4
+ attr_reader :raw_xml, :parsed_xml, :error
5
+ attr_reader :currency_code, :currency_role_type_code, :weight_charge, :total_amount, :total_tax_amount, :weight_charge_tax
6
+
7
+ CURRENCY_ROLE_TYPE_CODES = %w[ BILLC PULCL BASEC INVCU ]
8
+ DEFAULT_CURRENCY_ROLE_TYPE_CODE = 'BILLC'
9
+
10
+ def initialize(xml="")
11
+ @raw_xml = xml
12
+ begin
13
+ @parsed_xml = MultiXml.parse(xml)
14
+ rescue MultiXml::ParseError => e
15
+ @error = e
16
+ return self
17
+ end
18
+
19
+ if response_indicates_error?
20
+ @error = case response_error_condition_code.to_s
21
+ when "100"
22
+ Dhl::GetQuote::Upstream::ValidationFailureError.new(response_error_condition_data)
23
+ else
24
+ Dhl::GetQuote::Upstream::UnknownError.new(response_error_condition_data)
25
+ end
26
+ else
27
+ load_costs(DEFAULT_CURRENCY_ROLE_TYPE_CODE)
28
+ end
29
+ end
30
+
31
+ def error?
32
+ !!@error
33
+ end
34
+
35
+ def load_costs(currency_role_type_code=DEFAULT_CURRENCY_ROLE_TYPE_CODE)
36
+ validate_currency_role_type_code!(currency_role_type_code)
37
+
38
+ return if error?
39
+ qtd_s_in_ad_cur = @parsed_xml["DCTResponse"]["GetQuoteResponse"]["BkgDetails"]["QtdShp"]["QtdSInAdCur"]
40
+ pricing = if x = qtd_s_in_ad_cur.detect{|q|q["CurrencyRoleTypeCode"]==currency_role_type_code}
41
+ x
42
+ else
43
+ qtd_s_in_ad_cur.first
44
+ end
45
+
46
+ pricing.each do |k,v|
47
+ instance_variable_set("@#{underscore(k)}".to_sym, v)
48
+ end
49
+ end
50
+
51
+ def validate_currency_role_type_code!(currency_role_type_code)
52
+ unless CURRENCY_ROLE_TYPE_CODES.include?(currency_role_type_code)
53
+ raise Dhl::GetQuote::OptionsError,
54
+ "'#{currency_role_type_code}' is not one of #{CURRENCY_ROLE_TYPE_CODES.join(', ')}"
55
+ end
56
+ end
57
+
58
+ def offered_services
59
+ market_services.select do
60
+ |m| m['TransInd'].to_s == "Y" || m['MrkSrvInd'].to_s == "Y"
61
+ end.map do |m|
62
+ Dhl::GetQuote::MarketService.new(m)
63
+ end.sort{|a,b| a.code <=> b.code }
64
+ end
65
+
66
+ def all_services
67
+ market_services.map do |m|
68
+ Dhl::GetQuote::MarketService.new(m)
69
+ end.sort{|a,b| a.code <=> b.code }
70
+ end
71
+
72
+ protected
73
+
74
+ def response_indicates_error?
75
+ @parsed_xml.keys.include?('ErrorResponse')
76
+ end
77
+
78
+ def response_error_status_condition
79
+ @response_error_status_condition ||= @parsed_xml['ErrorResponse']['Response']['Status']['Condition']
80
+ end
81
+
82
+ def response_error_condition_code
83
+ @response_error_condition_code ||= response_error_status_condition['ConditionCode']
84
+ end
85
+
86
+ def response_error_condition_data
87
+ @response_error_condition_data ||= response_error_status_condition['ConditionData']
88
+ end
89
+
90
+ def market_services
91
+ @market_services ||= @parsed_xml["DCTResponse"]["GetQuoteResponse"]["Srvs"]["Srv"]["MrkSrv"]
92
+ end
93
+
94
+ end
@@ -0,0 +1,5 @@
1
+ class Dhl
2
+ class GetQuote
3
+ VERSION = "0.4.25"
4
+ end
5
+ end
@@ -0,0 +1,96 @@
1
+ require "dhl/get_quote/version"
2
+ require "dhl/get_quote/helper"
3
+ require "dhl/get_quote/errors"
4
+ require "dhl/get_quote/request"
5
+ require "dhl/get_quote/response"
6
+ require "dhl/get_quote/piece"
7
+ require "dhl/get_quote/market_service"
8
+
9
+ class Dhl
10
+ class GetQuote
11
+
12
+ DIMENSIONS_UNIT_CODES = { :centimeters => "CM", :inches => "IN" }
13
+ WEIGHT_UNIT_CODES = { :kilograms => "KG", :pounds => "LB" }
14
+
15
+ def self.configure(&block)
16
+ yield self if block_given?
17
+ end
18
+
19
+ def self.test_mode!
20
+ @@test_mode = true
21
+ end
22
+
23
+ def self.test_mode?
24
+ !!@@test_mode
25
+ end
26
+
27
+ def self.production_mode!
28
+ @@test_mode = false
29
+ end
30
+
31
+ def self.site_id(site_id=nil)
32
+ if (s = site_id.to_s).size > 0
33
+ @@site_id = s
34
+ else
35
+ @@site_id
36
+ end
37
+ end
38
+
39
+ def self.password(password=nil)
40
+ if (s = password.to_s).size > 0
41
+ @@password = s
42
+ else
43
+ @@password
44
+ end
45
+ end
46
+
47
+ def self.kilograms!
48
+ @@weight_unit = WEIGHT_UNIT_CODES[:kilograms]
49
+ end
50
+ def self.kilogrammes!; self.kilograms!; end
51
+
52
+ def self.pounds!
53
+ @@weight_unit = WEIGHT_UNIT_CODES[:pounds]
54
+ end
55
+
56
+ def self.weight_unit
57
+ @@weight_unit
58
+ end
59
+
60
+ def self.centimeters!
61
+ @@dimensions_unit = DIMENSIONS_UNIT_CODES[:centimeters]
62
+ end
63
+ def self.centimetres!; self.centimeters!; end
64
+
65
+ def self.inches!
66
+ @@dimensions_unit = DIMENSIONS_UNIT_CODES[:inches]
67
+ end
68
+
69
+ def self.dimensions_unit
70
+ @@dimensions_unit
71
+ end
72
+
73
+ def self.dutiable!
74
+ @@dutiable = true
75
+ end
76
+
77
+ def self.not_dutiable!
78
+ @@dutiable = false
79
+ end
80
+
81
+ def self.dutiable?
82
+ !!@@dutiable
83
+ end
84
+
85
+ def self.set_defaults
86
+ @@site_id = nil
87
+ @@password = nil
88
+ @@weight_unit = WEIGHT_UNIT_CODES[:kilograms]
89
+ @@dimensions_unit = DIMENSIONS_UNIT_CODES[:centimeters]
90
+ @@dutiable = false
91
+ @@test_mode = false
92
+ end
93
+ end
94
+ end
95
+
96
+ Dhl::GetQuote.set_defaults
@@ -0,0 +1,177 @@
1
+ require 'spec_helper'
2
+ require 'dhl-get_quote'
3
+
4
+ describe Dhl::GetQuote do
5
+
6
+ # classvars may be remembered between tests, this sets things back to default
7
+ after(:each) do
8
+ Dhl::GetQuote.set_defaults
9
+ end
10
+
11
+ let(:klass) { Dhl::GetQuote }
12
+
13
+ let(:valid_request_options) do
14
+ {
15
+ :site_id => 'SiteId',
16
+ :password => 'p4ssw0rd'
17
+ }
18
+ end
19
+
20
+ let (:valid_request) { request = Dhl::GetQuote::Request.new(valid_request_options) }
21
+
22
+ describe ".configure" do
23
+
24
+ it "must accept and execute a block" do
25
+ lambda do
26
+ klass.configure do
27
+ raise RuntimeError, "Testing"
28
+ end
29
+ end.must raise_exception RuntimeError
30
+ end
31
+
32
+ context "configure() block" do
33
+
34
+ describe ".test_mode!" do
35
+ before(:each) do
36
+ klass.configure { |c| c.test_mode! }
37
+ end
38
+
39
+ it "must set the class test_mode? to true" do
40
+ klass.test_mode?.must be_true
41
+ end
42
+
43
+ it "Dhl::GetQuote::Request must honor this test mode" do
44
+ request = Dhl::GetQuote::Request.new(valid_request_options)
45
+ request.test_mode?.must be_true
46
+ end
47
+ end
48
+
49
+ describe ".production_mode!" do
50
+ before(:each) do
51
+ klass.configure { |c| c.production_mode! }
52
+ end
53
+
54
+ it "must set the classvar test_mode to false" do
55
+ klass.test_mode?.must be_false
56
+ end
57
+
58
+ it "Dhl::GetQuote::Request must honor this test mode" do
59
+ request = Dhl::GetQuote::Request.new(valid_request_options)
60
+ request.test_mode?.must be_false
61
+ end
62
+ end
63
+
64
+ describe ".site_id" do
65
+ before(:each) { klass.configure { |c| c.site_id "SomethingHere" } }
66
+
67
+ it "must set class site_id to passed string" do
68
+ klass.site_id.must == "SomethingHere"
69
+ end
70
+
71
+ it "Dhl::GetQuote::Request must honor this" do
72
+ request = Dhl::GetQuote::Request.new(
73
+ :password => 'xxx'
74
+ )
75
+
76
+ request.instance_variable_get(:@site_id).must == "SomethingHere"
77
+ end
78
+ end
79
+
80
+ describe ".password" do
81
+ before(:each) { klass.configure { |c| c.password "ppaasswwoorrdd" } }
82
+
83
+ it "must set class password to passed string" do
84
+ klass.password.must == "ppaasswwoorrdd"
85
+ end
86
+
87
+ it "Dhl::GetQuote::Request must honor this" do
88
+ request = Dhl::GetQuote::Request.new(
89
+ :site_id => 'ASiteId'
90
+ )
91
+
92
+ request.instance_variable_get(:@password).must == "ppaasswwoorrdd"
93
+ end
94
+ end
95
+
96
+ describe "kilograms!" do
97
+ before(:each) { klass.configure { |c| c.kilograms! } }
98
+
99
+ it "must set class weight_unit to KG" do
100
+ klass.weight_unit.must == "KG"
101
+ end
102
+
103
+ it "Dhl::GetQuote::Request must honor this" do
104
+ valid_request.weight_unit.must == "KG"
105
+ end
106
+ end
107
+
108
+ describe "pounds!" do
109
+ before(:each) { klass.configure { |c| c.pounds! } }
110
+
111
+ it "must set class weight_unit to LB" do
112
+ klass.weight_unit.must == "LB"
113
+ end
114
+
115
+ it "Dhl::GetQuote::Request must honor this" do
116
+ valid_request.weight_unit.must == "LB"
117
+ end
118
+ end
119
+
120
+ describe "centimeters!" do
121
+ before(:each) { klass.configure { |c| c.centimeters! } }
122
+
123
+ it "must set class weight_unit to CM" do
124
+ klass.dimensions_unit.must == "CM"
125
+ end
126
+
127
+ it "Dhl::GetQuote::Request must honor this" do
128
+ valid_request.dimensions_unit.must == "CM"
129
+ end
130
+ end
131
+
132
+ describe "inches!" do
133
+ before(:each) { klass.configure { |c| c.inches! } }
134
+
135
+ it "must set class weight_unit to IN" do
136
+ klass.dimensions_unit.must == "IN"
137
+ end
138
+
139
+ it "Dhl::GetQuote::Request must honor this" do
140
+ valid_request.dimensions_unit.must == "IN"
141
+ end
142
+ end
143
+
144
+ describe ".dutiable!" do
145
+ before(:each) do
146
+ klass.configure { |c| c.dutiable! }
147
+ end
148
+
149
+ it "must set the class dutiable to true" do
150
+ klass.dutiable?.must be_true
151
+ end
152
+
153
+ it "Dhl::GetQuote::Request must honor this test mode" do
154
+ request = Dhl::GetQuote::Request.new(valid_request_options)
155
+ request.dutiable?.must be_true
156
+ end
157
+ end
158
+
159
+ describe ".not_dutiable!" do
160
+ before(:each) do
161
+ klass.configure { |c| c.not_dutiable! }
162
+ end
163
+
164
+ it "must set the classvar test_mode to false" do
165
+ klass.dutiable?.must be_false
166
+ end
167
+
168
+ it "Dhl::GetQuote::Request must honor this test mode" do
169
+ request = Dhl::GetQuote::Request.new(valid_request_options)
170
+ request.dutiable?.must be_false
171
+ end
172
+ end
173
+
174
+ end
175
+ end
176
+
177
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+ require 'dhl-get_quote'
3
+
4
+ describe Dhl::GetQuote::MarketService do
5
+
6
+ let(:valid_params) do
7
+ {
8
+ :local_service_type => "SA",
9
+ :global_service_name => "DELIVERY SIGNATURE",
10
+ :local_service_type_name => "DELIVERY SIGNATURE",
11
+ :s_offered_cust_agreement => "N",
12
+ :charge_code_type => "XCH",
13
+ :mrk_srv_ind => "Y"
14
+ }
15
+ end
16
+
17
+ let(:valid_xml) do
18
+ '<MrkSrv>
19
+ <LocalServiceType>SA</LocalServiceType>
20
+ <GlobalServiceName>DELIVERY SIGNATURE</GlobalServiceName>
21
+ <LocalServiceTypeName>DELIVERY SIGNATURE</LocalServiceTypeName>
22
+ <SOfferedCustAgreement>N</SOfferedCustAgreement>
23
+ <ChargeCodeType>XCH</ChargeCodeType>
24
+ <MrkSrvInd>Y</MrkSrvInd>
25
+ </MrkSrv>'
26
+ end
27
+
28
+ let(:klass) { Dhl::GetQuote::MarketService }
29
+
30
+ subject do
31
+ klass.new(valid_params)
32
+ end
33
+
34
+ describe "#initialize" do
35
+
36
+ context "string of xml is passed" do
37
+
38
+ it 'must build object from xml data' do
39
+
40
+ m = klass.new(valid_xml)
41
+
42
+ m.local_service_type.must == "SA"
43
+ m.global_service_name.must == "DELIVERY SIGNATURE"
44
+ m.local_service_type_name.must == "DELIVERY SIGNATURE"
45
+ m.s_offered_cust_agreement.must == "N"
46
+ m.charge_code_type.must == "XCH"
47
+ m.mrk_srv_ind.must == "Y"
48
+
49
+ end
50
+ end
51
+
52
+ context "hash is passed" do
53
+
54
+ it "must build an object from the hash" do
55
+
56
+ m = klass.new(valid_params)
57
+
58
+ valid_params.each do |k,v|
59
+ m.send(k).must == v
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
66
+
67
+ describe "#code" do
68
+ it "must return the special service code for a mrksrv" do
69
+ subject.code.must == "SA"
70
+ end
71
+ end
72
+
73
+ describe "#name" do
74
+ it "must return the local name for a mrksrv" do
75
+ subject.name.must == "DELIVERY SIGNATURE"
76
+ end
77
+ end
78
+ end