ups_shipping 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ups_shipping.rb +190 -0
- data/lib/ups_shipping/address.rb +39 -0
- data/lib/ups_shipping/organization.rb +39 -0
- data/lib/ups_shipping/package.rb +45 -0
- data/lib/ups_shipping/pickup.rb +49 -0
- data/spec/ups_shipping_spec.rb +42 -0
- metadata +84 -0
data/lib/ups_shipping.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "httparty"
|
3
|
+
require 'ups_shipping/address'
|
4
|
+
require 'ups_shipping/organization'
|
5
|
+
require 'ups_shipping/package'
|
6
|
+
require 'ups_shipping/pickup'
|
7
|
+
|
8
|
+
module Shipping
|
9
|
+
|
10
|
+
VERSION = '1.0.0'
|
11
|
+
|
12
|
+
class UPS
|
13
|
+
|
14
|
+
TEST_URL = "https://wwwcie.ups.com"
|
15
|
+
LIVE_URL = "https://onlinetools.ups.com"
|
16
|
+
|
17
|
+
class Http
|
18
|
+
include HTTParty
|
19
|
+
base_uri LIVE_URL
|
20
|
+
|
21
|
+
def initialize(access_request, options={})
|
22
|
+
@access_request = access_request
|
23
|
+
|
24
|
+
if (options[:test])
|
25
|
+
self.class.base_uri TEST_URL
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def commit(url, request)
|
30
|
+
request = @access_request + request
|
31
|
+
self.class.post(url, :body => request).parsed_response
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(user, password, license, options={})
|
36
|
+
@options = options
|
37
|
+
@shipper = options[:shipper]
|
38
|
+
@access_request = access_request(user, password, license)
|
39
|
+
@http = Http.new(@access_request, :test => @options[:test])
|
40
|
+
|
41
|
+
@services = {
|
42
|
+
"01" => "Next Day Air",
|
43
|
+
"02" => "2nd Day Air",
|
44
|
+
"03" => "Ground",
|
45
|
+
"07" => "Express",
|
46
|
+
"08" => "Expedited",
|
47
|
+
"11" => "UPS Standard",
|
48
|
+
"12" => "3 Day Select",
|
49
|
+
"13" => "Next Day Air Saver",
|
50
|
+
"14" => "Next Day Air Early AM",
|
51
|
+
"54" => "Express Plus",
|
52
|
+
"59" => "2nd Day Air A.M.",
|
53
|
+
"65" => "UPS Saver",
|
54
|
+
"82" => "UPS Today Standard",
|
55
|
+
"83" => "UPS Today Dedicated Courier",
|
56
|
+
"84" => "UPS Today Intercity",
|
57
|
+
"85" => "UPS Today Express",
|
58
|
+
"86" => "UPS Today Express Saver"
|
59
|
+
}
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_address(address)
|
64
|
+
validate_request = Nokogiri::XML::Builder.new do |xml|
|
65
|
+
xml.AddressValidationRequest {
|
66
|
+
xml.Request {
|
67
|
+
xml.RequestAction "XAV"
|
68
|
+
xml.RequestOption "3"
|
69
|
+
}
|
70
|
+
xml.AddressKeyFormat {
|
71
|
+
xml.AddressLine address.address_line[0]
|
72
|
+
xml.PoliticalDivision2 address.city
|
73
|
+
xml.PoliticalDivision1 address.state
|
74
|
+
xml.PostcodePrimaryLow address.zip
|
75
|
+
xml.CountryCode address.country
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
@http.commit("/ups.app/xml/XAV", validate_request.to_xml)
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_rates(package, origin, destination, options={})
|
83
|
+
rate_request = Nokogiri::XML::Builder.new do |xml|
|
84
|
+
xml.RatingServiceSelectionRequest {
|
85
|
+
xml.Request {
|
86
|
+
xml.RequestAction "Rate"
|
87
|
+
xml.RequestOption "Rate"
|
88
|
+
}
|
89
|
+
if options[:pickup]
|
90
|
+
@options[:pickup].build_type(xml)
|
91
|
+
end
|
92
|
+
@shipper.build(xml, "Shipper")
|
93
|
+
destination.build(xml, "ShipTo")
|
94
|
+
origin.build(xml, "ShipFrom")
|
95
|
+
xml.PaymentInformation {
|
96
|
+
xml.Prepaid {
|
97
|
+
xml.BillShipper {
|
98
|
+
xml.AccountNumber "Ship Number"
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
package.build(xml)
|
103
|
+
}
|
104
|
+
end
|
105
|
+
@http.commit("/ups.app/xml/Rate", rate_request.to_xml)
|
106
|
+
end
|
107
|
+
|
108
|
+
def request_shipment(package, origin, destination, service, options={})
|
109
|
+
shipment_request = Nokogiri::XML::Builder.new do |xml|
|
110
|
+
xml.ShipmentConfirmRequest {
|
111
|
+
xml.Request {
|
112
|
+
xml.RequestAction "ShipConfirm"
|
113
|
+
xml.RequestOptions "validate"
|
114
|
+
}
|
115
|
+
if options[:label]
|
116
|
+
xml.LabelSpecification {
|
117
|
+
xml.LabelPrintMethod {
|
118
|
+
xml.Code "GIF"
|
119
|
+
xml.Description "gif file"
|
120
|
+
}
|
121
|
+
xml.HTTPUserAgent "Mozilla/4.5"
|
122
|
+
xml.LabelImageFormat {
|
123
|
+
xml.Code "GIF"
|
124
|
+
xml.Description "gif"
|
125
|
+
}
|
126
|
+
}
|
127
|
+
end
|
128
|
+
@shipper.build(xml, "Shipper")
|
129
|
+
destination.build(xml, "ShipTo")
|
130
|
+
origin.build(xml, "ShipFrom")
|
131
|
+
xml.PaymentInformation {
|
132
|
+
xml.Prepaid {
|
133
|
+
xml.BillShipper {
|
134
|
+
xml.AccountNumber "Ship Number"
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
xml.Service {
|
139
|
+
xml.Code service
|
140
|
+
xml.Description @services[service]
|
141
|
+
}
|
142
|
+
package.build(xml)
|
143
|
+
}
|
144
|
+
end
|
145
|
+
@http.commit("/ups.app/xml/ShipConfirm", shipment_request.to_xml)
|
146
|
+
end
|
147
|
+
|
148
|
+
def cancel_shipment(shipment_id)
|
149
|
+
cancel_request = Nokogiri::XML::Builder.new do |xml|
|
150
|
+
xml.VoidShipmentRequest {
|
151
|
+
xml.Request {
|
152
|
+
xml.RequestAction "1"
|
153
|
+
xml.RequestOption "1"
|
154
|
+
}
|
155
|
+
xml.ShipmentIdentificationNumber shipment_id
|
156
|
+
}
|
157
|
+
end
|
158
|
+
@http.commit("/ups.app/xml/Void", cancel_request.to_xml)
|
159
|
+
end
|
160
|
+
|
161
|
+
def track_shipment(tracking_number)
|
162
|
+
track_request = Nokogiri::XML::Builder.new do
|
163
|
+
TrackRequest {
|
164
|
+
Request {
|
165
|
+
RequestAction "Track"
|
166
|
+
RequestOption "activity"
|
167
|
+
}
|
168
|
+
TrackingNumber tracking_number
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
@http.commit("/ups.app/xml/Track", track_request.to_xml)
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
private
|
177
|
+
def access_request(user, password, license)
|
178
|
+
access_request = Nokogiri::XML::Builder.new do
|
179
|
+
AccessRequest {
|
180
|
+
AccessLicenseNumber license
|
181
|
+
UserId user
|
182
|
+
Password password
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
access_request.to_xml
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
3
|
+
module Shipping
|
4
|
+
class Address
|
5
|
+
ADDRESS_TYPES = %w{residential commercial po_box}
|
6
|
+
attr_accessor :address_lines
|
7
|
+
:city
|
8
|
+
:state
|
9
|
+
:zip
|
10
|
+
:country
|
11
|
+
:type
|
12
|
+
|
13
|
+
def initialize(options={})
|
14
|
+
@address_lines = options[:address_lines]
|
15
|
+
@city = options[:city]
|
16
|
+
@state = options[:state]
|
17
|
+
@zip = options[:zip]
|
18
|
+
@country = options[:country]
|
19
|
+
@type = options[:type]
|
20
|
+
end
|
21
|
+
|
22
|
+
def build(xml)
|
23
|
+
xml.Address {
|
24
|
+
xml.AddressLine1 @address_lines[0]
|
25
|
+
xml.City @city
|
26
|
+
xml.StateProvinceCode @state
|
27
|
+
xml.PostalCode @zip
|
28
|
+
xml.CountryCode @country
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_xml()
|
33
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
34
|
+
build(xml)
|
35
|
+
end
|
36
|
+
builder.to_xml
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
3
|
+
module Shipping
|
4
|
+
class Organization
|
5
|
+
attr_accessor :name
|
6
|
+
:phone
|
7
|
+
:email
|
8
|
+
:address
|
9
|
+
:shipper_number
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(options={})
|
13
|
+
@name = options[:name]
|
14
|
+
@phone = options[:phone]
|
15
|
+
@address = options[:address]
|
16
|
+
if options[:shipper_number]
|
17
|
+
@shipper_number = options[:shipper_number]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def build(xml, rootname)
|
22
|
+
xml.send(rootname) {
|
23
|
+
xml.CompanyName @name
|
24
|
+
xml.PhoneNumber @phone
|
25
|
+
if @shipper_number
|
26
|
+
xml.ShipperNumber @shipper_number
|
27
|
+
end
|
28
|
+
@address.build(xml)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_xml(rootname)
|
33
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
34
|
+
build(xml, rootname)
|
35
|
+
end
|
36
|
+
builder.to_xml
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
3
|
+
module Shipping
|
4
|
+
class Package
|
5
|
+
attr_accessor :large
|
6
|
+
:weight
|
7
|
+
:description
|
8
|
+
:monetary_value
|
9
|
+
|
10
|
+
def initialize(options={})
|
11
|
+
@large = options[:large]
|
12
|
+
@weight = options[:weight]
|
13
|
+
@description = options[:description]
|
14
|
+
@monetary_value = options[:monetary_value]
|
15
|
+
end
|
16
|
+
|
17
|
+
def build(xml)
|
18
|
+
xml.Package {
|
19
|
+
xml.PackagingType {
|
20
|
+
xml.Code "02"
|
21
|
+
xml.Description "Customer Supplied"
|
22
|
+
}
|
23
|
+
xml.Description @description
|
24
|
+
xml.ReferenceNumber {
|
25
|
+
xml.Code "00"
|
26
|
+
xml.Value "Package"
|
27
|
+
}
|
28
|
+
xml.PackageWeight {
|
29
|
+
xml.UnitOfMeasurement
|
30
|
+
xml.Weight @weight
|
31
|
+
}
|
32
|
+
if @large
|
33
|
+
xml.LargePackageIndicator
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_xml()
|
39
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
40
|
+
build(xml)
|
41
|
+
end
|
42
|
+
builder.to_xml
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
3
|
+
module Shipping
|
4
|
+
class Pickup
|
5
|
+
attr_accessor :address_lines
|
6
|
+
:pickup_day
|
7
|
+
:contact_method
|
8
|
+
:type
|
9
|
+
|
10
|
+
def initialize(options={})
|
11
|
+
@type = options[:type]
|
12
|
+
if options[:pickup_day]
|
13
|
+
@pickup_day = options[:pickup_day]
|
14
|
+
end
|
15
|
+
if options[:contact_method]
|
16
|
+
@contact_method = options[:contact_method]
|
17
|
+
end
|
18
|
+
|
19
|
+
@pickupTypes = {
|
20
|
+
"01" => "Daily Pickup",
|
21
|
+
"03" => "Customer Counter",
|
22
|
+
"06" => "One Time Pickup",
|
23
|
+
"07" => "On Call Air",
|
24
|
+
"19" => "Letter Center",
|
25
|
+
"20" => "Air Service Center"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_schedule(xml)
|
30
|
+
xml.OnCallAir {
|
31
|
+
xml.Schedule {
|
32
|
+
if @pickup_day
|
33
|
+
xml.PickupDay @pickup_day
|
34
|
+
end
|
35
|
+
if @contact_method
|
36
|
+
xml.Method @contact_method
|
37
|
+
end
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_type(xml)
|
43
|
+
xml.PickupType {
|
44
|
+
xml.Code type
|
45
|
+
xml.Description @pickupTypes[type]
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "ups_shipping"
|
3
|
+
require "address"
|
4
|
+
require "organization"
|
5
|
+
|
6
|
+
describe Shipping::UPS do
|
7
|
+
before(:all) do
|
8
|
+
@ups = Shipping::UPS.new("deanchen", "Transcriptic#1", "EC96D31A8D672E28", :test => true)
|
9
|
+
@address = Shipping::Address.new(
|
10
|
+
:address_lines => ["1402 Faber St."],
|
11
|
+
:city => "Durham",
|
12
|
+
:state => "NC",
|
13
|
+
:zip => "27705",
|
14
|
+
:country => "US"
|
15
|
+
)
|
16
|
+
end
|
17
|
+
it "address object" do
|
18
|
+
@address.to_xml.gsub(/^\s+/, "").gsub(/\s+$/, $/).should == '
|
19
|
+
<?xml version="1.0"?>
|
20
|
+
<Address>
|
21
|
+
<AddressLine1>1402 Faber St.</AddressLine1>
|
22
|
+
<City>Durham</City>
|
23
|
+
<StateProvinceCode>NC</StateProvinceCode>
|
24
|
+
<PostalCode>27705</PostalCode>
|
25
|
+
<CountryCode>US</CountryCode>
|
26
|
+
</Address>
|
27
|
+
'.gsub(/^\s+/, "").gsub(/\s+$/, $/)
|
28
|
+
end
|
29
|
+
it "organization object" do
|
30
|
+
@organization = Shipping::Organization.new(
|
31
|
+
:name => "Transcriptic",
|
32
|
+
:phone => "1233455678",
|
33
|
+
:address => @address
|
34
|
+
)
|
35
|
+
puts @organization.to_xml("Shipper")
|
36
|
+
end
|
37
|
+
it "#track_shipment" do
|
38
|
+
tracking_result = @ups.track_shipment("1ZXX31290231000092")
|
39
|
+
tracking_result.should have_key("TrackResponse")
|
40
|
+
tracking_result["TrackResponse"]["Response"]["ResponseStatusCode"].should == "1"
|
41
|
+
end
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ups_shipping
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Transcriptic, Inc
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-04 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: &70314021897160 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70314021897160
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70314021896740 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70314021896740
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: httparty
|
38
|
+
requirement: &70314021896320 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70314021896320
|
47
|
+
description: UPS shipping gem for integrating UPS's API into a Ruby script.
|
48
|
+
email: team@transcriptic.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- lib/ups_shipping.rb
|
54
|
+
- lib/ups_shipping/address.rb
|
55
|
+
- lib/ups_shipping/organization.rb
|
56
|
+
- lib/ups_shipping/package.rb
|
57
|
+
- lib/ups_shipping/pickup.rb
|
58
|
+
- spec/ups_shipping_spec.rb
|
59
|
+
homepage: http://www.github.com/transcriptic/ups-shipping
|
60
|
+
licenses: []
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.8.6
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: UPS shipping API integration.
|
83
|
+
test_files: []
|
84
|
+
has_rdoc:
|