mc-fedex 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +57 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/lib/constants.rb +73 -0
- data/lib/mc-fedex.rb +297 -0
- data/mc-fedex.gemspec +73 -0
- data/spec/mc-fedex_spec.rb +52 -0
- data/spec/spec_helper.rb +22 -0
- metadata +182 -0
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
gem 'soap4r'
|
9
|
+
group :development do
|
10
|
+
gem "rspec"
|
11
|
+
gem "bundler", "~> 1.0.0"
|
12
|
+
gem "jeweler", "~> 1.5.1"
|
13
|
+
gem "rcov", ">= 0"
|
14
|
+
gem "ruby-debug"
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
columnize (0.3.2)
|
5
|
+
diff-lcs (1.1.2)
|
6
|
+
git (1.2.5)
|
7
|
+
httpclient (2.1.6.1)
|
8
|
+
jeweler (1.5.2)
|
9
|
+
bundler (~> 1.0.0)
|
10
|
+
git (>= 1.2.5)
|
11
|
+
rake
|
12
|
+
linecache (0.43)
|
13
|
+
rake (0.8.7)
|
14
|
+
rcov (0.9.9)
|
15
|
+
rspec (2.4.0)
|
16
|
+
rspec-core (~> 2.4.0)
|
17
|
+
rspec-expectations (~> 2.4.0)
|
18
|
+
rspec-mocks (~> 2.4.0)
|
19
|
+
rspec-core (2.4.0)
|
20
|
+
rspec-expectations (2.4.0)
|
21
|
+
diff-lcs (~> 1.1.2)
|
22
|
+
rspec-mocks (2.4.0)
|
23
|
+
ruby-debug (0.10.4)
|
24
|
+
columnize (>= 0.1)
|
25
|
+
ruby-debug-base (~> 0.10.4.0)
|
26
|
+
ruby-debug-base (0.10.4)
|
27
|
+
linecache (>= 0.3)
|
28
|
+
soap4r (1.5.8)
|
29
|
+
httpclient (>= 2.1.1)
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
ruby
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
bundler (~> 1.0.0)
|
36
|
+
jeweler (~> 1.5.1)
|
37
|
+
rcov
|
38
|
+
rspec
|
39
|
+
ruby-debug
|
40
|
+
soap4r
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Edwin & Adam
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
= mc-fedex
|
2
|
+
|
3
|
+
A gem to generate a FedEx label. You must supply the WSDL file in roder for this to work. We've set up limited tests due to the fact that the WSDL is not included.
|
4
|
+
|
5
|
+
Some code were copied from: https://github.com/mcmire/fedex
|
6
|
+
|
7
|
+
== Usage
|
8
|
+
|
9
|
+
= Create an instance
|
10
|
+
These are the required options:
|
11
|
+
|
12
|
+
driver = McFedex::Base.new(:auth_key => AUTH_KEY,
|
13
|
+
:security_code => SECURITY_CODE,
|
14
|
+
:account_number => ACCOUNT_NUMBER,
|
15
|
+
:meter_number => METER_NUMBER,
|
16
|
+
:wsdl_path => WSDL_PATH,
|
17
|
+
:smart_post_hub => SMART_POST_HUB,
|
18
|
+
:use_smart_post => USE_SMART_POST)
|
19
|
+
|
20
|
+
Passing :debug => true will output XML passed to FedEx, and FedEx's response.
|
21
|
+
|
22
|
+
= Supplied variables
|
23
|
+
When setting :use_smart_post to true, you will generate a SmartPost returns label with the supplied :smart_post_hub id.
|
24
|
+
If set to false, it will default to a normal returns label.
|
25
|
+
|
26
|
+
You can set the default for your normal label, by specifying:
|
27
|
+
McFedex::ServiceTypes::FEDEX_GROUND as the service type.
|
28
|
+
|
29
|
+
= Generating the label
|
30
|
+
driver.label(
|
31
|
+
:shipper => {:contact => SHIPPER, :address => SHIPPER_ORIGIN},
|
32
|
+
:recipient => {:contact => RECIPIENT, :address => RECIPIENT_ADDRESS},
|
33
|
+
:count => PACKAGE_COUNT,
|
34
|
+
:weight => PACKAGE_WEIGHT,
|
35
|
+
:service_type => SERVICE_TYPE
|
36
|
+
)
|
37
|
+
|
38
|
+
This method will return 3 values:
|
39
|
+
* Price
|
40
|
+
* Label
|
41
|
+
* Tracking Number
|
42
|
+
|
43
|
+
== Contributing to mc-fedex
|
44
|
+
|
45
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
46
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
47
|
+
* Fork the project
|
48
|
+
* Start a feature/bugfix branch
|
49
|
+
* Commit and push until you are happy with your contribution
|
50
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
51
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
52
|
+
|
53
|
+
== Copyright
|
54
|
+
|
55
|
+
Copyright (c) 2011 Edwin & Adam. See LICENSE.txt for
|
56
|
+
further details.
|
57
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "mc-fedex"
|
16
|
+
gem.homepage = "http://github.com/modcloth/mc-fedex"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = "Fedex library on ruby"
|
19
|
+
gem.description = "Fedex wrapper to print FedEx labels"
|
20
|
+
gem.email = "github@modcloth.com"
|
21
|
+
gem.authors = ["Edwin & Adam & Eric"]
|
22
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
25
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
26
|
+
gem.add_runtime_dependency 'soap4r', ">= 1.5.8"
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rspec/core'
|
31
|
+
require 'rspec/core/rake_task'
|
32
|
+
require 'ruby-debug'
|
33
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
34
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :spec
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "mc-fedex #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/lib/constants.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module McFedex
|
2
|
+
# {http://fedex.com/ws/rate/v5}PackagingType
|
3
|
+
module PackagingTypes
|
4
|
+
FEDEX_10KG_BOX = "FEDEX_10KG_BOX"
|
5
|
+
FEDEX_25KG_BOX = "FEDEX_25KG_BOX"
|
6
|
+
FEDEX_BOX = "FEDEX_BOX"
|
7
|
+
FEDEX_ENVELOPE = "FEDEX_ENVELOPE"
|
8
|
+
FEDEX_PAK = "FEDEX_PAK"
|
9
|
+
FEDEX_TUBE = "FEDEX_TUBE"
|
10
|
+
YOUR_PACKAGING = "YOUR_PACKAGING"
|
11
|
+
end
|
12
|
+
|
13
|
+
# {http://fedex.com/ws/rate/v5}DropoffType
|
14
|
+
module DropoffTypes
|
15
|
+
BUSINESS_SERVICE_CENTER = "BUSINESS_SERVICE_CENTER"
|
16
|
+
DROP_BOX = "DROP_BOX"
|
17
|
+
REGULAR_PICKUP = "REGULAR_PICKUP"
|
18
|
+
REQUEST_COURIER = "REQUEST_COURIER"
|
19
|
+
STATION = "STATION"
|
20
|
+
end
|
21
|
+
|
22
|
+
# {http://fedex.com/ws/ship/v5}LabelFormatType
|
23
|
+
module LabelFormatTypes
|
24
|
+
COMMON2D = "COMMON2D"
|
25
|
+
LABEL_DATA_ONLY = "LABEL_DATA_ONLY"
|
26
|
+
end
|
27
|
+
|
28
|
+
# {http://fedex.com/ws/ship/v5}LabelSpecificationImageType
|
29
|
+
module LabelSpecificationImageTypes
|
30
|
+
DPL = "DPL"
|
31
|
+
EPL2 = "EPL2"
|
32
|
+
PDF = "PDF"
|
33
|
+
PNG = "PNG"
|
34
|
+
ZPLII = "ZPLII"
|
35
|
+
end
|
36
|
+
|
37
|
+
# {http://fedex.com/ws/rate/v5}RateRequestType
|
38
|
+
module RateRequestTypes
|
39
|
+
ACCOUNT = "ACCOUNT"
|
40
|
+
LIST = "LIST"
|
41
|
+
MULTIWEIGHT = "MULTIWEIGHT"
|
42
|
+
end
|
43
|
+
# {http://fedex.com/ws/rate/v5}PaymentType
|
44
|
+
module PaymentTypes
|
45
|
+
SENDER = "SENDER"
|
46
|
+
end
|
47
|
+
# {http://fedex.com/ws/rate/v5}WeightUnits
|
48
|
+
module WeightUnits
|
49
|
+
KG = "KG"
|
50
|
+
LB = "LB"
|
51
|
+
end
|
52
|
+
|
53
|
+
# {http://fedex.com/ws/rate/v5}ServiceType
|
54
|
+
module ServiceTypes
|
55
|
+
EUROPE_FIRST_INTERNATIONAL_PRIORITY = "EUROPE_FIRST_INTERNATIONAL_PRIORITY"
|
56
|
+
FEDEX_1_DAY_FREIGHT = "FEDEX_1_DAY_FREIGHT"
|
57
|
+
FEDEX_2_DAY = "FEDEX_2_DAY"
|
58
|
+
SMART_POST = "SMART_POST"
|
59
|
+
FEDEX_2_DAY_FREIGHT = "FEDEX_2_DAY_FREIGHT"
|
60
|
+
FEDEX_3_DAY_FREIGHT = "FEDEX_3_DAY_FREIGHT"
|
61
|
+
FEDEX_EXPRESS_SAVER = "FEDEX_EXPRESS_SAVER"
|
62
|
+
FEDEX_GROUND = "FEDEX_GROUND"
|
63
|
+
FIRST_OVERNIGHT = "FIRST_OVERNIGHT"
|
64
|
+
GROUND_HOME_DELIVERY = "GROUND_HOME_DELIVERY"
|
65
|
+
INTERNATIONAL_ECONOMY = "INTERNATIONAL_ECONOMY"
|
66
|
+
INTERNATIONAL_ECONOMY_FREIGHT = "INTERNATIONAL_ECONOMY_FREIGHT"
|
67
|
+
INTERNATIONAL_FIRST = "INTERNATIONAL_FIRST"
|
68
|
+
INTERNATIONAL_PRIORITY = "INTERNATIONAL_PRIORITY"
|
69
|
+
INTERNATIONAL_PRIORITY_FREIGHT = "INTERNATIONAL_PRIORITY_FREIGHT"
|
70
|
+
PRIORITY_OVERNIGHT = "PRIORITY_OVERNIGHT"
|
71
|
+
STANDARD_OVERNIGHT = "STANDARD_OVERNIGHT"
|
72
|
+
end
|
73
|
+
end
|
data/lib/mc-fedex.rb
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'constants'
|
3
|
+
require 'soap/wsdlDriver'
|
4
|
+
#require 'cgi'
|
5
|
+
require 'base64'
|
6
|
+
module McFedex
|
7
|
+
class FedexError < StandardError; end #:nodoc
|
8
|
+
|
9
|
+
class Base
|
10
|
+
# Defines the required parameters for various methods
|
11
|
+
REQUIRED_OPTIONS = {
|
12
|
+
:base => [ :auth_key, :security_code, :account_number, :meter_number ],
|
13
|
+
:price => [ :shipper, :recipient, :weight ],
|
14
|
+
:label => [ :shipper, :recipient, :weight, :service_type ],
|
15
|
+
:contact => [ :name, :phone_number ],
|
16
|
+
:address => [ :country, :street, :city, :state, :zip ],
|
17
|
+
:ship_cancel => [ :tracking_number ]
|
18
|
+
}
|
19
|
+
|
20
|
+
# Defines the relative path to the WSDL files. Defaults assume lib/wsdl under plugin directory.
|
21
|
+
WSDL_PATHS = {
|
22
|
+
:ship => @wsdl_path,
|
23
|
+
}
|
24
|
+
|
25
|
+
# DIR = File.dirname(__FILE__)
|
26
|
+
|
27
|
+
# Defines the Web Services version implemented.
|
28
|
+
WS_VERSION = { :Major => 9, :Intermediate => 0, :Minor => 0, :ServiceId => 'ship' }
|
29
|
+
|
30
|
+
SUCCESSFUL_RESPONSES = ['SUCCESS', 'WARNING', 'NOTE'] #:nodoc:
|
31
|
+
|
32
|
+
# Initializes the Fedex::Base class, setting defaults where necessary.
|
33
|
+
#
|
34
|
+
# fedex = Fedex::Base.new(options = {})
|
35
|
+
#
|
36
|
+
# === Example:
|
37
|
+
# fedex = Fedex::Base.new(:auth_key => AUTH_KEY,
|
38
|
+
# :security_code => SECURITY_CODE
|
39
|
+
# :account_number => ACCOUNT_NUMBER,
|
40
|
+
# :meter_number => METER_NUMBER)
|
41
|
+
#
|
42
|
+
# === Required options for new
|
43
|
+
# :auth_key - Your Fedex Authorization Key
|
44
|
+
# :security_code - Your Fedex Security Code
|
45
|
+
# :account_number - Your Fedex Account Number
|
46
|
+
# :meter_number - Your Fedex Meter Number
|
47
|
+
#
|
48
|
+
# === Additional options
|
49
|
+
# :dropoff_type - One of Fedex::DropoffTypes. Defaults to DropoffTypes::REGULAR_PICKUP
|
50
|
+
# :packaging_type - One of Fedex::PackagingTypes. Defaults to PackagingTypes::YOUR_PACKAGING
|
51
|
+
# :label_type - One of Fedex::LabelFormatTypes. Defaults to LabelFormatTypes::COMMON2D. You'll only need to change this
|
52
|
+
# if you're generating completely custom labels with a format of your own design. If printing to Fedex stock
|
53
|
+
# leave this alone.
|
54
|
+
# :label_image_type - One of Fedex::LabelSpecificationImageTypes. Defaults to LabelSpecificationImageTypes::PDF.
|
55
|
+
# :rate_request_type - One of Fedex::RateRequestTypes. Defaults to RateRequestTypes::ACCOUNT
|
56
|
+
# :payment - One of Fedex::PaymentTypes. Defaults to PaymentTypes::SENDER
|
57
|
+
# :units - One of Fedex::WeightUnits. Defaults to WeightUnits::LB
|
58
|
+
# :currency - One of Fedex::CurrencyTypes. Defaults to CurrencyTypes::USD
|
59
|
+
# :debug - Enable or disable debug (wiredump) output. Defaults to false.
|
60
|
+
def initialize(options = {})
|
61
|
+
check_required_options(:base, options)
|
62
|
+
|
63
|
+
@auth_key = options[:auth_key]
|
64
|
+
@security_code = options[:security_code]
|
65
|
+
@account_number = options[:account_number]
|
66
|
+
@meter_number = options[:meter_number]
|
67
|
+
|
68
|
+
@wsdl_path = options[:wsdl_path]
|
69
|
+
@use_smart_post = options[:use_smart_post]
|
70
|
+
@smart_post_hub = options[:smart_post_hub]
|
71
|
+
|
72
|
+
@dropoff_type = options[:dropoff_type] || DropoffTypes::REGULAR_PICKUP
|
73
|
+
@packaging_type = options[:packaging_type] || PackagingTypes::YOUR_PACKAGING
|
74
|
+
@label_type = options[:label_type] || LabelFormatTypes::COMMON2D
|
75
|
+
@label_image_type = options[:label_image_type] || LabelSpecificationImageTypes::PDF
|
76
|
+
@rate_request_type = options[:rate_request_type] || RateRequestTypes::LIST
|
77
|
+
@payment_type = options[:payment] || PaymentTypes::SENDER
|
78
|
+
@units = options[:units] || WeightUnits::LB
|
79
|
+
@currency = options[:currency] || 'USD'
|
80
|
+
@debug = options[:debug] || false
|
81
|
+
end
|
82
|
+
|
83
|
+
# Generate a new shipment and return associated data, including price, tracking number, and the label itself.
|
84
|
+
#
|
85
|
+
# fedex = Fedex::Base.new(options)
|
86
|
+
# price, label, tracking_number = fedex.label(fields)
|
87
|
+
#
|
88
|
+
# Returns the actual price for the label, the Base64-decoded label in the format specified in Fedex::Base,
|
89
|
+
# and the tracking_number for the shipment.
|
90
|
+
#
|
91
|
+
# === Required options for label
|
92
|
+
# :shipper - A hash containing contact information and an address for the shipper. (See below.)
|
93
|
+
# :recipient - A hash containing contact information and an address for the recipient. (See below.)
|
94
|
+
# :weight - The total weight of the shipped package.
|
95
|
+
# :service_type - One of Fedex::ServiceTypes
|
96
|
+
#
|
97
|
+
# === Address format
|
98
|
+
# The 'shipper' and 'recipient' address values should be hashes. Like this:
|
99
|
+
#
|
100
|
+
# shipper = {:contact => {:name => 'John Doe',
|
101
|
+
# :phone_number => '4805551212'},
|
102
|
+
# :address => address} # See "Address" for under price.
|
103
|
+
def label(options = {})
|
104
|
+
puts options.inspect if @debug
|
105
|
+
# Check overall options
|
106
|
+
check_required_options(:label, options)
|
107
|
+
|
108
|
+
# Check Address Options
|
109
|
+
check_required_options(:contact, options[:shipper][:contact])
|
110
|
+
check_required_options(:address, options[:shipper][:address])
|
111
|
+
|
112
|
+
# Check Contact Options
|
113
|
+
check_required_options(:contact, options[:recipient][:contact])
|
114
|
+
check_required_options(:address, options[:recipient][:address])
|
115
|
+
|
116
|
+
# Prepare variables
|
117
|
+
shipper = options[:shipper]
|
118
|
+
recipient = options[:recipient]
|
119
|
+
|
120
|
+
shipper_contact = shipper[:contact]
|
121
|
+
shipper_address = shipper[:address]
|
122
|
+
|
123
|
+
recipient_contact = recipient[:contact]
|
124
|
+
recipient_address = recipient[:address]
|
125
|
+
|
126
|
+
service_type = options[:service_type]
|
127
|
+
count = options[:count] || 1
|
128
|
+
weight = options[:weight]
|
129
|
+
rma = options[:rma] || ""
|
130
|
+
|
131
|
+
time = options[:time] || Time.now
|
132
|
+
puts time.class
|
133
|
+
time = time.iso8601 if time.is_a?(Time)
|
134
|
+
|
135
|
+
residential = !!recipient_address[:residential]
|
136
|
+
|
137
|
+
service_type = resolve_service_type(service_type, residential)
|
138
|
+
|
139
|
+
# Create the driver
|
140
|
+
driver = create_driver(:ship)
|
141
|
+
|
142
|
+
smart_post_data = {
|
143
|
+
:SmartPostDetail => {
|
144
|
+
:Indicia => "PARCEL_RETURN",
|
145
|
+
:HubId => @smart_post_hub
|
146
|
+
},
|
147
|
+
:SpecialServicesRequested => {
|
148
|
+
:SpecialServiceTypes => "RETURN_SHIPMENT",
|
149
|
+
:ReturnShipmentDetail => {
|
150
|
+
:ReturnType => "PRINT_RETURN_LABEL",
|
151
|
+
:Rma => {
|
152
|
+
:Number => rma
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
} if @use_smart_post
|
157
|
+
|
158
|
+
result = driver.processShipment(common_options.merge(
|
159
|
+
:RequestedShipment => {
|
160
|
+
:ShipTimestamp => time,
|
161
|
+
:DropoffType => @dropoff_type,
|
162
|
+
:ServiceType => service_type,
|
163
|
+
:PackagingType => @packaging_type,
|
164
|
+
:TotalWeight => { :Units => @units, :Value => weight },
|
165
|
+
:PreferredCurrency => @currency,
|
166
|
+
:Shipper => {
|
167
|
+
:Contact => {
|
168
|
+
:PersonName => shipper_contact[:name],
|
169
|
+
:PhoneNumber => shipper_contact[:phone_number]
|
170
|
+
},
|
171
|
+
:Address => {
|
172
|
+
:CountryCode => shipper_address[:country],
|
173
|
+
:StreetLines => shipper_address[:street],
|
174
|
+
:City => shipper_address[:city],
|
175
|
+
:StateOrProvinceCode => shipper_address[:state],
|
176
|
+
:PostalCode => shipper_address[:zip]
|
177
|
+
}
|
178
|
+
},
|
179
|
+
:Recipient => {
|
180
|
+
:Contact => {
|
181
|
+
:PersonName => recipient_contact[:name],
|
182
|
+
:PhoneNumber => recipient_contact[:phone_number]
|
183
|
+
},
|
184
|
+
:Address => {
|
185
|
+
:CountryCode => recipient_address[:country],
|
186
|
+
:StreetLines => recipient_address[:street],
|
187
|
+
:City => recipient_address[:city],
|
188
|
+
:StateOrProvinceCode => recipient_address[:state],
|
189
|
+
:PostalCode => recipient_address[:zip],
|
190
|
+
:Residential => residential
|
191
|
+
}
|
192
|
+
},
|
193
|
+
:ShippingChargesPayment => {
|
194
|
+
:PaymentType => @payment_type,
|
195
|
+
:Payor => {
|
196
|
+
:AccountNumber => @account_number,
|
197
|
+
:CountryCode => shipper_address[:country]
|
198
|
+
}
|
199
|
+
},
|
200
|
+
:LabelSpecification => {
|
201
|
+
:LabelFormatType => @label_type,
|
202
|
+
:ImageType => @label_image_type
|
203
|
+
},
|
204
|
+
:RateRequestTypes => @rate_request_type,
|
205
|
+
:PackageCount => count,
|
206
|
+
:RequestedPackageLineItems => [ {:SequenceNumber=>1, :Weight => {:Units => @units, :Value => weight} } ]
|
207
|
+
}.merge(smart_post_data || {})
|
208
|
+
))
|
209
|
+
|
210
|
+
successful = successful?(result)
|
211
|
+
|
212
|
+
msg = error_msg(result, false)
|
213
|
+
if successful && msg !~ /There are no valid services available/
|
214
|
+
if @use_smart_post
|
215
|
+
charge = 0
|
216
|
+
tracking_number = result.completedShipmentDetail.completedPackageDetails.trackingIds[1].trackingNumber
|
217
|
+
else
|
218
|
+
pre = result.completedShipmentDetail.shipmentRating.shipmentRateDetails
|
219
|
+
charge = ((pre.class == Array ? pre[0].totalNetCharge.amount.to_f : pre.totalNetCharge.amount.to_f) * 100).to_i
|
220
|
+
tracking_number = result.completedShipmentDetail.completedPackageDetails.trackingIds.trackingNumber
|
221
|
+
end
|
222
|
+
label = Base64.decode64(result.completedShipmentDetail.completedPackageDetails.label.parts.image)
|
223
|
+
[charge, label, tracking_number]
|
224
|
+
else
|
225
|
+
raise FedexError.new("Unable to get label from Fedex: #{msg}")
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
# Options that go along with each request
|
231
|
+
def common_options
|
232
|
+
{
|
233
|
+
:WebAuthenticationDetail => {:UserCredential => {:Key => @auth_key, :Password => @security_code}},
|
234
|
+
:ClientDetail => {:AccountNumber => @account_number, :MeterNumber => @meter_number},
|
235
|
+
:Version => WS_VERSION
|
236
|
+
}
|
237
|
+
end
|
238
|
+
|
239
|
+
# Checks the supplied options for a given method or field and throws an exception if anything is missing
|
240
|
+
def check_required_options(option_set_name, options = {})
|
241
|
+
required_options = REQUIRED_OPTIONS[option_set_name]
|
242
|
+
missing = []
|
243
|
+
required_options.each { |option| missing << option if options[option].nil? }
|
244
|
+
|
245
|
+
unless missing.empty?
|
246
|
+
raise MissingInformationError.new("Missing #{missing.collect { |m| ":#{m}" }.join(', ')}")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Creates and returns a driver for the requested action
|
251
|
+
def create_driver(name)
|
252
|
+
path = File.expand_path(@wsdl_path)
|
253
|
+
wsdl = SOAP::WSDLDriverFactory.new(path)
|
254
|
+
driver = wsdl.create_rpc_driver
|
255
|
+
# /s+(1000|0|9c9|fcc)\s+/ => ""
|
256
|
+
driver.wiredump_dev = STDOUT if @debug
|
257
|
+
|
258
|
+
driver
|
259
|
+
end
|
260
|
+
|
261
|
+
# Resolves the ground+residential discrepancy. If a package is shipped via Fedex Ground to an address marked as residential the service type
|
262
|
+
# must be set to ServiceTypes::GROUND_HOME_DELIVERY and not ServiceTypes::FEDEX_GROUND.
|
263
|
+
def resolve_service_type(service_type, residential)
|
264
|
+
if residential && (service_type == ServiceTypes::FEDEX_GROUND)
|
265
|
+
ServiceTypes::GROUND_HOME_DELIVERY
|
266
|
+
else
|
267
|
+
service_type
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Returns a boolean determining whether a request was successful.
|
272
|
+
def successful?(result)
|
273
|
+
if defined?(result.cancelPackageReply)
|
274
|
+
SUCCESSFUL_RESPONSES.any? { |r| r == result.cancelPackageReply.highestSeverity }
|
275
|
+
else
|
276
|
+
SUCCESSFUL_RESPONSES.any? { |r| r == result.highestSeverity }
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# Returns the error message contained in the SOAP response, if one exists.
|
281
|
+
def error_msg(result, return_nothing_if_successful=true)
|
282
|
+
return "" if successful?(result) && return_nothing_if_successful
|
283
|
+
notes = result.notifications
|
284
|
+
notes.respond_to?(:message) ? notes.message : notes.first.message
|
285
|
+
end
|
286
|
+
|
287
|
+
# Attempts to determine the carrier code for a tracking number based upon its length. Currently supports Fedex Ground and Fedex Express
|
288
|
+
def carrier_code_for_tracking_number(tracking_number)
|
289
|
+
case tracking_number.length
|
290
|
+
when 12
|
291
|
+
'FDXE'
|
292
|
+
when 15
|
293
|
+
'FDXG'
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
data/mc-fedex.gemspec
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{mc-fedex}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Edwin & Adam & Eric"]
|
12
|
+
s.date = %q{2011-02-25}
|
13
|
+
s.description = %q{Fedex wrapper to print FedEx labels}
|
14
|
+
s.email = %q{github@modcloth.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"Gemfile",
|
21
|
+
"Gemfile.lock",
|
22
|
+
"LICENSE.txt",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/constants.rb",
|
27
|
+
"lib/mc-fedex.rb",
|
28
|
+
"mc-fedex.gemspec",
|
29
|
+
"spec/mc-fedex_spec.rb",
|
30
|
+
"spec/spec_helper.rb"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://github.com/modcloth/mc-fedex}
|
33
|
+
s.licenses = ["MIT"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = %q{1.3.7}
|
36
|
+
s.summary = %q{Fedex library on ruby}
|
37
|
+
s.test_files = [
|
38
|
+
"spec/mc-fedex_spec.rb",
|
39
|
+
"spec/spec_helper.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<soap4r>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
50
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
|
51
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<ruby-debug>, [">= 0"])
|
53
|
+
s.add_runtime_dependency(%q<soap4r>, [">= 1.5.8"])
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<soap4r>, [">= 0"])
|
56
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
57
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
58
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
59
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
60
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
61
|
+
s.add_dependency(%q<soap4r>, [">= 1.5.8"])
|
62
|
+
end
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<soap4r>, [">= 0"])
|
65
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
66
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
67
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
68
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
69
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
70
|
+
s.add_dependency(%q<soap4r>, [">= 1.5.8"])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe McFedex do
|
4
|
+
|
5
|
+
describe "create a shipping label" do
|
6
|
+
it "should call fedex api and get label" do
|
7
|
+
fedex = McFedex::Base.new(:auth_key => 'AUTH_KEY',
|
8
|
+
:security_code => 'SECURITY_CODE',
|
9
|
+
:account_number => 'ACCOUNT_NUMBER',
|
10
|
+
:meter_number => 'METER_NUMBER',
|
11
|
+
:wsdl => 'WSDL PATH HERE')
|
12
|
+
shipper = {
|
13
|
+
:name => "NAME",
|
14
|
+
:phone_number => 'PHONE_NUMBER'
|
15
|
+
}
|
16
|
+
recipient = {
|
17
|
+
:name => "NAME",
|
18
|
+
:phone_number => 'PHONE_NUMBER'
|
19
|
+
}
|
20
|
+
origin = {
|
21
|
+
:street => ['STREET1', 'STREET2'],
|
22
|
+
:city => 'CITY',
|
23
|
+
:state => 'STATE',
|
24
|
+
:zip => 'ZIP',
|
25
|
+
:country => 'COUNTRY'
|
26
|
+
}
|
27
|
+
destination = {
|
28
|
+
:street => 'STREET',
|
29
|
+
:city => 'CITY',
|
30
|
+
:state => 'STATE',
|
31
|
+
:zip => 'ZIP',
|
32
|
+
:country => 'COUNTRY',
|
33
|
+
:residential => false
|
34
|
+
}
|
35
|
+
pkg_count = 1
|
36
|
+
weight = 10
|
37
|
+
service_type = McFedex::ServiceTypes::FEDEX_GROUND
|
38
|
+
|
39
|
+
price, label, tracking_number = fedex.label(
|
40
|
+
:shipper => {:contact => shipper, :address => origin},
|
41
|
+
:recipient => {:contact => recipient, :address => destination},
|
42
|
+
:count => pkg_count,
|
43
|
+
:weight => weight,
|
44
|
+
:service_type => service_type
|
45
|
+
)
|
46
|
+
|
47
|
+
f = File.new("label.pdf", "w+")
|
48
|
+
f.write(label)
|
49
|
+
f.close
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
require 'mc-fedex'
|
14
|
+
require 'rspec'
|
15
|
+
|
16
|
+
# Requires supporting files with custom matchers and macros, etc,
|
17
|
+
# in ./support/ and its subdirectories.
|
18
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
19
|
+
|
20
|
+
RSpec.configure do |config|
|
21
|
+
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mc-fedex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 0.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Edwin & Adam & Eric
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-25 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
type: :runtime
|
24
|
+
name: soap4r
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
34
|
+
requirement: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
prerelease: false
|
37
|
+
type: :development
|
38
|
+
name: rspec
|
39
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
requirement: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
prerelease: false
|
51
|
+
type: :development
|
52
|
+
name: bundler
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 23
|
59
|
+
segments:
|
60
|
+
- 1
|
61
|
+
- 0
|
62
|
+
- 0
|
63
|
+
version: 1.0.0
|
64
|
+
requirement: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
prerelease: false
|
67
|
+
type: :development
|
68
|
+
name: jeweler
|
69
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 1
|
75
|
+
segments:
|
76
|
+
- 1
|
77
|
+
- 5
|
78
|
+
- 1
|
79
|
+
version: 1.5.1
|
80
|
+
requirement: *id004
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
prerelease: false
|
83
|
+
type: :development
|
84
|
+
name: rcov
|
85
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
requirement: *id005
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
prerelease: false
|
97
|
+
type: :development
|
98
|
+
name: ruby-debug
|
99
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
hash: 3
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
version: "0"
|
108
|
+
requirement: *id006
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
prerelease: false
|
111
|
+
type: :runtime
|
112
|
+
name: soap4r
|
113
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
hash: 19
|
119
|
+
segments:
|
120
|
+
- 1
|
121
|
+
- 5
|
122
|
+
- 8
|
123
|
+
version: 1.5.8
|
124
|
+
requirement: *id007
|
125
|
+
description: Fedex wrapper to print FedEx labels
|
126
|
+
email: github@modcloth.com
|
127
|
+
executables: []
|
128
|
+
|
129
|
+
extensions: []
|
130
|
+
|
131
|
+
extra_rdoc_files:
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.rdoc
|
134
|
+
files:
|
135
|
+
- Gemfile
|
136
|
+
- Gemfile.lock
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.rdoc
|
139
|
+
- Rakefile
|
140
|
+
- VERSION
|
141
|
+
- lib/constants.rb
|
142
|
+
- lib/mc-fedex.rb
|
143
|
+
- mc-fedex.gemspec
|
144
|
+
- spec/mc-fedex_spec.rb
|
145
|
+
- spec/spec_helper.rb
|
146
|
+
has_rdoc: true
|
147
|
+
homepage: http://github.com/modcloth/mc-fedex
|
148
|
+
licenses:
|
149
|
+
- MIT
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
hash: 3
|
161
|
+
segments:
|
162
|
+
- 0
|
163
|
+
version: "0"
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
hash: 3
|
170
|
+
segments:
|
171
|
+
- 0
|
172
|
+
version: "0"
|
173
|
+
requirements: []
|
174
|
+
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 1.3.7
|
177
|
+
signing_key:
|
178
|
+
specification_version: 3
|
179
|
+
summary: Fedex library on ruby
|
180
|
+
test_files:
|
181
|
+
- spec/mc-fedex_spec.rb
|
182
|
+
- spec/spec_helper.rb
|