binarylogic-shippinglogic 0.9.0 → 1.0.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.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]
|