fedex 3.6.1 → 3.8.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.
data/Readme.md CHANGED
@@ -70,6 +70,21 @@ shipping_options = {
70
70
  }
71
71
  ```
72
72
 
73
+ If you pass a non-nil `:return_reason` as part of the shipping options, you will create
74
+ a return shipment. The request to fedex will include the following additional XML.
75
+
76
+ ```xml
77
+ <SpecialServicesRequested>
78
+ <SpecialServiceTypes>RETURN_SHIPMENT</SpecialServiceTypes>
79
+ <ReturnShipmentDetail>
80
+ <ReturnType>PRINT_RETURN_LABEL</ReturnType>
81
+ <Rma>
82
+ <Reason>YOUR RETURN REASON HERE</Reason>
83
+ </Rma>
84
+ </ReturnShipmentDetail>
85
+ </SpecialServicesRequested>
86
+ ```
87
+
73
88
  By default the shipping charges will be assigned to the sender. If you may
74
89
  change this by passing an extra hash of payment options.
75
90
 
@@ -131,7 +146,7 @@ ship = fedex.ship(:shipper=>shipper,
131
146
  :packages => packages,
132
147
  :service_type => "FEDEX_GROUND",
133
148
  :shipping_options => shipping_options)
134
- puts ship[:completed_shipment_detail][:operational_detail] [:transit_time]
149
+ puts ship[:completed_shipment_detail][:operational_detail][:transit_time]
135
150
  ```
136
151
  Above code will give you the transit time.
137
152
 
@@ -189,6 +204,97 @@ shipment.save!
189
204
 
190
205
  Documentation for setting up Paperclip with Amazon S3 can be found in the Paperclip [README](https://github.com/thoughtbot/paperclip/#storage).
191
206
 
207
+ ### ** Generate shipping labels for multi-package shipments (MPS) **
208
+
209
+ Multiple packages for a single pick-up, destination and payer can be combined into a single MPS shipment. The first label will provide a master tracking number which must be used in the subsequent calls for the remaining packages in the shipment.
210
+
211
+ Parameters for the first label:
212
+ ```ruby
213
+ label = fedex.label(
214
+ :filename => file_name,
215
+ :shipper => shipper,
216
+ :recipient => recipient,
217
+ :packages => packages,
218
+ :service_type => service_type,
219
+ :shipping_details => shipping_details,
220
+ :shipping_charges_payment => shipping_charges_payment,
221
+ :customs_clearance_detail => customs_clearance_detail,
222
+ :mps => {:package_count => package_count, :total_weight => total_weight, :sequence_number => '1'}
223
+ )
224
+ ```
225
+
226
+ Parameters for labels 2 through 'n':
227
+ ```ruby
228
+ fedex.label(
229
+ :filename => file_name,
230
+ :shipper => shipper,
231
+ :recipient => recipient,
232
+ :packages => packages,
233
+ :service_type => service_type,
234
+ :shipping_details => shipping_details,
235
+ :shipping_charges_payment => shipping_charges_payment,
236
+ :customs_clearance_detail => customs_clearance_detail,
237
+ :mps => {
238
+ :master_tracking_id => {:tracking_id_type => 'FEDEX', :tracking_number =>tracking_number},
239
+ :package_count => package_count,
240
+ :total_weight => {
241
+ :value => total_weight,
242
+ :units => 'KG'
243
+ }
244
+ :sequence_number => 'n'
245
+ }
246
+ )
247
+ ```
248
+
249
+ ### ** Create COD Shipment **
250
+
251
+ To create a Cash On Delivery label for a shipment:
252
+
253
+ change "commerical_invoice = {:purpose => 'SOLD'}" in customs_clearance_detail
254
+
255
+ add shipping_options with {:cod => {:currency => "currency", :amount => "amount", :collection_type => 'PAYMENT COLLECTION TYPE'}
256
+
257
+ PAYMENT COLLECTION TYPE - CASH, CHEQUE, DEMAND DRAFT
258
+
259
+ ### ** To add multiple commodities in customs_clearance_detail
260
+
261
+ use this format commodities_1 .... commodities_N
262
+
263
+ example
264
+
265
+ ```
266
+
267
+ customs_clearance_detail['commodites_1']
268
+ customs_clearance_detail['commodites_2']
269
+
270
+ ```
271
+
272
+ ### ** Masking shipper details in label **
273
+
274
+ this allows you hide shipper details on the label
275
+
276
+ Add customer_specified_detail = {:masked_data_1 => 'SOMETHING', :masked_data_2 => 'SOMETHING'} in :label_specification key
277
+
278
+ Example
279
+
280
+ ```
281
+ customer_specified_detail = {
282
+ :masked_data_1 => "SHIPPER_ACCOUNT_NUMBER",
283
+ :masked_data_2 => "TRANSPORTATION_CHARGES_PAYOR_ACCOUNT_NUMBER",
284
+ :masked_data_3 => "DUTIES_AND_TAXES_PAYOR_ACCOUNT_NUMBER"
285
+ }
286
+
287
+ ```
288
+
289
+ ### ** Delete a shipment **
290
+
291
+ If you do not intend to use a label you should delete it. This will notify FedEx that you will not be using the label and they won't charge you.
292
+
293
+ To delete a shipment:
294
+
295
+ ```ruby
296
+ fedex.delete(:tracking_number => "1234567890123")
297
+ ```
192
298
 
193
299
  ### ** Tracking a shipment **
194
300
 
@@ -219,7 +325,7 @@ To verify an address is valid and deliverable:
219
325
  ```ruby
220
326
 
221
327
  address = {
222
- :address => "5 Elm Street",
328
+ :street => "5 Elm Street",
223
329
  :city => "Norwalk",
224
330
  :state => "CT",
225
331
  :postal_code => "06850",
@@ -238,12 +344,58 @@ address_result.postal_code
238
344
  # => "06850-3901"
239
345
  ```
240
346
 
347
+ ### ** Requesting a Pickup **
348
+
349
+ To request a pickup:
350
+
351
+ ```ruby
352
+
353
+ pickup = fedex.pickup(:carrier_code => 'FDXE',
354
+ :packages => {:weight => {:units => "LB", :value => 10}, :count => 2},
355
+ :ready_timestamp => Date.today.to_datetime + 1.375,
356
+ :close_time => Date.today.to_time + 60 * 60 * 17)
357
+ puts pickup[:pickup_confirmation_number]
358
+ ```
359
+
360
+ ### ** Getting pickup availability details **
361
+
362
+ To check for pickup availability:
363
+
364
+ ```ruby
365
+
366
+ dispatch = Date.tomorrow.strftime('%Y-%m-%d')
367
+
368
+ pickup_availability = fedex.pickup_availability(:country_code => 'IN',
369
+ :postal_code => '400061',
370
+ :request_type => 'FUTURE_DAY',
371
+ :dispatch_date => dispatch_date,
372
+ :carrier_code => 'FDXE')
373
+
374
+ puts pickup_availability[:options]
375
+ ```
376
+
377
+ ### ** Getting service availability **
378
+
379
+ To check service availability:
380
+
381
+ ```ruby
382
+
383
+ origin = {:postal_code => '400012', :country_code => 'IN'}
384
+ destination = { :postal_code => '400020', :country_code => 'IN'}
385
+ fedex_service_hash = {:origin => origin, :destination => destination, :ship_date => '2014-06-28', :carrier_code => 'FDXE'}
386
+
387
+ service = fedex.service_availability(fedex_service_hash)
388
+
389
+ puts service[:options]
390
+ ```
391
+
241
392
  # Services/Options Available
242
393
 
243
394
  ```ruby
244
395
  Fedex::Shipment::SERVICE_TYPES
245
396
  Fedex::Shipment::PACKAGING_TYPES
246
397
  Fedex::Shipment::DROP_OFF_TYPES
398
+ Fedex::Shipment::CARRIER_CODES
247
399
  ````
248
400
 
249
401
  # Contributors:
@@ -259,6 +411,7 @@ Fedex::Shipment::DROP_OFF_TYPES
259
411
  - [yevgenko] (https://github.com/yevgenko) (Yevgeniy Viktorov)
260
412
  - [smartacus] (https://github.com/smartacus) (Michael Lippold)
261
413
  - [jonathandean] (https://github.com/jonathandean) (Jonathan Dean)
414
+ - [chirag7jain] (https://github.com/chirag7jain) (Chirag Jain)
262
415
  - and more... (https://github.com/jazminschroeder/fedex/graphs/contributors)
263
416
 
264
417
  # Copyright/License:
data/fedex.gemspec CHANGED
@@ -16,14 +16,14 @@ Gem::Specification.new do |s|
16
16
 
17
17
  s.license = 'MIT'
18
18
 
19
- s.add_dependency 'httparty', '~> 0.13.0'
20
- s.add_dependency 'nokogiri', '~> 1.6.0'
19
+ s.add_dependency 'httparty', '>= 0.8.3'
20
+ s.add_dependency 'nokogiri', '>= 1.5.6'
21
21
 
22
- s.add_development_dependency "rspec", '~> 2.9.0'
22
+ s.add_development_dependency "rspec", '~> 3.0.0'
23
23
  s.add_development_dependency 'vcr', '~> 2.0.0'
24
24
  s.add_development_dependency 'webmock', '~> 1.8.0'
25
25
  s.add_development_dependency 'pry'
26
- # s.add_runtime_dependency "rest-client"
26
+ s.add_development_dependency 'rake'
27
27
 
28
28
  s.files = `git ls-files`.split("\n")
29
29
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -0,0 +1,25 @@
1
+ require 'base64'
2
+ require 'pathname'
3
+
4
+ module Fedex
5
+ class GroundManifest
6
+ attr_reader :manifest_data, :filename
7
+
8
+ # Initialize Fedex::GroundManifest Object
9
+ # @param [Hash] options
10
+ def initialize(options = {})
11
+ puts options
12
+ @filename = options[:filename]
13
+ @manifest_data = Base64.decode64(options[:manifest][:file])
14
+ save
15
+ end
16
+
17
+ def save
18
+ return if manifest_data.nil? || filename.nil?
19
+ full_path = Pathname.new(filename)
20
+ File.open(full_path, 'wb') do |f|
21
+ f.write(manifest_data)
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/fedex/label.rb CHANGED
@@ -7,14 +7,19 @@ module Fedex
7
7
 
8
8
  # Initialize Fedex::Label Object
9
9
  # @param [Hash] options
10
- def initialize(label_details = {})
11
- @response_details = label_details[:process_shipment_reply]
12
- package_details = label_details[:process_shipment_reply][:completed_shipment_detail][:completed_package_details]
13
- @options = package_details[:label]
10
+ def initialize(label_details = {}, associated_shipments = false)
11
+ if associated_shipments
12
+ package_details = label_details
13
+ @options = package_details[:label]
14
+ @options[:tracking_number] = package_details[:tracking_id]
15
+ else
16
+ @response_details = label_details[:process_shipment_reply]
17
+ package_details = label_details[:process_shipment_reply][:completed_shipment_detail][:completed_package_details]
18
+ @options = package_details[:label]
19
+ @options[:tracking_number] = package_details[:tracking_ids][:tracking_number]
20
+ end
14
21
  @options[:format] = label_details[:format]
15
- @options[:tracking_number] = package_details[:tracking_ids][:tracking_number]
16
22
  @options[:file_name] = label_details[:file_name]
17
-
18
23
  @image = Base64.decode64(options[:parts][:image]) if has_image?
19
24
 
20
25
  if file_name = @options[:file_name]
@@ -52,5 +57,15 @@ module Fedex
52
57
  f.write(@image)
53
58
  end
54
59
  end
60
+
61
+ def associated_shipments
62
+ if (label_details = @response_details[:completed_shipment_detail][:associated_shipments])
63
+ label_details[:format] = format
64
+ label_details[:file_name] = file_name
65
+ Label.new(label_details, true)
66
+ else
67
+ nil
68
+ end
69
+ end
55
70
  end
56
71
  end
data/lib/fedex/rate.rb CHANGED
@@ -18,9 +18,10 @@ module Fedex
18
18
  # @total_net_freight #The freight charge minus dicounts
19
19
  # @total_surcharges #The total amount of all surcharges applied to this shipment
20
20
  # @total_base_charge #The total base charge
21
- attr_accessor :service_type, :rate_type, :rate_zone, :total_billing_weight, :total_freight_discounts, :total_net_charge, :total_taxes, :total_net_freight, :total_surcharges, :total_base_charge
21
+ attr_accessor :service_type, :transit_time, :rate_type, :rate_zone, :total_billing_weight, :total_freight_discounts, :total_net_charge, :total_taxes, :total_net_freight, :total_surcharges, :total_base_charge
22
22
  def initialize(options = {})
23
23
  @service_type = options[:service_type]
24
+ @transit_time = options[:transit_time]
24
25
  @rate_type = options[:rate_type]
25
26
  @rate_zone = options[:rate_zone]
26
27
  @total_billing_weight = "#{options[:total_billing_weight][:value]} #{options[:total_billing_weight][:units]}"
@@ -34,4 +35,4 @@ module Fedex
34
35
  @total_rebates = (options[:total_rebates]||{})[:amount]
35
36
  end
36
37
  end
37
- end
38
+ end
@@ -12,10 +12,10 @@ module Fedex
12
12
  # If true the rate method will return the complete response from the Fedex Web Service
13
13
  attr_accessor :debug
14
14
  # Fedex Text URL
15
- TEST_URL = "https://gatewaybeta.fedex.com:443/xml/"
15
+ TEST_URL = "https://wsbeta.fedex.com:443/xml/"
16
16
 
17
17
  # Fedex Production URL
18
- PRODUCTION_URL = "https://gateway.fedex.com:443/xml/"
18
+ PRODUCTION_URL = "https://ws.fedex.com:443/xml/"
19
19
 
20
20
  # List of available Service Types
21
21
  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)
@@ -35,6 +35,9 @@ module Fedex
35
35
  # List of available Payment Types
36
36
  PAYMENT_TYPE = %w(RECIPIENT SENDER THIRD_PARTY)
37
37
 
38
+ # List of available Carrier Codes
39
+ CARRIER_CODES = %w(FDXC FDXE FDXG FDCC FXFR FXSP)
40
+
38
41
  # In order to use Fedex rates API you must first apply for a developer(and later production keys),
39
42
  # Visit {http://www.fedex.com/us/developer/ Fedex Developer Center} for more information about how to obtain your keys.
40
43
  # @param [String] key - Fedex web service key
@@ -52,6 +55,13 @@ module Fedex
52
55
  @shipping_options = options[:shipping_options] ||={}
53
56
  @payment_options = options[:payment_options] ||={}
54
57
  requires!(@payment_options, :type, :account_number, :name, :company, :phone_number, :country_code) if @payment_options.length > 0
58
+ if options.has_key?(:mps)
59
+ @mps = options[:mps]
60
+ requires!(@mps, :package_count, :total_weight, :sequence_number)
61
+ requires!(@mps, :master_tracking_id) if @mps.has_key?(:sequence_number) && @mps[:sequence_number].to_i >= 2
62
+ else
63
+ @mps = {}
64
+ end
55
65
  # Expects hash with addr and port
56
66
  if options[:http_proxy]
57
67
  self.class.http_proxy options[:http_proxy][:host], options[:http_proxy][:port]
@@ -175,13 +185,32 @@ module Fedex
175
185
  }
176
186
  end
177
187
 
188
+ # Add Master Tracking Id (for MPS Shipping Labels, this is required when requesting labels 2 through n)
189
+ def add_master_tracking_id(xml)
190
+ if @mps.has_key? :master_tracking_id
191
+ xml.MasterTrackingId{
192
+ xml.TrackingIdType @mps[:master_tracking_id][:tracking_id_type]
193
+ xml.TrackingNumber @mps[:master_tracking_id][:tracking_number]
194
+ }
195
+ end
196
+ end
197
+
178
198
  # Add packages to xml request
179
199
  def add_packages(xml)
200
+ add_master_tracking_id(xml) if @mps.has_key? :master_tracking_id
180
201
  package_count = @packages.size
181
- xml.PackageCount package_count
202
+ if @mps.has_key? :package_count
203
+ xml.PackageCount @mps[:package_count]
204
+ else
205
+ xml.PackageCount package_count
206
+ end
182
207
  @packages.each do |package|
183
208
  xml.RequestedPackageLineItems{
184
- xml.GroupPackageCount 1
209
+ if @mps.has_key? :sequence_number
210
+ xml.SequenceNumber @mps[:sequence_number]
211
+ else
212
+ xml.GroupPackageCount 1
213
+ end
185
214
  if package[:insured_value]
186
215
  xml.InsuredValue{
187
216
  xml.Currency package[:insured_value][:currency]
@@ -294,7 +323,14 @@ module Fedex
294
323
  # Build xml nodes dynamically from the hash keys and values
295
324
  def hash_to_xml(xml, hash)
296
325
  hash.each do |key, value|
297
- element = camelize(key)
326
+ key_s_down = key.to_s.downcase
327
+ if key_s_down.match(/^commodities_\d{1,}$/)
328
+ element = 'Commodities'
329
+ elsif key_s_down.match(/^masked_data_\d{1,}$/)
330
+ element = 'MaskedData'
331
+ else
332
+ element = camelize(key)
333
+ end
298
334
  if value.is_a?(Hash)
299
335
  xml.send element do |x|
300
336
  hash_to_xml(x, value)
@@ -0,0 +1,62 @@
1
+ require 'fedex/request/base'
2
+
3
+ module Fedex
4
+ module Request
5
+ class Delete < Base
6
+
7
+ attr_reader :tracking_number
8
+
9
+ def initialize(credentials, options={})
10
+ requires!(options, :tracking_number)
11
+
12
+ @tracking_number = options[:tracking_number]
13
+ @deletion_control = options[:deletion_control] || 'DELETE_ALL_PACKAGES'
14
+ @credentials = credentials
15
+ end
16
+
17
+ def process_request
18
+ api_response = self.class.post(api_url, :body => build_xml)
19
+ puts api_response if @debug == true
20
+ response = parse_response(api_response)
21
+ unless success?(response)
22
+ error_message = if response[:shipment_reply]
23
+ [response[:shipment_reply][:notifications]].flatten.first[:message]
24
+ else
25
+ "#{api_response["Fault"]["detail"]["fault"]["reason"]}\n
26
+ --#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
27
+ end rescue $1
28
+ raise RateError, error_message
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Build xml Fedex Web Service request
35
+ def build_xml
36
+ builder = Nokogiri::XML::Builder.new do |xml|
37
+ xml.DeleteShipmentRequest(:xmlns => "http://fedex.com/ws/ship/v#{service[:version]}"){
38
+ add_web_authentication_detail(xml)
39
+ add_client_detail(xml)
40
+ add_version(xml)
41
+ xml.TrackingId {
42
+ xml.TrackingIdType 'FEDEX'
43
+ xml.TrackingNumber @tracking_number
44
+ }
45
+ xml.DeletionControl @deletion_control
46
+ }
47
+ end
48
+ builder.doc.root.to_xml
49
+ end
50
+
51
+ def service
52
+ { :id => 'ship', :version => Fedex::API_VERSION }
53
+ end
54
+
55
+ # Successful request
56
+ def success?(response)
57
+ response[:shipment_reply] &&
58
+ %w{SUCCESS WARNING NOTE}.include?(response[:shipment_reply][:highest_severity])
59
+ end
60
+ end
61
+ end
62
+ end