fedex_ship 0.1.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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.idea/.rakeTasks +7 -0
  4. data/.idea/fedex_ship-0.1.0.iml +22 -0
  5. data/.idea/misc.xml +7 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/vcs.xml +6 -0
  8. data/.idea/workspace.xml +56 -0
  9. data/.rspec +2 -0
  10. data/Gemfile +5 -0
  11. data/Rakefile +7 -0
  12. data/Readme.md +496 -0
  13. data/fedex_ship.gemspec +28 -0
  14. data/lib/fedex_ship.rb +55 -0
  15. data/lib/fedex_ship/address.rb +31 -0
  16. data/lib/fedex_ship/credentials.rb +26 -0
  17. data/lib/fedex_ship/document.rb +51 -0
  18. data/lib/fedex_ship/ground_manifest.rb +25 -0
  19. data/lib/fedex_ship/helpers.rb +20 -0
  20. data/lib/fedex_ship/label.rb +71 -0
  21. data/lib/fedex_ship/rate.rb +38 -0
  22. data/lib/fedex_ship/request/address.rb +97 -0
  23. data/lib/fedex_ship/request/base.rb +443 -0
  24. data/lib/fedex_ship/request/delete.rb +76 -0
  25. data/lib/fedex_ship/request/document.rb +45 -0
  26. data/lib/fedex_ship/request/ground_close.rb +73 -0
  27. data/lib/fedex_ship/request/label.rb +29 -0
  28. data/lib/fedex_ship/request/logs_fedex.rb +74 -0
  29. data/lib/fedex_ship/request/pickup.rb +135 -0
  30. data/lib/fedex_ship/request/pickup_availability.rb +102 -0
  31. data/lib/fedex_ship/request/rate.rb +94 -0
  32. data/lib/fedex_ship/request/service_availability.rb +86 -0
  33. data/lib/fedex_ship/request/shipment.rb +249 -0
  34. data/lib/fedex_ship/request/tracking_information.rb +119 -0
  35. data/lib/fedex_ship/shipment.rb +115 -0
  36. data/lib/fedex_ship/tracking_information.rb +54 -0
  37. data/lib/fedex_ship/tracking_information/event.rb +24 -0
  38. data/lib/fedex_ship/version.rb +6 -0
  39. data/spec/config/fedex_credentials.example.yml +13 -0
  40. data/spec/lib/fedex_ship/address_spec.rb +59 -0
  41. data/spec/lib/fedex_ship/delete_spec.rb +26 -0
  42. data/spec/lib/fedex_ship/document_spec.rb +177 -0
  43. data/spec/lib/fedex_ship/ground_close_spec.rb +42 -0
  44. data/spec/lib/fedex_ship/label_spec.rb +73 -0
  45. data/spec/lib/fedex_ship/pickup_availability_spec.rb +19 -0
  46. data/spec/lib/fedex_ship/pickup_spec.rb +32 -0
  47. data/spec/lib/fedex_ship/rate_spec.rb +216 -0
  48. data/spec/lib/fedex_ship/service_availability_spec.rb +20 -0
  49. data/spec/lib/fedex_ship/shipment_spec.rb +86 -0
  50. data/spec/lib/fedex_ship/track_spec.rb +67 -0
  51. data/spec/spec_helper.rb +12 -0
  52. data/spec/support/credentials.rb +15 -0
  53. data/spec/support/vcr.rb +14 -0
  54. metadata +193 -0
@@ -0,0 +1,45 @@
1
+ require 'fedex_ship/request/shipment'
2
+ require 'fedex_ship/document'
3
+
4
+ module FedexShip
5
+ module Request
6
+ class Document < Shipment
7
+
8
+ def initialize(credentials, options={})
9
+ super(credentials, options)
10
+
11
+ @shipping_document = options[:shipping_document]
12
+ @filenames = options.fetch(:filenames) { {} }
13
+ end
14
+
15
+ def add_custom_components(xml)
16
+ super
17
+
18
+ add_shipping_document(xml) if @shipping_document
19
+ end
20
+
21
+ private
22
+
23
+ # Add shipping document specification
24
+ def add_shipping_document(xml)
25
+ xml.ShippingDocumentSpecification{
26
+ Array(@shipping_document[:shipping_document_types]).each do |type|
27
+ xml.ShippingDocumentTypes type
28
+ end
29
+ hash_to_xml(xml, @shipping_document.reject{ |k| k == :shipping_document_types})
30
+ }
31
+ end
32
+
33
+ def success_response(api_response, response)
34
+ super
35
+
36
+ shipment_documents = response.merge!({
37
+ :filenames => @filenames
38
+ })
39
+
40
+ FedexShip::Document.new shipment_documents
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,73 @@
1
+ require 'fedex_ship/request/base'
2
+ require 'fedex_ship/ground_manifest'
3
+
4
+ module FedexShip
5
+ module Request
6
+ class GroundClose < Base
7
+
8
+ attr_reader :up_to_time, :filename
9
+
10
+ def initialize(credentials, options={})
11
+ requires!(options, :up_to_time)
12
+
13
+ @credentials = credentials
14
+ @up_to_time = options[:up_to_time]
15
+ @filename = options[:filename]
16
+ @debug = ENV['DEBUG'] == 'true'
17
+ end
18
+
19
+ def process_request
20
+ api_response = self.class.post(api_url, :body => build_xml)
21
+ puts api_response if @debug == true
22
+ response = parse_response(api_response)
23
+ if success?(response)
24
+ success_response(response)
25
+ else
26
+ error_message = if response[:ground_close_reply]
27
+ [response[:ground_close_reply][:notifications]].flatten.first[:message]
28
+ else
29
+ "#{api_response["Fault"]["detail"]["fault"]["reason"]}\n
30
+ --#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
31
+ end rescue $1
32
+ raise RateError, error_message
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def success_response(response)
39
+ manifest_details = {
40
+ :filename => filename,
41
+ :manifest => response[:ground_close_reply][:manifest]
42
+ }
43
+ manifest = FedexShip::GroundManifest.new(manifest_details)
44
+ puts "manifest written to #{filename}" if @debug == true
45
+ manifest
46
+ end
47
+
48
+ # Build xml Fedex Web Service request
49
+ def build_xml
50
+ builder = Nokogiri::XML::Builder.new do |xml|
51
+ xml.GroundCloseRequest(:xmlns => "http://fedex.com/ws/close/v2"){
52
+ add_web_authentication_detail(xml)
53
+ add_client_detail(xml)
54
+ add_version(xml)
55
+
56
+ xml.TimeUpToWhichShipmentsAreToBeClosed up_to_time.utc.iso8601(2)
57
+ }
58
+ end
59
+ builder.doc.root.to_xml
60
+ end
61
+
62
+ def service
63
+ { :id => 'clos', :version => '2' }
64
+ end
65
+
66
+ # Successful request
67
+ def success?(response)
68
+ response[:ground_close_reply] &&
69
+ %w{SUCCESS WARNING NOTE}.include?(response[:ground_close_reply][:highest_severity])
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,29 @@
1
+ require 'fedex_ship/request/base'
2
+ require 'fedex_ship/label'
3
+ require 'fedex_ship/request/shipment'
4
+ require 'fileutils'
5
+
6
+ module FedexShip
7
+ module Request
8
+ class Label < Shipment
9
+ def initialize(credentials, options={})
10
+ super(credentials, options)
11
+ @filename = options[:filename]
12
+ end
13
+
14
+ private
15
+
16
+ def success_response(api_response, response)
17
+ super
18
+
19
+ label_details = response.merge!({
20
+ :format => @label_specification[:image_type],
21
+ :file_name => @filename
22
+ })
23
+
24
+ FedexShip::Label.new label_details
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,74 @@
1
+ require 'fedex_ship/request/base'
2
+ require 'logger'
3
+ module FedexShip
4
+ module Request
5
+ class LogsFedex < Base
6
+
7
+ def ship_serv_log(info)
8
+ begin
9
+ date = Date.today.to_s
10
+ info = (Time.now).to_s + ' ' + info
11
+ log = File.open('log/shipment_' + date + '.log','a')
12
+ log.puts(info)
13
+ log.close
14
+ rescue Exception => ex.to_s
15
+ puts ex.to_s
16
+ log.close
17
+ end
18
+ end
19
+
20
+ def rate_serv_log(info)
21
+ begin
22
+ date = Date.today.to_s
23
+ info = (Time.now).to_s + ' ' + info
24
+ log = File.open('log/rate_' + date + '.log','a')
25
+ log.puts(info)
26
+ log.close
27
+ rescue Exception => ex.to_s
28
+ puts ex.to_s
29
+ log.close
30
+ end
31
+ end
32
+
33
+ def track_serv_log(info)
34
+ begin
35
+ date = Date.today.to_s
36
+ info = (Time.now).to_s + ' ' + info
37
+ log = File.open('log/track_' + date + '.log','a')
38
+ log.puts(info)
39
+ log.close
40
+ rescue Exception => ex.to_s
41
+ puts ex.to_s
42
+ log.close
43
+ end
44
+ end
45
+
46
+ def pickup_serv_log(info)
47
+ begin
48
+ date = Date.today.to_s
49
+ info = (Time.now).to_s + ' ' + info
50
+ log = File.open('log/pickup_' + date + '.log','a')
51
+ log.puts(info)
52
+ log.close
53
+ rescue Exception => ex.to_s
54
+ puts ex.to_s
55
+ log.close
56
+ end
57
+ end
58
+
59
+ def delete_ship_serv_log(info)
60
+ begin
61
+ date = Date.today.to_s
62
+ info = (Time.now).to_s + ' ' + info
63
+ log = File.open('log/delete_shipment_' + date + '.log','a')
64
+ log.puts(info)
65
+ log.close
66
+ rescue Exception => ex.to_s
67
+ puts ex.to_s
68
+ log.close
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,135 @@
1
+ require 'fedex_ship/request/base'
2
+ require 'fedex_ship/request/logs_fedex'
3
+
4
+ module FedexShip
5
+ module Request
6
+ class Pickup < LogsFedex
7
+ def initialize(credentials, options = {})
8
+ requires!(options, :packages, :ready_timestamp, :close_time, :carrier_code, :country_relationship)
9
+ @debug = ENV['DEBUG'] == 'true'
10
+
11
+ @credentials = credentials
12
+ @packages = options[:packages]
13
+ @ready_timestamp = options[:ready_timestamp]
14
+ @close_time = options[:close_time]
15
+ @carrier_code = options[:carrier_code]
16
+ @remarks = options[:remarks] if options[:remarks]
17
+ @pickup_location = options[:pickup_location]
18
+ @commodity_description = options[:commodity_description] if options[:commodity_description]
19
+ @country_relationship = options[:country_relationship]
20
+ end
21
+
22
+ # Sends post request to Fedex web service and parse the response, a Pickup object is created if the response is successful
23
+ def process_request
24
+ @build_xml = build_xml
25
+ pickup_serv_log('Final XML Request : ' + @build_xml.to_s)
26
+ api_url_srv = api_url + "/pickup"
27
+ pickup_serv_log('URL for API : ' + api_url_srv.to_s)
28
+ api_response = self.class.post(api_url_srv, :body => build_xml)
29
+ pickup_serv_log('API Response : ' + api_response.to_s)
30
+ puts api_response if @debug
31
+ response = parse_response(api_response)
32
+ if success?(response)
33
+ pickup_serv_log('Successfully Done : ' + response.to_s)
34
+ success_response(api_response, response)
35
+ else
36
+ failure_response(api_response, response)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ # Build xml Fedex Web Service request
43
+ def build_xml
44
+ ns = "http://fedex.com/ws/pickup/v17"
45
+ builder = Nokogiri::XML::Builder.new do |xml|
46
+ xml.Envelope("xmlns" => "http://fedex.com/ws/pickup/v17") {
47
+ xml.parent.namespace = xml.parent.add_namespace_definition("soapenv", "http://schemas.xmlsoap.org/soap/envelope/")
48
+ xml['soapenv'].Header {}
49
+ xml['soapenv'].Body {
50
+ xml.CreatePickupRequest() {
51
+ add_web_authentication_detail(xml)
52
+ add_client_detail(xml)
53
+ add_version(xml)
54
+ add_origin_detail(xml)
55
+ add_package_details(xml)
56
+ }
57
+ }
58
+ }
59
+ end
60
+ builder.doc.root.to_xml
61
+ end
62
+
63
+ def service
64
+ {:id => 'disp', :version => "17"}
65
+ end
66
+
67
+ # Add shipper to xml request
68
+ def add_origin_detail(xml)
69
+ xml.OriginDetail {
70
+ # if @pickup_location
71
+ if true
72
+ xml.UseAccountAddress false
73
+ add_pickup_location(xml)
74
+ else
75
+ xml.UseAccountAddress true
76
+ end
77
+ xml.ReadyTimestamp @ready_timestamp
78
+ xml.CompanyCloseTime @close_time.strftime("%H:%M:%S")
79
+ }
80
+ end
81
+
82
+ def add_package_details(xml)
83
+ xml.PackageCount @packages[:count]
84
+ xml.TotalWeight {
85
+ xml.Units @packages[:weight][:units]
86
+ xml.Value @packages[:weight][:value]
87
+ }
88
+ xml.CarrierCode @carrier_code
89
+ xml.Remarks @remarks if @remarks
90
+ xml.CommodityDescription @commodity_description if @commodity_description
91
+ xml.CountryRelationship @country_relationship
92
+ end
93
+
94
+ def add_pickup_location(xml)
95
+ xml.PickupLocation {
96
+ xml.Contact {
97
+ xml.PersonName @pickup_location[:name]
98
+ xml.CompanyName @pickup_location[:company]
99
+ xml.PhoneNumber @pickup_location[:phone_number]
100
+ }
101
+ xml.Address {
102
+ Array(@pickup_location[:address]).take(2).each do |address_line|
103
+ xml.StreetLines address_line
104
+ end
105
+ xml.City @pickup_location[:city]
106
+ xml.StateOrProvinceCode @pickup_location[:state]
107
+ xml.PostalCode @pickup_location[:postal_code]
108
+ xml.CountryCode @pickup_location[:country_code]
109
+ }
110
+ }
111
+ end
112
+
113
+ # Callback used after a failed pickup response.
114
+ def failure_response(api_response, response)
115
+ error_message = if response[:envelope][:body][:create_pickup_reply]
116
+ [response[:envelope][:body][:create_pickup_reply][:notifications]].flatten.first[:message]
117
+ else
118
+ "#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{Array(api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"]).join("\n--")}"
119
+ end rescue $1
120
+ raise RateError, error_message
121
+ end
122
+
123
+ # Callback used after a successful pickup response.
124
+ def success_response(api_response, response)
125
+ @response_details = response[:envelope][:body][:create_pickup_reply]
126
+ end
127
+
128
+ # Successful request
129
+ def success?(response)
130
+ response[:envelope][:body][:create_pickup_reply] &&
131
+ %w{SUCCESS WARNING NOTE}.include?(response[:envelope][:body][:create_pickup_reply][:highest_severity])
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,102 @@
1
+ require 'fedex_ship/request/base'
2
+ require 'fedex_ship/request/logs_fedex'
3
+
4
+ module FedexShip
5
+ module Request
6
+ class PickupAvailability < LogsFedex
7
+
8
+ def initialize(credentials, options = {})
9
+ requires!(options, :country_code, :request_type, :carrier_code)
10
+ @debug = ENV['DEBUG'] == 'true'
11
+
12
+ @credentials = credentials
13
+
14
+ @country_code = options[:country_code]
15
+ @postal_code = options[:postal_code] if options[:postal_code]
16
+ @state_code = options[:state_code] if options[:state_code]
17
+ @request_type = options[:request_type]
18
+ @carrier_code = options[:carrier_code]
19
+ @dispatch_date = options[:dispatch_date] if options[:dispatch_date]
20
+ end
21
+
22
+ def process_request
23
+ @build_xml = build_xml
24
+ pickup_serv_log('Final XML Request : ' + @build_xml.to_s)
25
+ api_url_srv = api_url + "/pickup"
26
+ pickup_serv_log('URL for API : ' + api_url_srv.to_s)
27
+ api_response = self.class.post(api_url_srv, :body => build_xml)
28
+ pickup_serv_log('API Response : ' + api_response.to_s)
29
+ puts api_response if @debug == true
30
+ response = parse_response(api_response)
31
+ if success?(response)
32
+ pickup_serv_log('Successfully Done : ' + response.to_s)
33
+ success_response(api_response, response)
34
+ else
35
+ failure_response(api_response, response)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # Build xml Fedex Web Service request
42
+ def build_xml
43
+ ns = "http://fedex.com/ws/pickup/v17"
44
+ builder = Nokogiri::XML::Builder.new do |xml|
45
+ xml.Envelope("xmlns" => "http://fedex.com/ws/pickup/v17") {
46
+ xml.parent.namespace = xml.parent.add_namespace_definition("soapenv", "http://schemas.xmlsoap.org/soap/envelope/")
47
+ xml['soapenv'].Header {}
48
+ xml['soapenv'].Body {
49
+ xml.PickupAvailabilityRequest(:xmlns => ns) {
50
+ add_web_authentication_detail(xml)
51
+ add_client_detail(xml)
52
+ add_version(xml)
53
+ add_pickup_address(xml)
54
+ add_other_pickup_details(xml)
55
+ }
56
+ }
57
+ }
58
+ end
59
+ builder.doc.root.to_xml
60
+ end
61
+
62
+ def service
63
+ {:id => 'disp', :version => "17"}
64
+ end
65
+
66
+ def add_pickup_address(xml)
67
+ xml.PickupAddress {
68
+ xml.PostalCode @postal_code if @postal_code
69
+ xml.CountryCode @country_code
70
+ xml.StateOrProvinceCode @state_code if @state_code
71
+ }
72
+ end
73
+
74
+ def add_other_pickup_details(xml)
75
+ xml.PickupRequestType @request_type
76
+ xml.DispatchDate @dispatch_date if @dispatch_date
77
+ xml.Carriers @carrier_code
78
+ end
79
+
80
+ # Callback used after a failed pickup response.
81
+ def failure_response(api_response, response)
82
+ error_message = if response[:envelope][:body][:pickup_availability_reply]
83
+ [response[:envelope][:body][:pickup_availability_reply][:notifications]].flatten.first[:message]
84
+ else
85
+ "#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
86
+ end rescue $1
87
+ raise RateError, error_message
88
+ end
89
+
90
+ # Callback used after a successful pickup response.
91
+ def success_response(api_response, response)
92
+ @response_details = response[:envelope][:body][:pickup_availability_reply]
93
+ end
94
+
95
+ # Successful request
96
+ def success?(response)
97
+ response[:envelope][:body][:pickup_availability_reply] &&
98
+ %w{SUCCESS WARNING NOTE}.include?(response[:envelope][:body][:pickup_availability_reply][:highest_severity])
99
+ end
100
+ end
101
+ end
102
+ end