ups-ruby 0.14.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -4
- data/lib/ups/builders/builder_base.rb +38 -24
- data/lib/ups/builders/international_invoice_builder.rb +12 -2
- data/lib/ups/builders/organisation_builder.rb +22 -0
- data/lib/ups/builders/package_builder.rb +69 -0
- data/lib/ups/builders/ship_confirm_builder.rb +40 -1
- data/lib/ups/builders/track_builder.rb +37 -0
- data/lib/ups/connection.rb +22 -0
- data/lib/ups/parsers/ship_accept_parser.rb +26 -0
- data/lib/ups/parsers/track_parser.rb +51 -0
- data/lib/ups/version.rb +2 -2
- data/lib/ups.rb +3 -0
- data/spec/stubs/rates_success_with_packaging_type.xml +187 -0
- data/spec/stubs/rates_success_with_packaging_type_and_dimensions.xml +185 -0
- data/spec/stubs/ship_accept_success_with_packaging_type.xml +69 -0
- data/spec/stubs/ship_accept_success_without_negotiated_price.xml +59 -0
- data/spec/stubs/ship_confirm_success_with_packaging_type.xml +33 -0
- data/spec/stubs/track_success.xml +105 -0
- data/spec/support/AccessRequest.xsd +12 -0
- data/spec/support/IF.xsd +352 -0
- data/spec/support/RateRequest.xsd +454 -1
- data/spec/support/ShipAcceptRequest.xsd +4 -15
- data/spec/support/ShipConfirmRequest.xsd +99 -367
- data/spec/support/shipping_options.rb +42 -0
- data/spec/support/xsd_validator.rb +1 -1
- data/spec/ups/builders/organisation_builder_spec.rb +51 -0
- data/spec/ups/builders/rate_builder_spec.rb +1 -0
- data/spec/ups/builders/ship_confirm_builder_spec.rb +2 -0
- data/spec/ups/connection/rates_standard_spec.rb +94 -1
- data/spec/ups/connection/ship_spec.rb +55 -1
- data/spec/ups/connection/track_spec.rb +47 -0
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a1733aa1ac4da9f295b01e07f0041d3de24a69df86e376a6e36f4bbd4114a45
|
4
|
+
data.tar.gz: 60cd995558755b4ae0a1cd5764d17f57502be21734fdbdd4ec81d353aab413b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a3e526f3189c142034e65f789cace831865e2f6acc7d43b4e2d3292b859d45f833bef80f3143246d6795914b876175b6dcddfd1b83f08e4b594f3d1103f663c
|
7
|
+
data.tar.gz: e4a123acb383f2ead01ed9a2a874a8570243bf8c14176348aeacb820a1a5d9c6a5c60d2f8de09e6b90f22654488cbf2014139b2bb660ab7ac111a246e1cf46cf
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ups-ruby (0.
|
4
|
+
ups-ruby (0.23.0)
|
5
5
|
excon (~> 0.45, >= 0.45.3)
|
6
6
|
insensitive_hash (~> 0.3.3)
|
7
7
|
levenshtein-ffi (~> 1.1)
|
@@ -13,8 +13,8 @@ GEM
|
|
13
13
|
codeclimate-test-reporter (1.0.5)
|
14
14
|
simplecov
|
15
15
|
docile (1.1.5)
|
16
|
-
excon (0.
|
17
|
-
ffi (1.
|
16
|
+
excon (0.83.0)
|
17
|
+
ffi (1.15.3)
|
18
18
|
insensitive_hash (0.3.3)
|
19
19
|
json (1.8.6)
|
20
20
|
levenshtein-ffi (1.1.0)
|
@@ -23,7 +23,7 @@ GEM
|
|
23
23
|
minitest (5.7.0)
|
24
24
|
nokogiri (1.10.3)
|
25
25
|
mini_portile2 (~> 2.4.0)
|
26
|
-
ox (2.
|
26
|
+
ox (2.14.5)
|
27
27
|
rake (12.0.0)
|
28
28
|
simplecov (0.10.0)
|
29
29
|
docile (~> 1.1.0)
|
@@ -69,10 +69,14 @@ module UPS
|
|
69
69
|
# @param [String] action The UPS API Action requested
|
70
70
|
# @param [String] option The UPS API Option
|
71
71
|
# @return [void]
|
72
|
-
def add_request(action, option)
|
72
|
+
def add_request(action, option, sub_version: nil)
|
73
73
|
root << Element.new('Request').tap do |request|
|
74
74
|
request << element_with_value('RequestAction', action)
|
75
75
|
request << element_with_value('RequestOption', option)
|
76
|
+
|
77
|
+
unless sub_version.nil?
|
78
|
+
request << element_with_value('SubVersion', sub_version)
|
79
|
+
end
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
@@ -143,12 +147,7 @@ module UPS
|
|
143
147
|
# @param [Hash] opts A Hash of data to build the requested section
|
144
148
|
# @return [void]
|
145
149
|
def add_package(opts = {})
|
146
|
-
shipment_root <<
|
147
|
-
org << packaging_type
|
148
|
-
org << element_with_value('Description', 'Rate')
|
149
|
-
org << package_weight(opts[:weight], opts[:unit])
|
150
|
-
org << package_dimensions(opts[:dimensions]) if opts[:dimensions]
|
151
|
-
end
|
150
|
+
shipment_root << PackageBuilder.new('Package', opts).to_xml
|
152
151
|
end
|
153
152
|
|
154
153
|
# Adds a PaymentInformation section to the XML document being built
|
@@ -165,6 +164,12 @@ module UPS
|
|
165
164
|
end
|
166
165
|
end
|
167
166
|
|
167
|
+
def add_itemized_payment_information(ship_number)
|
168
|
+
shipment_charge << Element.new('BillShipper').tap do |bill_shipper|
|
169
|
+
bill_shipper << element_with_value('AccountNumber', ship_number)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
168
173
|
# Adds a RateInformation/NegotiatedRatesIndicator section to the XML
|
169
174
|
# document being built
|
170
175
|
#
|
@@ -186,6 +191,27 @@ module UPS
|
|
186
191
|
end
|
187
192
|
end
|
188
193
|
|
194
|
+
# Adds Direct Delivery Only indicator to the shipment service options
|
195
|
+
#
|
196
|
+
# @return [void]
|
197
|
+
def add_shipment_direct_delivery_only
|
198
|
+
shipment_service_options << Element.new('DirectDeliveryOnlyIndicator')
|
199
|
+
end
|
200
|
+
|
201
|
+
# Adds MasterCartonIndicator to the shipment
|
202
|
+
#
|
203
|
+
# @return [void]
|
204
|
+
def add_master_carton_indicator
|
205
|
+
shipment_root << Element.new('MasterCartonIndicator')
|
206
|
+
end
|
207
|
+
|
208
|
+
# Adds MasterCartonID to the shipment
|
209
|
+
#
|
210
|
+
# @return [void]
|
211
|
+
def add_master_carton_id(master_carton_id)
|
212
|
+
shipment_root << element_with_value('MasterCartonID', master_carton_id)
|
213
|
+
end
|
214
|
+
|
189
215
|
# Returns a String representation of the XML document being built
|
190
216
|
#
|
191
217
|
# @return [String]
|
@@ -211,23 +237,11 @@ module UPS
|
|
211
237
|
end
|
212
238
|
end
|
213
239
|
|
214
|
-
def
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
Element.new('PackageWeight').tap do |org|
|
220
|
-
org << unit_of_measurement(unit)
|
221
|
-
org << element_with_value('Weight', weight)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def package_dimensions(dimensions)
|
226
|
-
Element.new('Dimensions').tap do |org|
|
227
|
-
org << unit_of_measurement(dimensions[:unit])
|
228
|
-
org << element_with_value('Length', dimensions[:length].to_s[0..8])
|
229
|
-
org << element_with_value('Width', dimensions[:width].to_s[0..8])
|
230
|
-
org << element_with_value('Height', dimensions[:height].to_s[0..8])
|
240
|
+
def shipment_charge
|
241
|
+
@shipment_charge ||= begin
|
242
|
+
element = Element.new('ShipmentCharge')
|
243
|
+
shipment_root << (Element.new('ItemizedPaymentInformation') << element)
|
244
|
+
element
|
231
245
|
end
|
232
246
|
end
|
233
247
|
|
@@ -23,13 +23,21 @@ module UPS
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def invoice_number
|
26
|
-
element_with_value('InvoiceNumber', opts[:invoice_number])
|
26
|
+
element_with_value('InvoiceNumber', opts[:invoice_number])
|
27
27
|
end
|
28
28
|
|
29
29
|
def invoice_date
|
30
30
|
element_with_value('InvoiceDate', opts[:invoice_date])
|
31
31
|
end
|
32
32
|
|
33
|
+
def terms_of_shipment
|
34
|
+
element_with_value('TermsOfShipment', opts[:terms_of_shipment])
|
35
|
+
end
|
36
|
+
|
37
|
+
def declaration_statement
|
38
|
+
element_with_value('DeclarationStatement', opts[:declaration_statement])
|
39
|
+
end
|
40
|
+
|
33
41
|
def reason_for_export
|
34
42
|
element_with_value('ReasonForExport', opts[:reason_for_export])
|
35
43
|
end
|
@@ -62,8 +70,10 @@ module UPS
|
|
62
70
|
def to_xml
|
63
71
|
Element.new(name).tap do |international_form|
|
64
72
|
international_form << form_type
|
65
|
-
international_form << invoice_number
|
73
|
+
international_form << invoice_number if opts[:invoice_number]
|
66
74
|
international_form << invoice_date
|
75
|
+
international_form << terms_of_shipment if opts[:terms_of_shipment]
|
76
|
+
international_form << declaration_statement if opts[:declaration_statement]
|
67
77
|
international_form << reason_for_export
|
68
78
|
international_form << currency_code
|
69
79
|
international_form << freight_charge
|
@@ -58,6 +58,13 @@ module UPS
|
|
58
58
|
element_with_value('TaxIdentificationNumber', opts[:sender_vat_number] || '')
|
59
59
|
end
|
60
60
|
|
61
|
+
# Returns an XML representation of the email address of the company
|
62
|
+
#
|
63
|
+
# @return [Ox::Element] XML representation of email address
|
64
|
+
def email_address
|
65
|
+
element_with_value('EMailAddress', opts[:email_address].to_s[0..50])
|
66
|
+
end
|
67
|
+
|
61
68
|
# Returns an XML representation of address
|
62
69
|
#
|
63
70
|
# @return [Ox::Element] An instance of {AddressBuilder} containing the
|
@@ -66,6 +73,19 @@ module UPS
|
|
66
73
|
AddressBuilder.new(opts).to_xml
|
67
74
|
end
|
68
75
|
|
76
|
+
# Returns an XML representation of vendor info (ioss number and more) of the company
|
77
|
+
#
|
78
|
+
# @return [Ox::Element] XML representation of sender_ioss_number
|
79
|
+
def vendor_info
|
80
|
+
ioss_vendor_collect_id = '0356'
|
81
|
+
|
82
|
+
Element.new('VendorInfo').tap do |vendor_info|
|
83
|
+
vendor_info << element_with_value('VendorCollectIDNumber', opts[:sender_ioss_number] || '')
|
84
|
+
vendor_info << element_with_value('VendorCollectIDTypeCode', ioss_vendor_collect_id)
|
85
|
+
vendor_info << element_with_value('ConsigneeType', '02')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
69
89
|
# Returns an XML representation of a UPS Organization
|
70
90
|
#
|
71
91
|
# @return [Ox::Element] XML representation of the current object
|
@@ -76,6 +96,8 @@ module UPS
|
|
76
96
|
org << attention_name
|
77
97
|
org << address
|
78
98
|
org << tax_identification_number
|
99
|
+
org << email_address
|
100
|
+
org << vendor_info unless opts[:sender_ioss_number].to_s.empty?
|
79
101
|
end
|
80
102
|
end
|
81
103
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'ox'
|
2
|
+
|
3
|
+
module UPS
|
4
|
+
module Builders
|
5
|
+
class PackageBuilder < BuilderBase
|
6
|
+
include Ox
|
7
|
+
|
8
|
+
attr_accessor :name, :opts
|
9
|
+
|
10
|
+
def initialize(name, opts = {})
|
11
|
+
self.name = name
|
12
|
+
self.opts = opts
|
13
|
+
end
|
14
|
+
|
15
|
+
def packaging_type(packaging_options_hash)
|
16
|
+
code_description 'PackagingType', packaging_options_hash[:code], packaging_options_hash[:description]
|
17
|
+
end
|
18
|
+
|
19
|
+
def reference_number
|
20
|
+
Element.new('ReferenceNumber').tap do |org|
|
21
|
+
org << element_with_value('Code', opts[:reference_number][:type]) if opts[:reference_number][:type]
|
22
|
+
org << element_with_value('Value', opts[:reference_number][:value])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def reference_number_2
|
27
|
+
Element.new('ReferenceNumber').tap do |org|
|
28
|
+
org << element_with_value('Code', opts[:reference_number_2][:type]) if opts[:reference_number_2][:type]
|
29
|
+
org << element_with_value('Value', opts[:reference_number_2][:value])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def description
|
34
|
+
element_with_value('Description', 'Rate')
|
35
|
+
end
|
36
|
+
|
37
|
+
def package_weight(weight, unit)
|
38
|
+
Element.new('PackageWeight').tap do |org|
|
39
|
+
org << unit_of_measurement(unit)
|
40
|
+
org << element_with_value('Weight', weight)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def customer_supplied_packaging
|
45
|
+
{ code: '02', description: 'Customer Supplied Package' }
|
46
|
+
end
|
47
|
+
|
48
|
+
def package_dimensions(dimensions)
|
49
|
+
Element.new('Dimensions').tap do |org|
|
50
|
+
org << unit_of_measurement(dimensions[:unit])
|
51
|
+
org << element_with_value('Length', dimensions[:length].to_s[0..8])
|
52
|
+
org << element_with_value('Width', dimensions[:width].to_s[0..8])
|
53
|
+
org << element_with_value('Height', dimensions[:height].to_s[0..8])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_xml
|
58
|
+
Element.new(name).tap do |product|
|
59
|
+
product << reference_number if opts[:reference_number]
|
60
|
+
product << reference_number_2 if opts[:reference_number_2]
|
61
|
+
product << packaging_type(opts[:packaging_type] || customer_supplied_packaging)
|
62
|
+
product << description
|
63
|
+
product << package_weight(opts[:weight], opts[:unit])
|
64
|
+
product << package_dimensions(opts[:dimensions]) if opts[:dimensions]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -16,7 +16,7 @@ module UPS
|
|
16
16
|
def initialize
|
17
17
|
super 'ShipmentConfirmRequest'
|
18
18
|
|
19
|
-
add_request 'ShipConfirm', 'validate'
|
19
|
+
add_request 'ShipConfirm', 'validate', sub_version: '1807'
|
20
20
|
end
|
21
21
|
|
22
22
|
# Adds a LabelSpecification section to the XML document being built
|
@@ -54,6 +54,10 @@ module UPS
|
|
54
54
|
service_description)
|
55
55
|
end
|
56
56
|
|
57
|
+
def add_invoice_line_total(value, currency_code)
|
58
|
+
shipment_root << invoice_line_total(value, currency_code)
|
59
|
+
end
|
60
|
+
|
57
61
|
# Adds Description to XML document being built
|
58
62
|
#
|
59
63
|
# @param [String] description The description for goods being sent
|
@@ -74,6 +78,31 @@ module UPS
|
|
74
78
|
shipment_root << reference_number(opts[:code], opts[:value])
|
75
79
|
end
|
76
80
|
|
81
|
+
def update_and_validate_for_worldwide_economy!
|
82
|
+
shipment_charge << element_with_value('Type', '01')
|
83
|
+
|
84
|
+
packages = document.locate('ShipmentConfirmRequest/Shipment/Package')
|
85
|
+
|
86
|
+
bill_shipper_account_number = document.locate(
|
87
|
+
'ShipmentConfirmRequest/Shipment/ItemizedPaymentInformation/ShipmentCharge/BillShipper/AccountNumber/*'
|
88
|
+
).first
|
89
|
+
|
90
|
+
unless packages.count == 1
|
91
|
+
raise InvalidAttributeError,
|
92
|
+
'Worldwide Economy shipment must be single-piece'
|
93
|
+
end
|
94
|
+
|
95
|
+
unless packages.first.locate('PackagingType/Code/*').first == '02'
|
96
|
+
raise InvalidAttributeError,
|
97
|
+
'Worldwide Economy shipment must use Customer Supplied Package'
|
98
|
+
end
|
99
|
+
|
100
|
+
unless bill_shipper_account_number.to_s.length > 0
|
101
|
+
raise InvalidAttributeError,
|
102
|
+
'Worldwide Economy shipment must have "Bill Shipper" Itemized Payment Information'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
77
106
|
private
|
78
107
|
|
79
108
|
def gif?(string)
|
@@ -107,6 +136,16 @@ module UPS
|
|
107
136
|
'Code' => code.to_s,
|
108
137
|
'Value' => value.to_s)
|
109
138
|
end
|
139
|
+
|
140
|
+
def invoice_line_total(value, currency_code)
|
141
|
+
multi_valued('InvoiceLineTotal',
|
142
|
+
'CurrencyCode' => currency_code.to_s,
|
143
|
+
'MonetaryValue' => value.to_s)
|
144
|
+
end
|
145
|
+
|
146
|
+
def service_code
|
147
|
+
document.locate('ShipmentConfirmRequest/Shipment/Service/Code/*').first
|
148
|
+
end
|
110
149
|
end
|
111
150
|
end
|
112
151
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'ox'
|
2
|
+
|
3
|
+
module UPS
|
4
|
+
module Builders
|
5
|
+
# The {TrackBuilder} class builds UPS XML Track Objects.
|
6
|
+
#
|
7
|
+
# @author Stephan van Diepen
|
8
|
+
# @since 0.17.1
|
9
|
+
class TrackBuilder < BuilderBase
|
10
|
+
include Ox
|
11
|
+
|
12
|
+
# Initializes a new {TrackBuilder} object
|
13
|
+
#
|
14
|
+
def initialize
|
15
|
+
super 'TrackRequest'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds an TrackingNumber to the XML document being built
|
19
|
+
# according to user inputs
|
20
|
+
#
|
21
|
+
# @return [void]
|
22
|
+
def add_tracking_number(number)
|
23
|
+
root << element_with_value('TrackingNumber', number)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds an OptionCode to the XML document being built
|
27
|
+
# according to user inputs
|
28
|
+
#
|
29
|
+
# @return [void]
|
30
|
+
def add_option_code(option_code)
|
31
|
+
root << Element.new('Request').tap do |request|
|
32
|
+
request << element_with_value('RequestOption', option_code)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/ups/connection.rb
CHANGED
@@ -21,6 +21,7 @@ module UPS
|
|
21
21
|
SHIP_CONFIRM_PATH = '/ups.app/xml/ShipConfirm'
|
22
22
|
SHIP_ACCEPT_PATH = '/ups.app/xml/ShipAccept'
|
23
23
|
ADDRESS_PATH = '/ups.app/xml/XAV'
|
24
|
+
TRACK_PATH = '/ups.app/xml/Track'
|
24
25
|
|
25
26
|
DEFAULT_PARAMS = {
|
26
27
|
test_mode: false
|
@@ -79,6 +80,27 @@ module UPS
|
|
79
80
|
make_accept_request(accept_builder)
|
80
81
|
end
|
81
82
|
|
83
|
+
# Makes a request to Track the status for a shipment.
|
84
|
+
#
|
85
|
+
# A pre-configured {Builders::TrackBuilder} object can be passed as the first
|
86
|
+
# option or a block yielded to configure a new {Builders::TrackBuilder}
|
87
|
+
# object.
|
88
|
+
#
|
89
|
+
# @param [Builders::TrackBuilder] track_builder A pre-configured
|
90
|
+
# {Builders::TrackBuilder} object to use
|
91
|
+
# @yield [track_builder] A TrackBuilder object for configuring
|
92
|
+
# the shipment information sent
|
93
|
+
def track(track_builder = nil)
|
94
|
+
if track_builder.nil? && block_given?
|
95
|
+
track_builder = UPS::Builders::TrackBuilder.new
|
96
|
+
yield track_builder
|
97
|
+
end
|
98
|
+
|
99
|
+
response = get_response(TRACK_PATH, track_builder.to_xml)
|
100
|
+
|
101
|
+
UPS::Parsers::TrackParser.new(response.body)
|
102
|
+
end
|
103
|
+
|
82
104
|
private
|
83
105
|
|
84
106
|
def build_url(path)
|
@@ -42,8 +42,34 @@ module UPS
|
|
42
42
|
[UPS::Models::PackageResult.new(package_results)]
|
43
43
|
end
|
44
44
|
|
45
|
+
def master_carton_id
|
46
|
+
shipment_results[:MasterCartonID]
|
47
|
+
end
|
48
|
+
|
49
|
+
def total_charge
|
50
|
+
return shipment_charge unless negotiated_rate
|
51
|
+
|
52
|
+
negotiated_rate
|
53
|
+
end
|
54
|
+
|
55
|
+
def negotiated_rate
|
56
|
+
negotiated_rate_response && negotiated_rate_response[:NetSummaryCharges][:GrandTotal][:MonetaryValue].to_f
|
57
|
+
end
|
58
|
+
|
59
|
+
def currency_code
|
60
|
+
shipment_results[:ShipmentCharges][:TotalCharges][:CurrencyCode]
|
61
|
+
end
|
62
|
+
|
45
63
|
private
|
46
64
|
|
65
|
+
def negotiated_rate_response
|
66
|
+
shipment_results[:NegotiatedRates]
|
67
|
+
end
|
68
|
+
|
69
|
+
def shipment_charge
|
70
|
+
shipment_results[:ShipmentCharges][:TotalCharges][:MonetaryValue].to_f
|
71
|
+
end
|
72
|
+
|
47
73
|
def form_graphic
|
48
74
|
shipment_results[:Form]
|
49
75
|
end
|