binarylogic-shippinglogic 0.9.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +29 -16
- data/VERSION.yml +2 -2
- data/lib/shippinglogic/fedex.rb +18 -3
- data/lib/shippinglogic/fedex/attributes.rb +3 -3
- data/lib/shippinglogic/fedex/cancel.rb +47 -0
- data/lib/shippinglogic/fedex/error.rb +23 -13
- data/lib/shippinglogic/fedex/{rates.rb → rate.rb} +99 -102
- data/lib/shippinglogic/fedex/request.rb +43 -1
- data/lib/shippinglogic/fedex/response.rb +1 -1
- data/lib/shippinglogic/fedex/service.rb +63 -2
- data/lib/shippinglogic/fedex/ship.rb +227 -0
- data/lib/shippinglogic/fedex/signature.rb +66 -0
- data/lib/shippinglogic/fedex/track.rb +16 -4
- data/shippinglogic.gemspec +96 -0
- data/spec/fedex/attributes_spec.rb +4 -4
- data/spec/fedex/cancel_spec.rb +10 -0
- data/spec/fedex/{rates_spec.rb → rate_spec.rb} +4 -3
- data/spec/fedex/service_spec.rb +5 -0
- data/spec/fedex/ship_spec.rb +22 -0
- data/spec/fedex/signature_spec.rb +10 -0
- data/spec/fedex/validation_spec.rb +1 -1
- data/spec/{fedex_spec_no.rb → fedex_spec.rb} +0 -0
- data/spec/lib/interceptor.rb +1 -1
- data/spec/responses/basic_ship.xml +7 -0
- data/spec/responses/basic_signature.xml +7 -0
- data/spec/responses/cancel_not_found.xml +7 -0
- data/spec/spec_helper.rb +8 -4
- metadata +20 -7
@@ -5,7 +5,7 @@ module Shippinglogic
|
|
5
5
|
private
|
6
6
|
# Convenience method for sending requests to FedEx
|
7
7
|
def request(body)
|
8
|
-
|
8
|
+
real_class.post(base.options[:test] ? base.options[:test_url] : base.options[:production_url], :body => body)
|
9
9
|
end
|
10
10
|
|
11
11
|
# Convenience method to create a builder object so that our builder options are consistent across
|
@@ -42,6 +42,48 @@ module Shippinglogic
|
|
42
42
|
b.Minor minor
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
# A convenience method for building the contact block in your XML request
|
47
|
+
def build_contact(b, type)
|
48
|
+
b.Contact do
|
49
|
+
b.Contact send("#{type}_name") if send("#{type}_name")
|
50
|
+
b.CompanyName send("#{type}_company_name") if send("#{type}_company_name")
|
51
|
+
b.PhoneNumber send("#{type}_phone_number") if send("#{type}_phone_number")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# A convenience method for building the address block in your XML request
|
56
|
+
def build_address(b, type)
|
57
|
+
b.Address do
|
58
|
+
b.StreetLines send("#{type}_streets") if send("#{type}_streets")
|
59
|
+
b.City send("#{type}_city") if send("#{type}_city")
|
60
|
+
b.StateOrProvinceCode send("#{type}_state") if send("#{type}_state")
|
61
|
+
b.PostalCode send("#{type}_postal_code") if send("#{type}_postal_code")
|
62
|
+
b.CountryCode send("#{type}_country") if send("#{type}_country")
|
63
|
+
b.Residential send("#{type}_residential")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# A convenience method for building the package block in your XML request
|
68
|
+
def build_package(b)
|
69
|
+
b.PackageCount package_count
|
70
|
+
|
71
|
+
b.RequestedPackages do
|
72
|
+
b.SequenceNumber 1
|
73
|
+
|
74
|
+
b.Weight do
|
75
|
+
b.Units package_weight_units
|
76
|
+
b.Value package_weight
|
77
|
+
end
|
78
|
+
|
79
|
+
b.Dimensions do
|
80
|
+
b.Length package_length
|
81
|
+
b.Width package_width
|
82
|
+
b.Height package_height
|
83
|
+
b.Units package_dimension_units
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
45
87
|
end
|
46
88
|
end
|
47
89
|
end
|
@@ -16,7 +16,7 @@ module Shippinglogic
|
|
16
16
|
|
17
17
|
# Was the response a success?
|
18
18
|
def success?(response)
|
19
|
-
response.is_a?(Hash) && ["SUCCESS", "NOTE"].include?(response[:highest_severity])
|
19
|
+
response.is_a?(Hash) && ["SUCCESS", "NOTE", "WARNING"].include?(response[:highest_severity])
|
20
20
|
end
|
21
21
|
|
22
22
|
# Cleans the response and returns it in a more 'user friendly' format that is easier
|
@@ -6,7 +6,8 @@ require "shippinglogic/fedex/validation"
|
|
6
6
|
module Shippinglogic
|
7
7
|
class FedEx
|
8
8
|
class Service
|
9
|
-
|
9
|
+
alias_method :real_class, :class
|
10
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|^real_class$|^send$|^object_id$)/ }
|
10
11
|
|
11
12
|
include Attributes
|
12
13
|
include HTTParty
|
@@ -14,6 +15,65 @@ module Shippinglogic
|
|
14
15
|
include Response
|
15
16
|
include Validation
|
16
17
|
|
18
|
+
# These constants aren't referenced or used anywhere in this library, they are purely for reference and as a tool for your to use.
|
19
|
+
# Many of the options that these services accept require a value from one of these arrays. The docs will specify which
|
20
|
+
# constant to use. You also might want to include these in a drop down on your interface.
|
21
|
+
#
|
22
|
+
# Lastly, if you want to make these user friendly use a string inflector (humanize or titlize).
|
23
|
+
|
24
|
+
# label options
|
25
|
+
LABEL_FORMATS = ["COMMON2D", "ERROR", "LABEL_DATA_ONLY", "MAILROOM", "NO_LABEL"]
|
26
|
+
LABEL_FILE_TYPES = ["DIB", "DPL", "EPL2", "GIF", "PDF", "PNG", "ZPLII"]
|
27
|
+
LABEL_STOCK_TYPES = [
|
28
|
+
"PAPER_4X6", "PAPER_4X8", "PAPER_4X9", "PAPER_7X4.75", "PAPER_8.5X11_BOTTOM_HALF_LABEL", "PAPER_8.5X11_TOP_HALF_LABEL",
|
29
|
+
"PAPER_LETTER", "STOCK_4X6", "STOCK_4X6.75_LEADING_DOC_TAB", "STOCK_4X6.75_TRAILING_DOC_TAB", "STOCK_4X8",
|
30
|
+
"STOCK_4X9_LEADING_DOC_TAB", "STOCK_4X9_TRAILING_DOC_TAB"
|
31
|
+
]
|
32
|
+
LABEL_MASK_OPTIONS = [
|
33
|
+
"CUSTOMS_VALUE", "DIMENSIONS", "DUTIES_AND_TAXES_PAYOR_ACCOUNT_NUMBER", "FREIGHT_PAYOR_ACCOUNT_NUMBER",
|
34
|
+
"PACKAGE_SEQUENCE_AND_COUNT", "SHIPPER_ACCOUNT_NUMBER", "SUPPLEMENTAL_LABEL_DOC_TAB", "TERMS_AND_CONDITIONS",
|
35
|
+
"TOTAL_WEIGHT", "TRANSPORTATION_CHARGES_PAYOR_ACCOUNT_NUMBER"
|
36
|
+
]
|
37
|
+
|
38
|
+
# service options
|
39
|
+
REGULAR_SERVICE_TYPES = [
|
40
|
+
"GROUND_HOME_DELIVERY", "FEDEX_GROUND", "FEDEX_EXPRESS_SAVER", "FEDEX_2_DAY", "STANDARD_OVERNIGHT",
|
41
|
+
"PRIORITY_OVERNIGHT", "FIRST_OVERNIGHT"
|
42
|
+
]
|
43
|
+
REGULAR_SATURDAY_SERVICE_TYPES = ["FEDEX_2_DAY_SATURDAY_DELIVERY", "PRIORITY_OVERNIGHT_SATURDAY_DELIVERY"]
|
44
|
+
FREIGHT_SERVICE_TYPES = ["FEDEX_3_DAY_FREIGHT", "FEDEX_2_DAY_FREIGHT", "FEDEX_1_DAY_FREIGHT"]
|
45
|
+
FREIGHT_SATURDAY_SERVICE_TYPES = [
|
46
|
+
"FEDEX_3_DAY_FREIGHT_SATURDAY_DELIVERY", "FEDEX_2_DAY_FREIGHT_SATURDAY_DELIVERY",
|
47
|
+
"FEDEX_1_DAY_FREIGHT_SATURDAY_DELIVERY"
|
48
|
+
]
|
49
|
+
INTERNATIONAL_SERVICE_TYPES = ["INTERNATIONAL_GROUND", "INTERNATIONAL_ECONOMY", "INTERNATIONAL_PRIORITY", "INTERNATIONAL_FIRST"]
|
50
|
+
INTERNATIONAL_SATURDAY_TYPES = ["INTERNATIONAL_PRIORITY_SATURDAY_DELIVERY"]
|
51
|
+
INTERNATIONA_FREIGHT_SERVICE_TYPES = ["INTERNATIONAL_ECONOMY_FREIGHT", "INTERNATIONAL_PRIORITY_FREIGHT"]
|
52
|
+
SERVICE_TYPES = REGULAR_SERVICE_TYPES + REGULAR_SATURDAY_SERVICE_TYPES + FREIGHT_SERVICE_TYPES + FREIGHT_SATURDAY_SERVICE_TYPES +
|
53
|
+
INTERNATIONAL_SERVICE_TYPES + INTERNATIONAL_SATURDAY_TYPES + INTERNATIONA_FREIGHT_SERVICE_TYPES
|
54
|
+
|
55
|
+
# delivery options
|
56
|
+
SIGNATURE_OPTION_TYPES = ["ADULT", "DIRECT", "INDIRECT", "NO_SIGNATURE_REQUIRED", "SERVICE_DEFAULT"]
|
57
|
+
SIGNATURE_IMAGE_TYPES = ["LETTER", "FAX"]
|
58
|
+
SPECIAL_SERVICES = [
|
59
|
+
"APPOINTMENT_DELIVERY", "DANGEROUS_GOODS", "DRY_ICE", "NON_STANDARD_CONTAINER", "PRIORITY_ALERT", "SIGNATURE_OPTION",
|
60
|
+
"FEDEX_FREIGHT", "FEDEX_NATIONAL_FREIGHT", "INSIDE_PICKUP", "INSIDE_DELIVERY", "EXHIBITION", "EXTREME_LENGTH", "FLATBED_TRAILER",
|
61
|
+
"FREIGHT_GUARANTEE", "LIFTGATE_DELIVERY", "LIFTGATE_PICKUP", "LIMITED_ACCESS_DELIVERY", "LIMITED_ACCESS_PICKUP", "PRE_DELIVERY_NOTIFICATION",
|
62
|
+
"PROTECTION_FROM_FREEZING", "REGIONAL_MALL_DELIVERY", "REGIONAL_MALL_PICKUP"
|
63
|
+
]
|
64
|
+
|
65
|
+
# misc options
|
66
|
+
DELETION_CONTROL = ["DELETE_ALL_PACKAGES", "DELETE_ONE_PACKAGE", "LEGACY"]
|
67
|
+
EMAIL_TYPES = ["HTML", "TEXT", "WIRELESS"]
|
68
|
+
PAYMENT_TYPES = ["SENDER", "CASH", "CREDIT_CARD"]
|
69
|
+
REFERENCE_TYPES = [
|
70
|
+
"BILL_OF_LADING", "CUSTOMER_REFERENCE", "DEPARTMENT_NUMBER", "INVOICE_NUMER", "P_O_NUMBER",
|
71
|
+
"SHIPMENT_INTEGRITY", "STORE_NUMBER"
|
72
|
+
]
|
73
|
+
PACKAGE_TYPES = ["FEDEX_ENVELOPE", "FEDEX_PAK", "FEDEX_BOX", "FEDEX_TUBE", "FEDEX_10KG_BOX", "FEDEX_25KG_BOX", "YOUR_PACKAGING"]
|
74
|
+
DROP_OFF_TYPES = ["REGULAR_PICKUP", "REQUEST_COURIER", "DROP_BOX", "BUSINESS_SERVICE_CENTER", "STATION"]
|
75
|
+
RATE_REQUEST_TYPES = ["ACCOUNT", "LIST", "MULTIWEIGHT"]
|
76
|
+
|
17
77
|
attr_accessor :base
|
18
78
|
|
19
79
|
# Accepts the base service object as a single parameter so that we can access
|
@@ -31,7 +91,8 @@ module Shippinglogic
|
|
31
91
|
end
|
32
92
|
|
33
93
|
# For each service you need to overwrite this method. This is where you make the call to fedex
|
34
|
-
# and do your magic.
|
94
|
+
# and do your magic. See the child classes for examples on how to define this method. It is very
|
95
|
+
# important that you cache the result into a variable to avoid uneccessary requests.
|
35
96
|
def target
|
36
97
|
raise ImplementationError.new("You need to implement a target method that the proxy class can delegate method calls to")
|
37
98
|
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
module Shippinglogic
|
2
|
+
class FedEx
|
3
|
+
# An interface to the shipe services provided by FedEx. Allows you to create shipments and get various information on the shipment
|
4
|
+
# created. Such as the tracking number, the label image, barcode image, delivery date, etc.
|
5
|
+
#
|
6
|
+
# == Options
|
7
|
+
# === Shipper options
|
8
|
+
#
|
9
|
+
# * <tt>shipper_streets</tt> - street part of the address, separate multiple streets with a new line, dont include blank lines.
|
10
|
+
# * <tt>shipper_city</tt> - city part of the address.
|
11
|
+
# * <tt>shipper_state_</tt> - state part of the address, use state abreviations.
|
12
|
+
# * <tt>shipper_postal_code</tt> - postal code part of the address. Ex: zip for the US.
|
13
|
+
# * <tt>shipper_country</tt> - country code part of the address, use abbreviations, ex: 'US'
|
14
|
+
# * <tt>shipper_residential</tt> - a boolean value representing if the address is redential or not (default: false)
|
15
|
+
#
|
16
|
+
# === Recipient options
|
17
|
+
#
|
18
|
+
# * <tt>recipient_streets</tt> - street part of the address, separate multiple streets with a new line, dont include blank lines.
|
19
|
+
# * <tt>recipient_city</tt> - city part of the address.
|
20
|
+
# * <tt>recipient_state</tt> - state part of the address, use state abreviations.
|
21
|
+
# * <tt>recipient_postal_code</tt> - postal code part of the address. Ex: zip for the US.
|
22
|
+
# * <tt>recipient_country</tt> - country code part of the address, use abbreviations, ex: 'US'
|
23
|
+
# * <tt>recipient_residential</tt> - a boolean value representing if the address is redential or not (default: false)
|
24
|
+
#
|
25
|
+
# === Label options
|
26
|
+
#
|
27
|
+
# * <tt>label_format</tt> - one of LABEL_FORMATS. (default: COMMON2D)
|
28
|
+
# * <tt>label_file_type</tt> - one of LABEL_FILE_TYPES. (default: PDF)
|
29
|
+
# * <tt>label_stock_type</tt> - one of LABEL_STOCK_TYPES. (default: PAPER_8.5X11_TOP_HALF_LABEL)
|
30
|
+
#
|
31
|
+
# === Packaging options
|
32
|
+
#
|
33
|
+
# One thing to note is that FedEx does support multiple package shipments. The problem is that all of the packages must be identical.
|
34
|
+
# FedEx specifically notes in their documentation that mutiple package specifications are not allowed. So your only option for a
|
35
|
+
# multi package shipment is to increase the package_count option and keep the dimensions and weight the same for all packages. Then again,
|
36
|
+
# the documentation for the FedEx web services is terrible, so I could be wrong. Any tests I tried resulted in an error though.
|
37
|
+
#
|
38
|
+
# * <tt>packaging_type</tt> - one of PACKAGE_TYPES. (default: YOUR_PACKAGING)
|
39
|
+
# * <tt>package_count</tt> - the number of packages in your shipment. (default: 1)
|
40
|
+
# * <tt>package_weight</tt> - a single packages weight.
|
41
|
+
# * <tt>package_weight_units</tt> - either LB or KG. (default: LB)
|
42
|
+
# * <tt>package_length</tt> - a single packages length.
|
43
|
+
# * <tt>package_width</tt> - a single packages width.
|
44
|
+
# * <tt>package_height</tt> - a single packages height.
|
45
|
+
# * <tt>package_dimension_units</tt> - either IN or CM. (default: IN)
|
46
|
+
#
|
47
|
+
# === Monetary options
|
48
|
+
#
|
49
|
+
# * <tt>currency_type</tt> - the type of currency. (default: nil, because FedEx will default to your account preferences)
|
50
|
+
# * <tt>insured_value</tt> - the value you want to insure, if any. (default: nil)
|
51
|
+
# * <tt>payment_type</tt> - one of PAYMENT_TYPES. (default: SENDER)
|
52
|
+
# * <tt>payor_account_number</tt> - if the account paying for this ship is different than the account you specified then
|
53
|
+
# you can specify that here. (default: your account number)
|
54
|
+
# * <tt>payor_country</tt> - the country code for the account number. (default: US)
|
55
|
+
#
|
56
|
+
# === Delivery options
|
57
|
+
#
|
58
|
+
# * <tt>ship_time</tt> - a Time object representing when you want to ship the package. (default: Time.now)
|
59
|
+
# * <tt>service_type</tt> - one of SERVICE_TYPES, this is optional, leave this blank if you want a list of all
|
60
|
+
# available services. (default: nil)
|
61
|
+
# * <tt>dropoff_type</tt> - one of DROP_OFF_TYPES. (default: REGULAR_PICKUP)
|
62
|
+
# * <tt>special_services_requested</tt> - any exceptions or special services FedEx needs to be aware of, this should be
|
63
|
+
# one or more of SPECIAL_SERVICES. (default: nil)
|
64
|
+
# * <tt>signature</tt> - one of SIGNATURE_OPTION_TYPES. (default: nil, which defaults to the service default)
|
65
|
+
#
|
66
|
+
# === Misc options
|
67
|
+
#
|
68
|
+
# * <tt>rate_request_types</tt> - one or more of RATE_REQUEST_TYPES. (default: ACCOUNT)
|
69
|
+
#
|
70
|
+
# == Simple Example
|
71
|
+
#
|
72
|
+
# Here is a very simple example. Mix and match the options above to get more accurate rates:
|
73
|
+
#
|
74
|
+
# fedex = Shippinglogic::FedEx.new(key, password, account, meter)
|
75
|
+
# shipment = fedex.ship(
|
76
|
+
# :shipper_postal_code => "10007",
|
77
|
+
# :shipper_country => "US",
|
78
|
+
# :recipient_postal_code => "75201",
|
79
|
+
# :recipient_country_code => "US",
|
80
|
+
# :package_weight => 24,
|
81
|
+
# :package_length => 12,
|
82
|
+
# :package_width => 12,
|
83
|
+
# :package_height => 12
|
84
|
+
# )
|
85
|
+
#
|
86
|
+
# shipment.inspect
|
87
|
+
# #<Shippinglogic::FedEx::Ship::Shipment rate:big_decimal, currency:string, delivery_date:date, tracking_number:string,
|
88
|
+
# label:string(base64 decoded), barcode:string(base64 decoded) >
|
89
|
+
#
|
90
|
+
# # to show accessor methods
|
91
|
+
# shipment.tracking_number
|
92
|
+
# # => "XXXXXXXXXXXXXX"
|
93
|
+
class Ship < Service
|
94
|
+
# The shipment result is an object of this class
|
95
|
+
class Shipment; attr_accessor :rate, :currency, :delivery_date, :tracking_number, :label, :barcode; end
|
96
|
+
|
97
|
+
VERSION = {:major => 6, :intermediate => 0, :minor => 0}
|
98
|
+
|
99
|
+
# shipper options
|
100
|
+
attribute :shipper_name, :string
|
101
|
+
attribute :shipper_company_name, :string
|
102
|
+
attribute :shipper_phone_number, :string
|
103
|
+
attribute :shipper_streets, :string
|
104
|
+
attribute :shipper_city, :string
|
105
|
+
attribute :shipper_state, :string
|
106
|
+
attribute :shipper_postal_code, :string
|
107
|
+
attribute :shipper_country, :string
|
108
|
+
attribute :shipper_residential, :boolean, :default => false
|
109
|
+
|
110
|
+
# recipient options
|
111
|
+
attribute :recipient_name, :string
|
112
|
+
attribute :recipient_company_name, :string
|
113
|
+
attribute :recipient_phone_number, :string
|
114
|
+
attribute :recipient_streets, :string
|
115
|
+
attribute :recipient_city, :string
|
116
|
+
attribute :recipient_state, :string
|
117
|
+
attribute :recipient_postal_code, :string
|
118
|
+
attribute :recipient_country, :string
|
119
|
+
attribute :recipient_residential, :boolean, :default => false
|
120
|
+
|
121
|
+
# label options
|
122
|
+
attribute :label_format, :string, :default => "COMMON2D"
|
123
|
+
attribute :label_file_type, :string, :default => "PDF"
|
124
|
+
attribute :label_stock_type, :string, :default => "PAPER_8.5X11_TOP_HALF_LABEL"
|
125
|
+
|
126
|
+
# packaging options
|
127
|
+
attribute :packaging_type, :string, :default => "YOUR_PACKAGING"
|
128
|
+
attribute :packaging_type, :string, :default => "YOUR_PACKAGING"
|
129
|
+
attribute :package_count, :integer, :default => 1
|
130
|
+
attribute :package_weight, :integer
|
131
|
+
attribute :package_weight_units, :string, :default => "LB"
|
132
|
+
attribute :package_length, :integer
|
133
|
+
attribute :package_width, :integer
|
134
|
+
attribute :package_height, :integer
|
135
|
+
attribute :package_dimension_units, :string, :default => "IN"
|
136
|
+
|
137
|
+
# monetary options
|
138
|
+
attribute :currency_type, :string
|
139
|
+
attribute :insured_value, :big_decimal
|
140
|
+
attribute :payment_type, :string, :default => "SENDER"
|
141
|
+
attribute :payor_account_number, :string, :default => lambda { |shipment| shipment.base.account }
|
142
|
+
attribute :payor_country, :string
|
143
|
+
|
144
|
+
# delivery options
|
145
|
+
attribute :ship_time, :datetime, :default => lambda { |shipment| Time.now }
|
146
|
+
attribute :service_type, :string
|
147
|
+
attribute :dropoff_type, :string, :default => "REGULAR_PICKUP"
|
148
|
+
attribute :special_services_requested, :array
|
149
|
+
attribute :signature, :string
|
150
|
+
|
151
|
+
# misc options
|
152
|
+
attribute :rate_request_types, :array, :default => ["ACCOUNT"]
|
153
|
+
|
154
|
+
private
|
155
|
+
def target
|
156
|
+
@target ||= parse_response(request(build_request))
|
157
|
+
end
|
158
|
+
|
159
|
+
# Just building some XML to send off to FedEx using our various options
|
160
|
+
def build_request
|
161
|
+
b = builder
|
162
|
+
xml = b.ProcessShipmentRequest(:xmlns => "http://fedex.com/ws/ship/v#{VERSION[:major]}") do
|
163
|
+
build_authentication(b)
|
164
|
+
build_version(b, "ship", VERSION[:major], VERSION[:intermediate], VERSION[:minor])
|
165
|
+
b.SpecialServicesRequested special_services_requested.join(",") if special_services_requested.any?
|
166
|
+
|
167
|
+
b.RequestedShipment do
|
168
|
+
b.ShipTimestamp ship_time.xmlschema if ship_time
|
169
|
+
b.DropoffType dropoff_type if dropoff_type
|
170
|
+
b.ServiceType service_type if service_type
|
171
|
+
b.PackagingType packaging_type if packaging_type
|
172
|
+
b.TotalInsuredValue insured_value if insured_value
|
173
|
+
|
174
|
+
b.Shipper do
|
175
|
+
build_contact(b, :shipper)
|
176
|
+
build_address(b, :shipper)
|
177
|
+
end
|
178
|
+
|
179
|
+
b.Recipient do
|
180
|
+
build_contact(b, :recipient)
|
181
|
+
build_address(b, :recipient)
|
182
|
+
end
|
183
|
+
|
184
|
+
b.ShippingChargesPayment do
|
185
|
+
b.PaymentType payment_type if payment_type
|
186
|
+
b.Payor do
|
187
|
+
b.AccountNumber payor_account_number if payor_account_number
|
188
|
+
b.CountryCode payor_country if payor_country
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
b.LabelSpecification do
|
193
|
+
b.LabelFormatType label_format if label_format
|
194
|
+
b.ImageType label_file_type if label_file_type
|
195
|
+
b.LabelStockType label_stock_type if label_stock_type
|
196
|
+
end
|
197
|
+
|
198
|
+
if signature
|
199
|
+
b.SignatureOptionDetail do
|
200
|
+
b.OptionType signature
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
b.RateRequestTypes rate_request_types.join(",")
|
205
|
+
build_package(b)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Making sense of the reponse and grabbing the information we need.
|
211
|
+
def parse_response(response)
|
212
|
+
details = response[:completed_shipment_detail]
|
213
|
+
rate = details[:shipment_rating][:shipment_rate_details].first[:total_net_charge]
|
214
|
+
package_details = details[:completed_package_details]
|
215
|
+
|
216
|
+
shipment = Shipment.new
|
217
|
+
shipment.rate = BigDecimal.new(rate[:amount])
|
218
|
+
shipment.currency = rate[:currency]
|
219
|
+
shipment.delivery_date = Date.parse(details[:routing_detail][:delivery_date])
|
220
|
+
shipment.tracking_number = package_details[:tracking_id][:tracking_number]
|
221
|
+
shipment.label = Base64.decode64(package_details[:label][:parts][:image])
|
222
|
+
shipment.barcode = Base64.decode64(package_details[:barcodes][:common2_d_barcode])
|
223
|
+
shipment
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Shippinglogic
|
2
|
+
class FedEx
|
3
|
+
# An interface to the signature proof of delivery services provided by FedEx. Allows you to get an image
|
4
|
+
# of the signature, or you can tell fedex to fax it to a fax number.
|
5
|
+
#
|
6
|
+
# == Accessor methods / options
|
7
|
+
#
|
8
|
+
# * <tt>tracking_number</tt> - the tracking number
|
9
|
+
# * <tt>image_type</tt> - one of SIGNATURE_IMAGE_TYPES. (default: LETTER)
|
10
|
+
# * <tt>image_file_type</tt> - one of LABEL_FILE_TYPES. (default: PDF)
|
11
|
+
# * <tt>fax_number</tt> - if image_type is set to FAX you must provide a fax number here. (default: nil)
|
12
|
+
#
|
13
|
+
# === Simple Example
|
14
|
+
#
|
15
|
+
# Here is a very simple example:
|
16
|
+
#
|
17
|
+
# fedex = Shippinglogic::FedEx.new(key, password, account, meter)
|
18
|
+
# signature = fedex.signature(:tracking_number => "my number")
|
19
|
+
#
|
20
|
+
# signature.inspect
|
21
|
+
# # => #<Shippinglogic::FedEx::Signature::Signature image:string(base64 decoded) >
|
22
|
+
#
|
23
|
+
# signature.image
|
24
|
+
# # => "a bunch of garble (write this to a file and save it)"
|
25
|
+
class Signature < Service
|
26
|
+
# Each tracking result is an object of this class
|
27
|
+
class Signature; attr_accessor :image; end
|
28
|
+
|
29
|
+
VERSION = {:major => 3, :intermediate => 0, :minor => 0}
|
30
|
+
|
31
|
+
attribute :image_type, :string, :default => "LETTER"
|
32
|
+
attribute :image_file_type, :string, :default => "PDF"
|
33
|
+
attribute :tracking_number, :string
|
34
|
+
attribute :fax_number, :string
|
35
|
+
|
36
|
+
private
|
37
|
+
# The parent class Service requires that we define this method. This is our kicker. This method is only
|
38
|
+
# called when we need to deal with information from FedEx. Notice the caching into the @target variable.
|
39
|
+
def target
|
40
|
+
@target ||= parse_response(request(build_request))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Just building some XML to send off to FedEx. FedEx require this particualr format.
|
44
|
+
def build_request
|
45
|
+
b = builder
|
46
|
+
xml = b.SignatureProofOfDeliveryRequest(:xmlns => "http://fedex.com/ws/track/v#{VERSION[:major]}") do
|
47
|
+
build_authentication(b)
|
48
|
+
build_version(b, "trck", VERSION[:major], VERSION[:intermediate], VERSION[:minor])
|
49
|
+
|
50
|
+
b.Type image_type
|
51
|
+
b.TrackingNumber tracking_number
|
52
|
+
b.FaxDetail fax_number if fax_number
|
53
|
+
b.LetterFormat image_file_type
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Grabbing the response from FedEx and making sense of it. There is a lot of unneeded information
|
58
|
+
# in the response.
|
59
|
+
def parse_response(response)
|
60
|
+
signature = Signature.new
|
61
|
+
signature.image = Base64.decode64(response[:letter])
|
62
|
+
signature
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -12,9 +12,16 @@ module Shippinglogic
|
|
12
12
|
# Here is a very simple example:
|
13
13
|
#
|
14
14
|
# fedex = Shippinglogic::FedEx.new(key, password, account, meter)
|
15
|
-
# fedex.track(:tracking_number => "my number")
|
15
|
+
# tracking = fedex.track(:tracking_number => "my number")
|
16
|
+
# tracking.first
|
17
|
+
# # => #<Shippinglogic::FedEx::Track::Event @postal_code="95817", @name="Delivered", @state="CA", @residential=false,
|
18
|
+
# # @city="Sacramento", @type="DL", @country="US", @occured_at=Mon Dec 08 10:43:37 -0500 2008>
|
19
|
+
#
|
20
|
+
# tracking.first.name
|
21
|
+
# # => "Delivered"
|
16
22
|
#
|
17
23
|
# === Note
|
24
|
+
#
|
18
25
|
# FedEx does support locating packages through means other than a tracking number.
|
19
26
|
# These are not supported and probably won't be until someone needs them. It should
|
20
27
|
# be fairly simple to add, but I could not think of a reason why anyone would want to track
|
@@ -28,11 +35,14 @@ module Shippinglogic
|
|
28
35
|
attribute :tracking_number, :string
|
29
36
|
|
30
37
|
private
|
38
|
+
# The parent class Service requires that we define this method. This is our kicker. This method is only
|
39
|
+
# called when we need to deal with information from FedEx. Notice the caching into the @target variable.
|
31
40
|
def target
|
32
|
-
@target ||=
|
41
|
+
@target ||= parse_response(request(build_request))
|
33
42
|
end
|
34
43
|
|
35
|
-
|
44
|
+
# Just building some XML to send off to FedEx. FedEx require this particualr format.
|
45
|
+
def build_request
|
36
46
|
b = builder
|
37
47
|
xml = b.TrackRequest(:xmlns => "http://fedex.com/ws/track/v#{VERSION[:major]}") do
|
38
48
|
build_authentication(b)
|
@@ -47,7 +57,9 @@ module Shippinglogic
|
|
47
57
|
end
|
48
58
|
end
|
49
59
|
|
50
|
-
|
60
|
+
# Grabbing the response from FedEx and making sense of it. There is a lot of unneeded information
|
61
|
+
# in the response.
|
62
|
+
def parse_response(response)
|
51
63
|
response[:track_details][:events].collect do |details|
|
52
64
|
event = Event.new
|
53
65
|
event.name = details[:event_description]
|