fedex 1.0.0 → 2.0.1

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.
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ doc/*
8
8
  pkg/*
9
9
  .yardopts
10
10
  fedex_credentials.yml
11
+ spec/vcr
data/Readme.md CHANGED
@@ -2,91 +2,149 @@
2
2
 
3
3
  For more information visit [Fedex Web Services for Shipping](https://www.fedex.com/wpor/web/jsp/drclinks.jsp?links=wss/index.html).
4
4
 
5
- This version uses the Non-SOAP Web Services so there is no need to download the Fedex WSDL files, note however that you will need to apply for
6
- development/production credentials.
5
+ This version uses the Non-SOAP Web Services so there is no need to download the
6
+ Fedex WSDL files, note however that you will need to apply for development/production credentials.
7
7
 
8
8
  Note: This is work in progress make sure to test your results.
9
9
 
10
10
  # Installation:
11
- // Rails 3.x
12
- $ gem 'fedex'
13
-
14
- // Rails 2.x
15
- $ gem install fedex
11
+
12
+ Rails 3.x using Bundler's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'fedex'
16
+ ````
17
+
18
+ Rails 2.x or without Rails or Bundler:
19
+
20
+ ```ruby
21
+ gem install fedex
22
+ ```
16
23
 
17
24
  # Usage example:
18
-
25
+
19
26
  Define the shipper:
20
-
21
- shipper = { :name => "Sender",
22
- :company => "Company",
23
- :phone_number => "555-555-5555",
24
- :address => "Main Street",
25
- :city => "Harrison",
26
- :state => "AR",
27
- :postal_code => "72601",
28
- :country_code => "US" }
29
-
30
- Define the recipient:
31
-
32
- recipient = { :name => "Recipient",
33
- :company => "Company",
34
- :phone_number => "555-555-5555",
35
- :address => "Main Street",
36
- :city => "Franklin Park",
37
- :state => "IL",
38
- :postal_code => "60131",
39
- :country_code => "US",
40
- :residential => "false" }
41
- Define the packages(multiple packages in a single shipment are allowed):
42
- Note that all the Dimensions must be integers
43
-
44
- packages = []
45
- packages << { :weight => {:units => "LB", :value => 2},
46
- :dimensions => {:length => 10, :width => 5, :height => 4, :units => "IN" } }
47
- packages << { :weight => {:units => "LB", :value => 6},
48
- :dimensions => {:length => 5, :width => 5, :height => 4, :units => "IN" } }
49
-
50
- By Default packaging type is "YOUR PACKAGING" and the drop off type is "REGULAR PICKUP", if you need something different you can pass an extra hash for shipping details
51
-
52
- shipping_details = { :packaging_type => "YOUR_PACKAGING", :drop_off_type => "REGULAR_PICKUP" }
53
-
54
-
55
- Create a Fedex::Shipment object using your FedEx credentials; mode should be either production or development depending on what Fedex environment you want to use.
56
-
57
- require 'fedex'
58
- fedex = Fedex::Shipment.new(:key => 'xxx',
59
- :password => 'xxxx',
60
- :account_number => 'xxxx',
61
- :meter => 'xxx',
62
- :mode=>'production')
63
-
64
- rate = fedex.rate({:shipper=>shipper, :recipient => recipient, :packages => packages, :service_type => "FEDEX_GROUND", :shipping_details => shipping_details})
65
-
66
- Fedex provides multiple total values; total_net_charge is the final amount you are looking for.
67
-
68
- $ rate.total_net_charge => "34.03"
69
- # Complete response
70
- $ <Fedex::Rate:0x1019ba5f8
71
- @total_net_charge="34.03",
72
- @total_surcharges="1.93",
73
- @total_billing_weight="8.0 LB",
74
- @total_taxes="0.0",
75
- @rate_type="PAYOR_ACCOUNT_PACKAGE",
76
- @total_base_charge="32.1",
77
- @total_freight_discounts=nil,
78
- @total_net_freight="32.1",
79
- @rate_zone="51">
80
-
81
-
27
+
28
+ ```ruby
29
+ shipper = { :name => "Sender",
30
+ :company => "Company",
31
+ :phone_number => "555-555-5555",
32
+ :address => "Main Street",
33
+ :city => "Harrison",
34
+ :state => "AR",
35
+ :postal_code => "72601",
36
+ :country_code => "US" }
37
+ ```
38
+
39
+ Define the recipient:
40
+
41
+ ```ruby
42
+ recipient = { :name => "Recipient",
43
+ :company => "Company",
44
+ :phone_number => "555-555-5555",
45
+ :address => "Main Street",
46
+ :city => "Franklin Park",
47
+ :state => "IL",
48
+ :postal_code => "60131",
49
+ :country_code => "US",
50
+ :residential => "false" }
51
+ ```
52
+
53
+ Define the packages; multiple packages in a single shipment are allowed:
54
+ Note that all the dimensions must be integers.
55
+
56
+ ```ruby
57
+ packages = []
58
+ packages << {
59
+ :weight => {:units => "LB", :value => 2},
60
+ :dimensions => {:length => 10, :width => 5, :height => 4, :units => "IN" }
61
+ }
62
+ packages << {
63
+ :weight => {:units => "LB", :value => 6},
64
+ :dimensions => {:length => 5, :width => 5, :height => 4, :units => "IN" }
65
+ }
66
+ ```
67
+
68
+ By default packaging type is "YOUR PACKAGING" and the drop off type is "REGULAR PICKUP".
69
+ If you need something different you can pass an extra hash for shipping details
70
+
71
+ ```ruby
72
+ shipping_details = {
73
+ :packaging_type => "YOUR_PACKAGING",
74
+ :drop_off_type => "REGULAR_PICKUP"
75
+ }
76
+ ```
77
+
78
+ Create a `Fedex::Shipment` object using your FedEx credentials; mode should be
79
+ either production or development depending on what Fedex environment you want to use.
80
+
81
+ ```ruby
82
+ require 'fedex'
83
+ fedex = Fedex::Shipment.new(:key => 'xxx',
84
+ :password => 'xxxx',
85
+ :account_number => 'xxxx',
86
+ :meter => 'xxx',
87
+ :mode => 'production')
88
+ ```
89
+
90
+ ### ** Getting Shipping Rates **
91
+
92
+ To find a shipping rate:
93
+
94
+ ```ruby
95
+ rate = fedex.rate(:shipper=>shipper,
96
+ :recipient => recipient,
97
+ :packages => packages,
98
+ :service_type => "FEDEX_GROUND",
99
+ :shipping_details => shipping_details)
100
+ ```
101
+
102
+ Fedex provides multiple total values; `total_net_charge` is the final amount you are looking for.
103
+
104
+ ```ruby
105
+ $ rate.total_net_charge => "34.03"
106
+ # Complete response
107
+ $ <Fedex::Rate:0x1019ba5f8
108
+ @total_net_charge="34.03",
109
+ @total_surcharges="1.93",
110
+ @total_billing_weight="8.0 LB",
111
+ @total_taxes="0.0",
112
+ @rate_type="PAYOR_ACCOUNT_PACKAGE",
113
+ @total_base_charge="32.1",
114
+ @total_freight_discounts=nil,
115
+ @total_net_freight="32.1",
116
+ @rate_zone="51">
117
+ ```
118
+
119
+ ### ** Generate a shipping label(PDF) **
120
+
121
+ To create a label for a shipment:
122
+
123
+ ```ruby
124
+ label = fedex.label(:filename => "my_dir/example.pdf",
125
+ :shipper=>shipper,
126
+ :recipient => recipient,
127
+ :packages => packages,
128
+ :service_type => "FEDEX_GROUND",
129
+ :shipping_details => shipping_details)
130
+ ```
131
+
132
+ The label will be saved to the file system as the filename you specify and is Adobe PDF format.
133
+ Note that you can currently print a label for a single package at a time.
134
+
82
135
  # Services/Options Available
83
136
 
84
- Fedex::Shipment::SERVICE_TYPES
85
- Fedex::Shipment::PACKAGING_TYPES
86
- Fedex::Shipment::DROP_OFF_TYPES
137
+ ```ruby
138
+ Fedex::Shipment::SERVICE_TYPES
139
+ Fedex::Shipment::PACKAGING_TYPES
140
+ Fedex::Shipment::DROP_OFF_TYPES
141
+ ````
87
142
 
88
- # Copyright/License:
89
- Copyright 2011 Jazmin Schroeder
143
+ # Contributors:
144
+ - [jazminschroeder](http://github.com/jazminschroeder) (Jazmin Schroeder)
145
+ - [parndt](https://github.com/parndt) (Philip Arndt)
90
146
 
91
- This gem is made available under the MIT license
147
+ # Copyright/License:
148
+ Copyright 2011 [Jazmin Schroeder](http://jazminschroeder.com)
92
149
 
150
+ This gem is made available under the MIT license.
data/fedex.gemspec CHANGED
@@ -13,9 +13,13 @@ Gem::Specification.new do |s|
13
13
  s.description = %q{Ruby Library to use Fedex Web Services(version 10)}
14
14
 
15
15
  s.rubyforge_project = "fedex"
16
- s.add_development_dependency "rspec", '~> 2.6.0'
16
+
17
17
  s.add_dependency 'httparty', '~> 0.8.0'
18
18
  s.add_dependency 'nokogiri', '~> 1.5.0'
19
+
20
+ s.add_development_dependency "rspec", '~> 2.9.0'
21
+ s.add_development_dependency 'vcr', '~> 2.0.0'
22
+ s.add_development_dependency 'fakeweb'
19
23
  # s.add_runtime_dependency "rest-client"
20
24
 
21
25
  s.files = `git ls-files`.split("\n")
@@ -23,5 +27,5 @@ Gem::Specification.new do |s|
23
27
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
28
  s.require_paths = ["lib"]
25
29
 
26
-
30
+
27
31
  end
data/lib/fedex.rb CHANGED
@@ -1,54 +1,55 @@
1
- require "fedex/shipment"
2
- require "fedex/rate"
1
+ require 'fedex/shipment'
2
+
3
3
  # Get shipping rates trough Fedex Web Services
4
- #
4
+ #
5
5
  # In order to use the API you will need to apply for developer/production credentials,
6
6
  # Visit {http://www.fedex.com/us/developer/ Fedex Developer Center} for more information about how to obtain your keys.
7
7
  #
8
8
  # ===Usage example
9
9
  # #Use your own Fedex Keys
10
- # fedex = Fedex::Shipment.new(:key => 'xxx',
11
- # :password => 'xxxx',
12
- # :account_number => 'xxxx',
13
- # :meter => 'xxx',
10
+ # fedex = Fedex::Shipment.new(:key => 'xxx',
11
+ # :password => 'xxxx',
12
+ # :account_number => 'xxxx',
13
+ # :meter => 'xxx',
14
14
  # :mode=>['production'|'development'])
15
- # shipper = {:name => "Sender",
16
- # :company => "Company",
17
- # :phone_number => "555-555-5555",
18
- # :address => "Main Street",
19
- # :city => "Harrison",
20
- # :state => "AR",
21
- # :postal_code => "72601",
15
+ # shipper = {:name => "Sender",
16
+ # :company => "Company",
17
+ # :phone_number => "555-555-5555",
18
+ # :address => "Main Street",
19
+ # :city => "Harrison",
20
+ # :state => "AR",
21
+ # :postal_code => "72601",
22
22
  # :country_code => "US" }
23
23
  #
24
- # recipient = { :name => "Recipient",
25
- # :company => "Company",
26
- # :phone_number => "555-555-5555",
27
- # :address => "Main Street",
28
- # :city => "City",
29
- # :state => "ST",
30
- # :postal_code => "55555",
31
- # :country_code => "US",
24
+ # recipient = { :name => "Recipient",
25
+ # :company => "Company",
26
+ # :phone_number => "555-555-5555",
27
+ # :address => "Main Street",
28
+ # :city => "City",
29
+ # :state => "ST",
30
+ # :postal_code => "55555",
31
+ # :country_code => "US",
32
32
  # :residential => "false" }
33
33
  # packages = []
34
- # packages << { :weight => {:units => "LB", :value => 2},
34
+ # packages << { :weight => {:units => "LB", :value => 2},
35
35
  # :dimensions => {:length => 10, :width => 5, :height => 4, :units => "IN" } }
36
- # packages << { :weight => {:units => "LB", :value => 6},
36
+ # packages << { :weight => {:units => "LB", :value => 6},
37
37
  # :dimensions => {:length => 5, :width => 5, :height => 4, :units => "IN" } }
38
38
  # # "YOUR PACKAGING" and "REGULAR PICKUP" are the default options for all shipments but you can easily change them by passing an extra hash for # shipping_options
39
- # shipping_options = { :packaging_type => "YOUR_PACKAGING", :drop_off_type => "REGULAR_PICKUP" }
39
+ # shipping_options = { :packaging_type => "YOUR_PACKAGING", :drop_off_type => "REGULAR_PICKUP" }
40
40
  # rate = fedex.rate({:shipper=>shipper, :recipient => recipient, :packages => packages, :service_type => "FEDEX_GROUND", :shipping_options => #shipping_options})
41
- #
42
- # $ <Fedex::Rate:0x1019ba5f8 @total_net_charge="34.03",
43
- # @total_surcharges="1.93",
44
- # @total_billing_weight="8.0 LB",
45
- # @total_taxes="0.0",
46
- # @rate_type="PAYOR_ACCOUNT_PACKAGE",
47
- # @total_base_charge="32.1",
48
- # @total_freight_discounts=nil,
49
- # @total_net_freight="32.1",
41
+ #
42
+ # $ <Fedex::Rate:0x1019ba5f8 @total_net_charge="34.03",
43
+ # @total_surcharges="1.93",
44
+ # @total_billing_weight="8.0 LB",
45
+ # @total_taxes="0.0",
46
+ # @rate_type="PAYOR_ACCOUNT_PACKAGE",
47
+ # @total_base_charge="32.1",
48
+ # @total_freight_discounts=nil,
49
+ # @total_net_freight="32.1",
50
50
  # @rate_zone="51">
51
51
  module Fedex
52
+ require 'fedex/version'
52
53
  #Exceptions: Fedex::RateError
53
54
  class RateError < StandardError; end
54
55
  end
@@ -0,0 +1,26 @@
1
+ require 'fedex/helpers'
2
+
3
+ module Fedex
4
+ class Credentials
5
+ include Helpers
6
+ attr_reader :key, :password, :account_number, :meter, :mode
7
+
8
+ # In order to use Fedex rates API you must first apply for a developer(and later production keys),
9
+ # Visit {http://www.fedex.com/us/developer/ Fedex Developer Center} for more information about how to obtain your keys.
10
+ # @param [String] key - Fedex web service key
11
+ # @param [String] password - Fedex password
12
+ # @param [String] account_number - Fedex account_number
13
+ # @param [String] meter - Fedex meter number
14
+ # @param [String] mode - [development/production]
15
+ #
16
+ # return a Fedex::Credentials object
17
+ def initialize(options={})
18
+ requires!(options, :key, :password, :account_number, :meter, :mode)
19
+ @key = options[:key]
20
+ @password = options[:password]
21
+ @account_number = options[:account_number]
22
+ @meter = options[:meter]
23
+ @mode = options[:mode]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module Fedex
2
+ module Helpers
3
+
4
+ private
5
+ # String to CamelCase
6
+ def camelize(str)
7
+ str.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
8
+ end
9
+
10
+ # Helper method to validate required fields
11
+ def requires!(hash, *params)
12
+ params.each { |param| raise RateError, "Missing Required Parameter #{param}" if hash[param].nil? }
13
+ end
14
+
15
+ def underscorize(key) #:nodoc:
16
+ key.to_s.sub(/^(v[0-9]+|ns):/, "").gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module Fedex
2
+ class Label
3
+ attr_accessor :options
4
+
5
+ # Initialize Fedex::Label Object
6
+ # @param [Hash] options
7
+ def initialize(options = {})
8
+ @options = options
9
+ end
10
+ end
11
+ end
data/lib/fedex/rate.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  module Fedex
2
2
  # Visit {http://www.fedex.com/us/developer/ Fedex Developer Center} for a complete list of values returned from the API
3
3
  #
4
- # Rate totals are contained in the node
4
+ # Rate totals are contained in the node
5
5
  # response[:rate_reply][:rate_reply_details][:rated_shipment_details]
6
6
  class Rate
7
- # Initialize Fedex::Rate Object
8
- # @param [Hash] options
7
+ # Initialize Fedex::Rate Object
8
+ # @param [Hash] options
9
9
  #
10
10
  #
11
- # return [Fedex::Rate Object]
11
+ # return [Fedex::Rate Object]
12
12
  # @rate_type #Type used for this specific set of rate data
13
13
  # @rate_zone #Indicates the rate zone used(based on origin and destination)
14
14
  # @total_billing_weight #The weight used to calculate these rates
@@ -29,8 +29,8 @@ module Fedex
29
29
  @total_net_freight = options[:total_net_freight][:amount]
30
30
  @total_surcharges = options[:total_surcharges][:amount]
31
31
  @total_base_charge = options[:total_base_charge][:amount]
32
- @total_net_fedex_charge = (options[:total_net_fe_dex_charge]||={})[:amount]
33
- @total_rebates = (options[:total_rebates]||={})[:amount]
32
+ @total_net_fedex_charge = (options[:total_net_fe_dex_charge]||{})[:amount]
33
+ @total_rebates = (options[:total_rebates]||{})[:amount]
34
34
  end
35
35
  end
36
36
  end
@@ -0,0 +1,251 @@
1
+ require 'httparty'
2
+ require 'nokogiri'
3
+ require 'fedex/helpers'
4
+ require 'fedex/rate'
5
+
6
+ module Fedex
7
+ module Request
8
+ class Base
9
+ include Helpers
10
+ include HTTParty
11
+ format :xml
12
+ # If true the rate method will return the complete response from the Fedex Web Service
13
+ attr_accessor :debug
14
+ # Fedex Text URL
15
+ TEST_URL = "https://gatewaybeta.fedex.com:443/xml/"
16
+
17
+ # Fedex Production URL
18
+ PRODUCTION_URL = "https://gateway.fedex.com:443/xml/"
19
+
20
+ # Fedex Version number for the Fedex service used
21
+ VERSION = 10
22
+
23
+ # List of available Service Types
24
+ SERVICE_TYPES = %w(EUROPE_FIRST_INTERNATIONAL_PRIORITY FEDEX_1_DAY_FREIGHT FEDEX_2_DAY FEDEX_2_DAY_AM FEDEX_2_DAY_FREIGHT FEDEX_3_DAY_FREIGHT FEDEX_EXPRESS_SAVER FEDEX_FIRST_FREIGHT FEDEX_FREIGHT_ECONOMY FEDEX_FREIGHT_PRIORITY FEDEX_GROUND FIRST_OVERNIGHT GROUND_HOME_DELIVERY INTERNATIONAL_ECONOMY INTERNATIONAL_ECONOMY_FREIGHT INTERNATIONAL_FIRST INTERNATIONAL_PRIORITY INTERNATIONAL_PRIORITY_FREIGHT PRIORITY_OVERNIGHT SMART_POST STANDARD_OVERNIGHT)
25
+
26
+ # List of available Packaging Type
27
+ PACKAGING_TYPES = %w(FEDEX_10KG_BOX FEDEX_25KG_BOX FEDEX_BOX FEDEX_ENVELOPE FEDEX_PAK FEDEX_TUBE YOUR_PACKAGING)
28
+
29
+ # List of available DropOffTypes
30
+ DROP_OFF_TYPES = %w(BUSINESS_SERVICE_CENTER DROP_BOX REGULAR_PICKUP REQUEST_COURIER STATION)
31
+
32
+ # Clearance Brokerage Type
33
+ CLEARANCE_BROKERAGE_TYPE = %w(BROKER_INCLUSIVE BROKER_INCLUSIVE_NON_RESIDENT_IMPORTER BROKER_SELECT BROKER_SELECT_NON_RESIDENT_IMPORTER BROKER_UNASSIGNED)
34
+
35
+ # Recipient Custom ID Type
36
+ RECIPIENT_CUSTOM_ID_TYPE = %w(COMPANY INDIVIDUAL PASSPORT)
37
+
38
+ # In order to use Fedex rates API you must first apply for a developer(and later production keys),
39
+ # Visit {http://www.fedex.com/us/developer/ Fedex Developer Center} for more information about how to obtain your keys.
40
+ # @param [String] key - Fedex web service key
41
+ # @param [String] password - Fedex password
42
+ # @param [String] account_number - Fedex account_number
43
+ # @param [String] meter - Fedex meter number
44
+ # @param [String] mode - [development/production]
45
+ #
46
+ # return a Fedex::Request::Base object
47
+ def initialize(credentials, options={})
48
+ requires!(options, :shipper, :recipient, :packages, :service_type)
49
+ @credentials = credentials
50
+ @shipper, @recipient, @packages, @service_type, @customs_clearance, @debug = options[:shipper], options[:recipient], options[:packages], options[:service_type], options[:customs_clearance], options[:debug]
51
+ @shipping_options = options[:shipping_options] ||={}
52
+ end
53
+
54
+ # Sends post request to Fedex web service and parse the response.
55
+ # Implemented by each subclass
56
+ def process_request
57
+ raise NotImplementedError, "Override process_request in subclass"
58
+ end
59
+
60
+ private
61
+ # Add web authentication detail information(key and password) to xml request
62
+ def add_web_authentication_detail(xml)
63
+ xml.WebAuthenticationDetail{
64
+ xml.UserCredential{
65
+ xml.Key @credentials.key
66
+ xml.Password @credentials.password
67
+ }
68
+ }
69
+ end
70
+
71
+ # Add Client Detail information(account_number and meter_number) to xml request
72
+ def add_client_detail(xml)
73
+ xml.ClientDetail{
74
+ xml.AccountNumber @credentials.account_number
75
+ xml.MeterNumber @credentials.meter
76
+ }
77
+ end
78
+
79
+ # Add Version to xml request, using the latest version V10 Sept/2011
80
+ def add_version(xml)
81
+ xml.Version{
82
+ xml.ServiceId service_id
83
+ xml.Major VERSION
84
+ xml.Intermediate 0
85
+ xml.Minor 0
86
+ }
87
+ end
88
+
89
+ # Add information for shipments
90
+ def add_requested_shipment(xml)
91
+ xml.RequestedShipment{
92
+ xml.DropoffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
93
+ xml.ServiceType service_type
94
+ xml.PackagingType @shipping_options[:packaging_type] ||= "YOUR_PACKAGING"
95
+ add_shipper(xml)
96
+ add_recipient(xml)
97
+ add_shipping_charges_payment(xml)
98
+ add_customs_clearance(xml) if @customs_clearance
99
+ xml.RateRequestTypes "ACCOUNT"
100
+ add_packages(xml)
101
+ }
102
+ end
103
+
104
+ # Add shipper to xml request
105
+ def add_shipper(xml)
106
+ xml.Shipper{
107
+ xml.Contact{
108
+ xml.PersonName @shipper[:name]
109
+ xml.CompanyName @shipper[:company]
110
+ xml.PhoneNumber @shipper[:phone_number]
111
+ }
112
+ xml.Address {
113
+ Array(@shipper[:address]).take(2).each do |address_line|
114
+ xml.StreetLines address_line
115
+ end
116
+ xml.City @shipper[:city]
117
+ xml.StateOrProvinceCode @shipper[:state]
118
+ xml.PostalCode @shipper[:postal_code]
119
+ xml.CountryCode @shipper[:country_code]
120
+ }
121
+ }
122
+ end
123
+
124
+ # Add recipient to xml request
125
+ def add_recipient(xml)
126
+ xml.Recipient{
127
+ xml.Contact{
128
+ xml.PersonName @recipient[:name]
129
+ xml.CompanyName @recipient[:company]
130
+ xml.PhoneNumber @recipient[:phone_number]
131
+ }
132
+ xml.Address {
133
+ Array(@recipient[:address]).take(2).each do |address_line|
134
+ xml.StreetLines address_line
135
+ end
136
+ xml.City @recipient[:city]
137
+ xml.StateOrProvinceCode @recipient[:state]
138
+ xml.PostalCode @recipient[:postal_code]
139
+ xml.CountryCode @recipient[:country_code]
140
+ xml.Residential @recipient[:residential]
141
+ }
142
+ }
143
+ end
144
+
145
+ # Add shipping charges to xml request
146
+ def add_shipping_charges_payment(xml)
147
+ xml.ShippingChargesPayment{
148
+ xml.PaymentType "SENDER"
149
+ xml.Payor{
150
+ xml.AccountNumber @credentials.account_number
151
+ xml.CountryCode @shipper[:country_code]
152
+ }
153
+ }
154
+ end
155
+
156
+ # Add packages to xml request
157
+ def add_packages(xml)
158
+ package_count = @packages.size
159
+ xml.PackageCount package_count
160
+ @packages.each do |package|
161
+ xml.RequestedPackageLineItems{
162
+ xml.GroupPackageCount 1
163
+ xml.Weight{
164
+ xml.Units package[:weight][:units]
165
+ xml.Value package[:weight][:value]
166
+ }
167
+ xml.Dimensions{
168
+ xml.Length package[:dimensions][:length]
169
+ xml.Width package[:dimensions][:width]
170
+ xml.Height package[:dimensions][:height]
171
+ xml.Units package[:dimensions][:units]
172
+ }
173
+ }
174
+ end
175
+ end
176
+
177
+ # Add customs clearance(for international shipments)
178
+ def add_customs_clearance(xml)
179
+ xml.CustomsClearanceDetail{
180
+ customs_to_xml(xml, @customs_clearance)
181
+ }
182
+ end
183
+
184
+ # Fedex Web Service Api
185
+ def api_url
186
+ @credentials.mode == "production" ? PRODUCTION_URL : TEST_URL
187
+ end
188
+
189
+ # Build xml Fedex Web Service request
190
+ # Implemented by each subclass
191
+ def build_xml
192
+ raise NotImplementedError, "Override build_xml in subclass"
193
+ end
194
+
195
+ # Build nodes dinamically from the provided customs clearance hash
196
+ def customs_to_xml(xml, hash)
197
+ hash.each do |key, value|
198
+ if value.is_a?(Hash)
199
+ xml.send "#{camelize(key.to_s)}" do |x|
200
+ customs_to_xml(x, value)
201
+ end
202
+ elsif value.is_a?(Array)
203
+ node = key
204
+ value.each do |v|
205
+ xml.send "#{camelize(node.to_s)}" do |x|
206
+ customs_to_xml(x, v)
207
+ end
208
+ end
209
+ else
210
+ xml.send "#{camelize(key.to_s)}", value unless key.is_a?(Hash)
211
+ end
212
+ end
213
+ end
214
+
215
+ # Parse response, convert keys to underscore symbols
216
+ def parse_response(response)
217
+ response = sanitize_response_keys(response)
218
+ end
219
+
220
+ # Recursively sanitizes the response object by clenaing up any hash keys.
221
+ def sanitize_response_keys(response)
222
+ if response.is_a?(Hash)
223
+ response.inject({}) { |result, (key, value)| result[underscorize(key).to_sym] = sanitize_response_keys(value); result }
224
+ elsif response.is_a?(Array)
225
+ response.collect { |result| sanitize_response_keys(result) }
226
+ else
227
+ response
228
+ end
229
+ end
230
+
231
+ def service_id
232
+ 'crs'
233
+ end
234
+
235
+ # Use GROUND_HOME_DELIVERY for shipments going to a residential address within the US.
236
+ def service_type
237
+ if @recipient[:residential].to_s =~ /true/i and @service_type =~ /GROUND/i and @recipient[:country_code] =~ /US/i
238
+ "GROUND_HOME_DELIVERY"
239
+ else
240
+ @service_type
241
+ end
242
+ end
243
+
244
+ # Successful request
245
+ def success?(response)
246
+ (!response[:rate_reply].nil? and %w{SUCCESS WARNING NOTE}.include? response[:rate_reply][:highest_severity])
247
+ end
248
+
249
+ end
250
+ end
251
+ end