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,115 @@
1
+ require 'fedex_ship/credentials'
2
+ require 'fedex_ship/request/label'
3
+ require 'fedex_ship/request/rate'
4
+ require 'fedex_ship/request/tracking_information'
5
+ require 'fedex_ship/request/address'
6
+ require 'fedex_ship/request/document'
7
+ require 'fedex_ship/request/delete'
8
+ require 'fedex_ship/request/ground_close'
9
+ require 'fedex_ship/request/pickup'
10
+ require 'fedex_ship/request/pickup_availability'
11
+ require 'fedex_ship/request/service_availability'
12
+
13
+ module FedexShip
14
+ class Shipment
15
+
16
+ # In order to use Fedex rates API you must first apply for a developer(and later production keys),
17
+ # Visit {http://www.fedex.com/us/developer/ Fedex Developer Center} for more information about how to obtain your keys.
18
+ # @param [String] key - Fedex web service key
19
+ # @param [String] password - Fedex password
20
+ # @param [String] account_number - Fedex account_number
21
+ # @param [String] meter - Fedex meter number
22
+ # @param [String] mode - [development/production]
23
+ #
24
+ # return a FedexShip::Shipment object
25
+ def initialize(options={})
26
+ @credentials = Credentials.new(options)
27
+ end
28
+
29
+ # @param [Hash] shipper, A hash containing the shipper information
30
+ # @param [Hash] recipient, A hash containing the recipient information
31
+ # @param [Array] packages, An array including a hash for each package being shipped
32
+ # @param [String] service_type, A valid fedex service type, to view a complete list of services FedexShip::Shipment::SERVICE_TYPES
33
+ # @param [String] filename, A location where the label will be saved
34
+ # @param [Hash] label_specification, A hash containing the label printer settings
35
+ def label(options = {})
36
+ Request::Label.new(@credentials, options).process_request
37
+ end
38
+
39
+ # @param [Hash] shipper, A hash containing the shipper information
40
+ # @param [Hash] recipient, A hash containing the recipient information
41
+ # @param [Array] packages, An array including a hash for each package being shipped
42
+ # @param [String] service_type, A valid fedex service type, to view a complete list of services FedexShip::Shipment::SERVICE_TYPES
43
+ def rate(options = {})
44
+ Request::Rate.new(@credentials, options).process_request
45
+ end
46
+
47
+ # @param [Hash] address, A hash containing the address information
48
+ def validate_address(options = {})
49
+ Request::Address.new(@credentials, options).process_request
50
+ end
51
+
52
+ # @param [Hash] shipper, A hash containing the shipper information
53
+ # @param [Hash] recipient, A hash containing the recipient information
54
+ # @param [Array] packages, An array including a hash for each package being shipped
55
+ # @param [String] service_type, A valid fedex service type, to view a complete list of services FedexShip::Shipment::SERVICE_TYPES
56
+ def ship(options = {})
57
+ Request::Shipment.new(@credentials, options).process_request
58
+ end
59
+
60
+ # @param [String] carrier_code, A valid fedex carrier code, to view a complete list of carrier codes FedexShip::Shipment::CARRIER_CODES
61
+ # @param [Hash] packages, A hash containing the number of packages and their total weight
62
+ # @param [DateTime] ready_timestamp, A timestamp that indicates what day and time the package will be available for pickup
63
+ # @param [Time] close_time, The latest time that the business will be open to accept a pickup
64
+ # @param [Hash] pickup_location, A hash containing the pickup location information
65
+ def pickup(options = {})
66
+ Request::Pickup.new(@credentials, options).process_request
67
+ end
68
+
69
+ # @param [Hash] package_id, A string with the requested tracking number
70
+ # @param [Hash] package_type, A string identifitying the type of tracking number used. Full list FedexShip::Track::PACKAGE_IDENTIFIER_TYPES
71
+ def track(options = {})
72
+ Request::TrackingInformation.new(@credentials, options).process_request
73
+ end
74
+
75
+ # @param [Hash] shipper, A hash containing the shipper information
76
+ # @param [Hash] recipient, A hash containing the recipient information
77
+ # @param [Array] packages, An array including a hash for each package being shipped
78
+ # @param [String] service_type, A valid fedex service type, to view a complete list of services FedexShip::Shipment::SERVICE_TYPES
79
+ # @param [Hash] customs_clearance, A hash containing customs clearance specification
80
+ # @param [Hash] shipping_document, A hash containing shipping document specification
81
+ # @param [Array] filenames, A location where the label and shipment documents will be saved
82
+ def document(options = {})
83
+ Request::Document.new(@credentials, options).process_request
84
+ end
85
+
86
+ # @param [Hash] package_id, A string with the tracking number to delete
87
+ def delete(options = {})
88
+ Request::Delete.new(@credentials, options).process_request
89
+ end
90
+
91
+ # @param [Date] up_to_time, A time up to which shipments are to be closed
92
+ # @param [String] filename, A location where the manifest (text file) will be saved
93
+ def ground_close(options = {})
94
+ Request::GroundClose.new(@credentials, options).process_request
95
+ end
96
+ # @param [String] country_code, A string containing country code
97
+ # @param [String] state_code, A string containing state code
98
+ # @param [String] postal_code, A string containing postal code
99
+ # @param [String] carrier_code, A string containing carrier code
100
+ # @param [String] request_type, A string with request type
101
+ # @param [String] dispatch_date, A string with dispatch date in YYYY-MM-DD format
102
+ def pickup_availability(options = {})
103
+ Request::PickupAvailability.new(@credentials, options).process_request
104
+ end
105
+
106
+ # param [Hash] origin, A hash containing origin information
107
+ # param [Hash] destination, A hash containing destination information
108
+ # param [date] ship_date, A string containing ship date in YYYY-MM-DD format
109
+ # param [String] carrier_code, A string containing carrier code
110
+ def service_availability(options = {})
111
+ Request::ServiceAvailability.new(@credentials, options).process_request
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,54 @@
1
+ require 'fedex_ship/tracking_information/event'
2
+
3
+ module FedexShip
4
+ class TrackingInformation
5
+ PACKAGE_IDENTIFIER_TYPES = %w{
6
+ BILL_OF_LADING
7
+ COD_RETURN_TRACKING_NUMBER
8
+ CUSTOMER_AUTHORIZATION_NUMBER
9
+ CUSTOMER_REFERENCE
10
+ DEPARTMENT
11
+ FREE_FORM_REFERENCE
12
+ GROUND_INTERNATIONAL
13
+ GROUND_SHIPMENT_ID
14
+ GROUP_MPS
15
+ INVOICE
16
+ JOB_GLOBAL_TRACKING_NUMBER
17
+ ORDER_GLOBAL_TRACKING_NUMBER
18
+ ORDER_TO_PAY_NUMBER
19
+ PARTNER_CARRIER_NUMBER
20
+ PART_NUMBER
21
+ PURCHASE_ORDER
22
+ RETURN_MATERIALS_AUTHORIZATION
23
+ RETURNED_TO_SHIPPER_TRACKING_NUMBER
24
+ TRACKING_CONTROL_NUMBER
25
+ TRACKING_NUMBER_OR_DOORTAG
26
+ TRANSPORTATION_CONTROL_NUMBER
27
+ SHIPPER_REFERENCE
28
+ STANDARD_MPS
29
+ }
30
+
31
+ attr_reader :tracking_number, :signature_name, :service_type, :status, :status_code, :delivery_at, :events, :unique_tracking_number, :details, :other_identifiers
32
+
33
+ def initialize(details = {})
34
+ @details = details
35
+
36
+ @tracking_number = details[:tracking_number]
37
+ @unique_tracking_number = details[:tracking_number_unique_identifier]
38
+ @signature_name = details[:delivery_signature_name]
39
+ @service_type = details[:service][:type]
40
+ @status = details[:status_detail][:description]
41
+ @status_code = details[:status_detail][:code]
42
+ @other_identifiers = details[:other_identifiers]
43
+
44
+ if details.has_key?(:actual_delivery_timestamp)
45
+ @delivery_at = Time.parse(details[:actual_delivery_timestamp])
46
+ end
47
+
48
+ @events = [details[:events]].flatten.compact.map do |event_details|
49
+ Event.new(event_details)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,24 @@
1
+ module FedexShip
2
+ class TrackingInformation
3
+ class Event
4
+ attr_reader :description, :type, :occurred_at, :city, :state, :postal_code,
5
+ :country, :residential, :exception_code, :exception_description
6
+
7
+ alias_method :occured_at, :occurred_at
8
+
9
+ def initialize(details = {})
10
+ @description = details[:event_description]
11
+ @type = details[:event_type]
12
+ @occurred_at = Time.parse(details[:timestamp])
13
+ @city = details[:address][:city]
14
+ @state = details[:address][:state_or_province_code]
15
+ @postal_code = details[:address][:postal_code]
16
+ @country = details[:address][:country_code]
17
+ @residential = details[:address][:residential] == "true"
18
+ @exception_code = details[:status_exception_code]
19
+ @exception_description = details[:status_exception_description]
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ module FedexShip
2
+ VERSION = "0.1.0"
3
+ API_VERSION = "13"
4
+ PICKUP_API_VERSION = "17"
5
+ SERVICE_AVAILABILITY_API_VERSION = "5"
6
+ end
@@ -0,0 +1,13 @@
1
+ development:
2
+ :key: 'xxx'
3
+ :password: 'xxx'
4
+ :account_number: 'xxx'
5
+ :meter: 'xxx'
6
+ :mode: 'test'
7
+
8
+ production:
9
+ :key: 'xxx'
10
+ :password: 'xxx'
11
+ :account_number: 'xxx'
12
+ :meter: 'xxx'
13
+ :mode: 'production'
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module FedexShip
4
+ describe Address, :production do
5
+ describe "validation" do
6
+
7
+ # Address Validation is only enabled in the production environment
8
+ #
9
+ let(:fedex) { Shipment.new(fedex_production_credentials) }
10
+
11
+ context "valid address", :vcr do
12
+ let(:address) do
13
+ {
14
+ :address => "5 Elm Street",
15
+ :city => "Norwalk",
16
+ :state => "CT",
17
+ :postal_code => "06850",
18
+ :country => "USA"
19
+ }
20
+ end
21
+
22
+ let(:options) do
23
+ { :address => address }
24
+ end
25
+
26
+ it "validates the address" do
27
+ address = fedex.validate_address(options)
28
+
29
+ expect(address.residential).to be_truthy
30
+ expect(address.business).to be_falsey
31
+ expect(address.score).to eq(100)
32
+
33
+ expect(address.postal_code).to eq("06850-3901")
34
+ end
35
+ end
36
+
37
+ context "multiple address validation results", :vcr do
38
+ let(:address) do
39
+ {
40
+ :address => "301 Las Colinas Blvd",
41
+ :city => "Irving",
42
+ :state => "TX",
43
+ :postal_code => "75039",
44
+ :country => "USA"
45
+ }
46
+ end
47
+
48
+ let(:options) do
49
+ { :address => address }
50
+ end
51
+
52
+ it "validates the address" do
53
+ expect{ fedex.validate_address(options) }.to_not raise_error
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module FedexShip
4
+ describe Shipment do
5
+ let (:fedex) { Shipment.new(fedex_credentials) }
6
+ context "#delete" do
7
+ context "delete shipment with tracking number", :vcr do
8
+ let(:options) do
9
+ { :tracking_number => '794608797150' }
10
+ end
11
+ it "deletes a shipment" do
12
+ expect{ fedex.delete(options) }.to_not raise_error
13
+ end
14
+ end
15
+ context "raise an error when the tracking number is invalid", :vcr do
16
+ let(:options) do
17
+ { :tracking_number => '111111111' }
18
+ end
19
+
20
+ it "raises an error" do
21
+ expect {fedex.delete(options) }.to raise_error(FedexShip::RateError, 'Invalid tracking number')
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,177 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+
4
+ module FedexShip
5
+ describe Document do
6
+ let(:fedex) { Shipment.new(fedex_credentials) }
7
+ let(:shipper) do
8
+ { :name => "Sender", :company => "Company", :phone_number => "555-555-5555", :address => "King Street", :city => "Ashbourne", :postal_code => "DE6 1EA", :country_code => "GB" }
9
+ end
10
+ let(:recipient) do
11
+ { :name => "Recipient", :company => "Company", :phone_number => "555-555-5555", :address => "Main Street", :city => "Frankin Park", :state => "IL", :postal_code => "60131", :country_code => "US", :residential => true }
12
+ end
13
+ let(:packages) do
14
+ [
15
+ {
16
+ :weight => { :units => "LB", :value => 2 },
17
+ :dimensions => { :length => 10, :width => 5, :height => 4, :units => "IN" }
18
+ }
19
+ ]
20
+ end
21
+ let(:shipping_options) do
22
+ { :packaging_type => "YOUR_PACKAGING", :drop_off_type => "REGULAR_PICKUP" }
23
+ end
24
+ let(:customs) do
25
+ {
26
+ :duties_payment => {
27
+ :payment_type => 'SENDER',
28
+ :payor => {
29
+ :responsible_party => {
30
+ :account_number => fedex_credentials[:account_number],
31
+ :contact => {
32
+ :person_name => 'Mr. Test',
33
+ :phone_number => '12345678'
34
+ }
35
+ }
36
+ }
37
+ },
38
+ :document_content => 'NON_DOCUMENTS',
39
+ :customs_value => {
40
+ :currency => 'UKL', # UK Pounds Sterling
41
+ :amount => 155.79
42
+ },
43
+ :commercial_invoice => {
44
+ :terms_of_sale => 'DDU'
45
+ },
46
+ :commodities => [
47
+ {
48
+ :number_of_pieces => 1,
49
+ :description => 'Pink Toy',
50
+ :country_of_manufacture => 'GB',
51
+ :weight => {
52
+ :units => 'LB',
53
+ :value => 2
54
+ },
55
+ :quantity => 1,
56
+ :quantity_units => 'EA',
57
+ :unit_price => {
58
+ :currency => 'UKL',
59
+ :amount => 155.79
60
+ },
61
+ :customs_value => {
62
+ :currency => 'UKL', # UK Pounds Sterling
63
+ :amount => 155.79
64
+ }
65
+ }
66
+ ]
67
+ }
68
+ end
69
+ let(:document) do
70
+ {
71
+ :shipping_document_types => 'COMMERCIAL_INVOICE',
72
+ :commercial_invoice_detail => {
73
+ :format => {
74
+ :image_type => 'PDF',
75
+ :stock_type => 'PAPER_LETTER'
76
+ }
77
+ }
78
+ }
79
+ end
80
+
81
+ let(:filenames) {
82
+ require 'tmpdir'
83
+ {
84
+ :label => File.join(Dir.tmpdir, "label#{rand(15000)}.pdf"),
85
+ :commercial_invoice => File.join(Dir.tmpdir, "invoice#{rand(15000)}.pdf")
86
+ }
87
+ }
88
+
89
+ let(:options) do
90
+ {
91
+ :shipper => shipper,
92
+ :recipient => recipient,
93
+ :packages => packages,
94
+ :service_type => "INTERNATIONAL_PRIORITY",
95
+ :shipping_details => shipping_options,
96
+ :customs_clearance_detail => customs,
97
+ :shipping_document => document,
98
+ :filenames => filenames
99
+ }
100
+ end
101
+
102
+ describe 'document service', :vcr do
103
+
104
+ context 'with document specification' do
105
+
106
+ before do
107
+ @document = fedex.document(options)
108
+ end
109
+
110
+ it "saves a label to file" do
111
+ expect(File).to exist(filenames[:label])
112
+ end
113
+
114
+ it "saves invoice to file" do
115
+ expect(File).to exist(filenames[:commercial_invoice])
116
+ end
117
+
118
+ it "returns tracking number" do
119
+ expect(@document).to respond_to('tracking_number')
120
+ end
121
+
122
+ it "exposes complete response" do
123
+ expect(@document).to respond_to('response_details')
124
+ end
125
+
126
+ it "exposes the filenames" do
127
+ expect(@document).to respond_to('filenames')
128
+ end
129
+
130
+ end
131
+
132
+ context 'without document specification' do
133
+
134
+ before do
135
+ @document = fedex.document(
136
+ options.reject{|k| k == :shipping_document}
137
+ )
138
+ end
139
+
140
+ it "saves a label to file" do
141
+ expect(File).to exist(filenames[:label])
142
+ end
143
+
144
+ it "has no others files" do
145
+ expect(File).not_to exist(filenames[:commercial_invoice])
146
+ end
147
+
148
+ end
149
+
150
+ context 'filename missed' do
151
+ context 'for label' do
152
+ before do
153
+ filenames.delete(:label)
154
+ @document = fedex.document(options)
155
+ end
156
+
157
+ it "saves invoice to file" do
158
+ expect(File).to exist(filenames[:commercial_invoice])
159
+ end
160
+ end
161
+
162
+ context 'for invoice' do
163
+ before do
164
+ filenames.delete(:commercial_invoice)
165
+ @document = fedex.document(options)
166
+ end
167
+
168
+ it "saves label to file" do
169
+ expect(File).to exist(filenames[:label])
170
+ end
171
+ end
172
+ end
173
+
174
+ end
175
+
176
+ end
177
+ end