shipping 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +158 -3
- data/lib/shipping.rb +3 -0
- data/lib/shipping/base.rb +4 -2
- data/lib/shipping/fedex.rb +207 -82
- data/test/fedex/fedex_test.rb +4 -3
- data/test/ups/ups_test.rb +1 -1
- metadata +2 -2
data/Rakefile
CHANGED
@@ -4,8 +4,14 @@ require 'rake/testtask'
|
|
4
4
|
require 'rake/rdoctask'
|
5
5
|
require 'rake/gempackagetask'
|
6
6
|
require 'rake/contrib/rubyforgepublisher'
|
7
|
+
require File.dirname(__FILE__) + '/lib/shipping'
|
7
8
|
|
8
|
-
PKG_VERSION =
|
9
|
+
PKG_VERSION = Shipping::VERSION
|
10
|
+
PKG_NAME = "shipping"
|
11
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
12
|
+
RUBY_FORGE_PROJECT = "shipping"
|
13
|
+
RUBY_FORGE_USER = ENV['RUBY_FORGE_USER'] || "cardmagic"
|
14
|
+
RELEASE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
9
15
|
|
10
16
|
PKG_FILES = FileList[
|
11
17
|
"lib/**/*", "bin/*", "test/**/*", "[A-Z]*", "Rakefile", "doc/**/*"
|
@@ -90,7 +96,156 @@ end
|
|
90
96
|
|
91
97
|
desc "Publish new documentation"
|
92
98
|
task :publish do
|
93
|
-
`ssh rufy update-shipping-doc`
|
94
|
-
`scp pkg/shipping-1.0.0.* rufy:www/shipping.rufy.com/docs/`
|
95
99
|
Rake::RubyForgePublisher.new('shipping', 'cardmagic').upload
|
100
|
+
`ssh rufy update-shipping-doc`
|
101
|
+
end
|
102
|
+
|
103
|
+
desc "Publish the release files to RubyForge."
|
104
|
+
task :upload => [:package] do
|
105
|
+
files = ["gem", "tgz", "zip"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
|
106
|
+
|
107
|
+
if RUBY_FORGE_PROJECT then
|
108
|
+
require 'net/http'
|
109
|
+
require 'open-uri'
|
110
|
+
|
111
|
+
project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
|
112
|
+
project_data = open(project_uri) { |data| data.read }
|
113
|
+
group_id = project_data[/[?&]group_id=(\d+)/, 1]
|
114
|
+
raise "Couldn't get group id" unless group_id
|
115
|
+
|
116
|
+
# This echos password to shell which is a bit sucky
|
117
|
+
if ENV["RUBY_FORGE_PASSWORD"]
|
118
|
+
password = ENV["RUBY_FORGE_PASSWORD"]
|
119
|
+
else
|
120
|
+
password = Proc.new do
|
121
|
+
sync = STDOUT.sync
|
122
|
+
begin
|
123
|
+
echo false
|
124
|
+
STDOUT.sync = true
|
125
|
+
print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
|
126
|
+
STDIN.gets.chomp
|
127
|
+
ensure
|
128
|
+
echo true
|
129
|
+
STDOUT.sync = sync
|
130
|
+
puts
|
131
|
+
end
|
132
|
+
end.call
|
133
|
+
end
|
134
|
+
|
135
|
+
login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
136
|
+
data = [
|
137
|
+
"login=1",
|
138
|
+
"form_loginname=#{RUBY_FORGE_USER}",
|
139
|
+
"form_pw=#{password}"
|
140
|
+
].join("&")
|
141
|
+
http.post("/account/login.php", data)
|
142
|
+
end
|
143
|
+
|
144
|
+
cookie = login_response["set-cookie"]
|
145
|
+
raise "Login failed" unless cookie
|
146
|
+
headers = { "Cookie" => cookie }
|
147
|
+
|
148
|
+
release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
|
149
|
+
release_data = open(release_uri, headers) { |data| data.read }
|
150
|
+
package_id = release_data[/[?&]package_id=(\d+)/, 1]
|
151
|
+
raise "Couldn't get package id" unless package_id
|
152
|
+
|
153
|
+
first_file = true
|
154
|
+
release_id = ""
|
155
|
+
|
156
|
+
files.each do |filename|
|
157
|
+
basename = File.basename(filename)
|
158
|
+
file_ext = File.extname(filename)
|
159
|
+
file_data = File.open(filename, "rb") { |file| file.read }
|
160
|
+
|
161
|
+
puts "Releasing #{basename}..."
|
162
|
+
|
163
|
+
release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
164
|
+
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
|
165
|
+
type_map = {
|
166
|
+
".zip" => "3000",
|
167
|
+
".tgz" => "3110",
|
168
|
+
".gz" => "3110",
|
169
|
+
".gem" => "1400"
|
170
|
+
}; type_map.default = "9999"
|
171
|
+
type = type_map[file_ext]
|
172
|
+
boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
|
173
|
+
|
174
|
+
query_hash = if first_file then
|
175
|
+
{
|
176
|
+
"group_id" => group_id,
|
177
|
+
"package_id" => package_id,
|
178
|
+
"release_name" => RELEASE_NAME,
|
179
|
+
"release_date" => release_date,
|
180
|
+
"type_id" => type,
|
181
|
+
"processor_id" => "8000", # Any
|
182
|
+
"release_notes" => "",
|
183
|
+
"release_changes" => "",
|
184
|
+
"preformatted" => "1",
|
185
|
+
"submit" => "1"
|
186
|
+
}
|
187
|
+
else
|
188
|
+
{
|
189
|
+
"group_id" => group_id,
|
190
|
+
"release_id" => release_id,
|
191
|
+
"package_id" => package_id,
|
192
|
+
"step2" => "1",
|
193
|
+
"type_id" => type,
|
194
|
+
"processor_id" => "8000", # Any
|
195
|
+
"submit" => "Add This File"
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
query = "?" + query_hash.map do |(name, value)|
|
200
|
+
[name, URI.encode(value)].join("=")
|
201
|
+
end.join("&")
|
202
|
+
|
203
|
+
data = [
|
204
|
+
"--" + boundary,
|
205
|
+
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
|
206
|
+
"Content-Type: application/octet-stream",
|
207
|
+
"Content-Transfer-Encoding: binary",
|
208
|
+
"", file_data, ""
|
209
|
+
].join("\x0D\x0A")
|
210
|
+
|
211
|
+
release_headers = headers.merge(
|
212
|
+
"Content-Type" => "multipart/form-data; boundary=#{boundary}"
|
213
|
+
)
|
214
|
+
|
215
|
+
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
|
216
|
+
http.post(target + query, data, release_headers)
|
217
|
+
end
|
218
|
+
|
219
|
+
if first_file then
|
220
|
+
release_id = release_response.body[/release_id=(\d+)/, 1]
|
221
|
+
raise("Couldn't get release id") unless release_id
|
222
|
+
end
|
223
|
+
|
224
|
+
first_file = false
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
begin
|
230
|
+
if !defined?(USE_TERMIOS) || USE_TERMIOS
|
231
|
+
require 'termios'
|
232
|
+
else
|
233
|
+
raise LoadError
|
234
|
+
end
|
235
|
+
|
236
|
+
# Enable or disable stdin echoing to the terminal.
|
237
|
+
def echo(enable)
|
238
|
+
term = Termios::getattr(STDIN)
|
239
|
+
|
240
|
+
if enable
|
241
|
+
term.c_lflag |= (Termios::ECHO | Termios::ICANON)
|
242
|
+
else
|
243
|
+
term.c_lflag &= ~Termios::ECHO
|
244
|
+
end
|
245
|
+
|
246
|
+
Termios::setattr(STDIN, Termios::TCSANOW, term)
|
247
|
+
end
|
248
|
+
rescue LoadError
|
249
|
+
def echo(enable)
|
250
|
+
end
|
96
251
|
end
|
data/lib/shipping.rb
CHANGED
@@ -24,6 +24,8 @@
|
|
24
24
|
# Copyright:: Copyright (c) 2005 Lucas Carlson
|
25
25
|
# License:: LGPL
|
26
26
|
|
27
|
+
$:.unshift(File.dirname(__FILE__))
|
28
|
+
|
27
29
|
begin
|
28
30
|
require 'rubygems'
|
29
31
|
rescue LoadError
|
@@ -35,6 +37,7 @@ require 'yaml'
|
|
35
37
|
require 'rexml/document'
|
36
38
|
require 'net/http'
|
37
39
|
require 'net/https'
|
40
|
+
require 'base64'
|
38
41
|
|
39
42
|
require 'extensions'
|
40
43
|
require 'shipping/base'
|
data/lib/shipping/base.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
# License:: LGPL
|
4
4
|
|
5
5
|
module Shipping
|
6
|
+
VERSION = "1.3.0"
|
7
|
+
|
6
8
|
class ShippingError < StandardError; end
|
7
9
|
|
8
10
|
class Base
|
@@ -11,13 +13,13 @@ module Shipping
|
|
11
13
|
attr_writer :ups_account, :ups_user, :ups_password
|
12
14
|
attr_writer :fedex_account, :fedex_meter, :fedex_url
|
13
15
|
|
14
|
-
attr_accessor :name, :phone, :email, :address, :address2, :city, :state, :zip, :country
|
16
|
+
attr_accessor :name, :phone, :company, :email, :address, :address2, :city, :state, :zip, :country
|
15
17
|
attr_accessor :sender_name, :sender_phone, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip, :sender_country
|
16
18
|
|
17
19
|
attr_accessor :weight, :weight_units, :insured_value, :declared_value, :transaction_type, :description
|
18
20
|
attr_accessor :package_total, :packaging_type, :service_type
|
19
21
|
|
20
|
-
|
22
|
+
attr_accessor :ship_date, :dropoff_type, :pay_type, :currency_code, :image_type, :label_type
|
21
23
|
|
22
24
|
def initialize(options = {})
|
23
25
|
prefs = File.expand_path(options[:prefs] || "~/.shipping.yml")
|
data/lib/shipping/fedex.rb
CHANGED
@@ -6,22 +6,24 @@
|
|
6
6
|
# See http://www.fedex.com/us/solutions/wis/pdf/xml_transguide.pdf?link=4 for the full XML-based API
|
7
7
|
|
8
8
|
module Shipping
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
class FedEx < Base
|
10
|
+
# Gets the list price the regular consumer would have to pay. Discount price is what the
|
11
|
+
# person with this particular account number will pay
|
12
|
+
def price
|
13
|
+
get_price
|
14
|
+
return REXML::XPath.first(@response, "//FDXRateReply/EstimatedCharges/ListCharges/NetCharge").text.to_f
|
15
|
+
rescue
|
16
|
+
raise ShippingError, get_error
|
17
|
+
end
|
18
|
+
|
19
|
+
# Gets the discount price of the shipping (with discounts taken into consideration).
|
20
|
+
# "base price" doesn't include surcharges like fuel cost, so I don't think it is the correct price to get
|
21
|
+
def discount_price
|
22
|
+
get_price
|
23
|
+
return REXML::XPath.first(@response, "//FDXRateReply/EstimatedCharges/DiscountedCharges/NetCharge").text.to_f
|
24
|
+
rescue
|
25
|
+
raise ShippingError, get_error
|
26
|
+
end
|
25
27
|
|
26
28
|
# still not sure what the best way to handle this transaction's return data would be. Possibly a hash of the form {service => delivery_estimate}?
|
27
29
|
def express_service_availability
|
@@ -84,18 +86,159 @@ module Shipping
|
|
84
86
|
|
85
87
|
return REXML::XPath.first(@response, "//FDXSubscriptionReply/MeterNumber").text
|
86
88
|
end
|
87
|
-
|
89
|
+
|
88
90
|
# require 'fileutils'
|
89
91
|
# fedex = Shipping::FedEx.new :name => 'John Doe', ... , :sender_zip => 97202
|
90
92
|
# label = fedex.label
|
91
|
-
# puts label.url
|
92
93
|
# puts label.tracking_number
|
93
|
-
|
94
|
-
#
|
94
|
+
# FileUtils.cp label.image.path, '/path/to/my/images/directory/'
|
95
|
+
#
|
96
|
+
# There are several types of labels that can be returned by changing @image_type.
|
97
|
+
# PNG is selected by default.
|
95
98
|
#
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
+
def label
|
100
|
+
@required = [:phone, :email, :address, :city, :state, :zip ]
|
101
|
+
@required += [:sender_phone, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip ]
|
102
|
+
@required += [:fedex_account, :fedex_url]
|
103
|
+
|
104
|
+
@transaction_type ||= 'rate_ground'
|
105
|
+
@weight = (@weight.to_f*10).round/10.0
|
106
|
+
@declared_value = (@declared_value.to_f*100).round/100.0 unless @declared_value.blank?
|
107
|
+
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase
|
108
|
+
sender_state = STATES.has_value?(@sender_state.downcase) ? STATES.index(@sender_state.downcase).upcase : @sender_state.upcase
|
109
|
+
|
110
|
+
@data = String.new
|
111
|
+
|
112
|
+
b = Builder::XmlMarkup.new :target => @data
|
113
|
+
b.instruct!
|
114
|
+
b.FDXShipRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXShipRequest.xsd') { |b|
|
115
|
+
b.RequestHeader { |b|
|
116
|
+
b.AccountNumber @fedex_account
|
117
|
+
b.MeterNumber @fedex_meter
|
118
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
119
|
+
}
|
120
|
+
b.ShipDate((Time.now).strftime("%Y-%m-%d"))
|
121
|
+
b.ShipTime((Time.now).strftime("%H:%M:%S"))
|
122
|
+
b.DropoffType @dropoff_type || 'REGULARPICKUP'
|
123
|
+
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
124
|
+
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
125
|
+
b.WeightUnits @weight_units || 'LBS' # or KGS
|
126
|
+
b.Weight @weight
|
127
|
+
b.CurrencyCode @currency_code || 'USD'
|
128
|
+
b.Origin { |b|
|
129
|
+
b.Contact { |b|
|
130
|
+
if @sender_name.to_s.size > 2
|
131
|
+
b.PersonName @sender_name
|
132
|
+
b.CompanyName @sender_company unless @sender_company.blank?
|
133
|
+
elsif @sender_company.to_s.size > 2
|
134
|
+
b.PersonName @sender_name unless @sender_name.blank?
|
135
|
+
b.CompanyName @sender_company
|
136
|
+
else
|
137
|
+
raise ShippingError, "Either the sender_name or the sender_company value must be bigger than 2 characters."
|
138
|
+
end
|
139
|
+
b.Department @sender_department unless @sender_department.blank?
|
140
|
+
b.PhoneNumber @sender_phone.gsub(/[^\d]/,"")
|
141
|
+
b.PagerNumber @sender_pager.gsub(/[^\d]/,"") if @sender_pager.class == String
|
142
|
+
b.FaxNumber @sender_fax.gsub(/[^\d]/,"") if @sender_fax.class == String
|
143
|
+
b.tag! :"E-MailAddress", @sender_email
|
144
|
+
}
|
145
|
+
b.Address { |b|
|
146
|
+
b.Line1 @sender_address
|
147
|
+
b.Line2 @sender_address2 unless @sender_address2.blank?
|
148
|
+
b.City @sender_city
|
149
|
+
b.StateOrProvinceCode sender_state
|
150
|
+
b.PostalCode @sender_zip
|
151
|
+
b.CountryCode @sender_country || 'US'
|
152
|
+
}
|
153
|
+
}
|
154
|
+
b.Destination { |b|
|
155
|
+
b.Contact { |b|
|
156
|
+
if @name.to_s.size > 2
|
157
|
+
b.PersonName @name
|
158
|
+
b.CompanyName @company unless @company.blank?
|
159
|
+
elsif @company.to_s.size > 2
|
160
|
+
b.PersonName @name unless @name.blank?
|
161
|
+
b.CompanyName @company
|
162
|
+
else
|
163
|
+
raise ShippingError, "Either the name or the company value must be bigger than 2 characters."
|
164
|
+
end
|
165
|
+
b.Department @department unless @department.blank?
|
166
|
+
b.PhoneNumber @phone.gsub(/[^\d]/,"")
|
167
|
+
b.PagerNumber @pager.gsub(/[^\d]/,"") if @pager.class == String
|
168
|
+
b.FaxNumber @fax.gsub(/[^\d]/,"") if @fax.class == String
|
169
|
+
b.tag! :"E-MailAddress", @email
|
170
|
+
}
|
171
|
+
b.Address { |b|
|
172
|
+
b.Line1 @address
|
173
|
+
b.Line2 @address2 unless @address2.blank?
|
174
|
+
b.City @city
|
175
|
+
b.StateOrProvinceCode state
|
176
|
+
b.PostalCode @zip
|
177
|
+
b.CountryCode @country || 'US'
|
178
|
+
}
|
179
|
+
}
|
180
|
+
b.Payment { |b|
|
181
|
+
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
182
|
+
b.Payor { |b|
|
183
|
+
b.AccountNumber @payor_account_number
|
184
|
+
b.CountryCode @payor_country_code unless @payor_country_code.blank?
|
185
|
+
} unless @payor_account_number.blank?
|
186
|
+
}
|
187
|
+
b.RMA { |b|
|
188
|
+
b.Number @rma_number
|
189
|
+
} unless @rma_number.blank?
|
190
|
+
b.SpecialServices { |b|
|
191
|
+
b.EMailNotification { |b|
|
192
|
+
b.ShipAlertOptionalMessage @message
|
193
|
+
b.Shipper { |b|
|
194
|
+
b.ShipAlert @shipper_ship_alert ? 'true' : 'false'
|
195
|
+
b.LanguageCode @shipper_language || 'EN' # FR also available
|
196
|
+
}
|
197
|
+
b.Recipient { |b|
|
198
|
+
b.ShipAlert @recipient_ship_alert ? 'true' : 'false'
|
199
|
+
b.LanguageCode @recipient_language || 'EN' # FR also available
|
200
|
+
}
|
201
|
+
b.Other { |b|
|
202
|
+
b.tag! :"E-MailAddress", @other_email
|
203
|
+
b.ShipAlert @other_ship_alert ? 'true' : 'false'
|
204
|
+
b.LanguageCode @other_language || 'EN' # FR also available
|
205
|
+
} unless @other_email.blank?
|
206
|
+
}
|
207
|
+
} unless @message.blank?
|
208
|
+
b.Label { |b|
|
209
|
+
b.Type @label_type || '2DCOMMON'
|
210
|
+
b.ImageType @image_type || 'PNG'
|
211
|
+
}
|
212
|
+
}
|
213
|
+
get_response @fedex_url
|
214
|
+
|
215
|
+
begin
|
216
|
+
response = Hash.new
|
217
|
+
response[:tracking_number] = REXML::XPath.first(@response, "//FDXShipReply/Tracking/TrackingNumber").text
|
218
|
+
response[:encoded_image] = REXML::XPath.first(@response, "//FDXShipReply/Labels/OutboundLabel").text
|
219
|
+
response[:image] = Tempfile.new("shipping_label")
|
220
|
+
response[:image].write Base64.decode64( response[:encoded_image] )
|
221
|
+
response[:image].rewind
|
222
|
+
rescue
|
223
|
+
raise ShippingError, get_error
|
224
|
+
end
|
225
|
+
|
226
|
+
# allows for things like fedex.label.url
|
227
|
+
def response.method_missing(name, *args)
|
228
|
+
has_key?(name) ? self[name] : super
|
229
|
+
end
|
230
|
+
|
231
|
+
# don't allow people to edit the response
|
232
|
+
response.freeze
|
233
|
+
end
|
234
|
+
|
235
|
+
# require 'fileutils'
|
236
|
+
# fedex = Shipping::FedEx.new :name => 'John Doe', ... , :sender_zip => 97202
|
237
|
+
# label = fedex.email_label
|
238
|
+
# puts label.url
|
239
|
+
# puts label.tracking_number
|
240
|
+
#
|
241
|
+
def return_label
|
99
242
|
@required = [:phone, :email, :address, :city, :state, :zip ]
|
100
243
|
@required += [:sender_phone, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip ]
|
101
244
|
@required += [:fedex_account, :fedex_url, :weight ]
|
@@ -222,37 +365,18 @@ module Shipping
|
|
222
365
|
response[:userid] = REXML::XPath.first(@response, "//FDXEmailLabelReply/UserID").text
|
223
366
|
response[:password] = REXML::XPath.first(@response, "//FDXEmailLabelReply/Password").text
|
224
367
|
response[:tracking_number] = REXML::XPath.first(@response, "//FDXEmailLabelReply/Package/TrackingNumber").text
|
225
|
-
|
226
|
-
|
227
|
-
|
368
|
+
rescue
|
369
|
+
raise ShippingError, get_error
|
370
|
+
end
|
228
371
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
372
|
+
# allows for things like fedex.label.url
|
373
|
+
def response.method_missing(name, *args)
|
374
|
+
has_key?(name) ? self[name] : super
|
375
|
+
end
|
233
376
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
f = Tempfile.new response.password
|
238
|
-
uri = URI.parse response.url
|
239
|
-
|
240
|
-
http = Net::HTTP.new uri.host, uri.port
|
241
|
-
if uri.port == 443
|
242
|
-
http.use_ssl = true
|
243
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
244
|
-
end
|
245
|
-
req = Net::HTTP::Get.new(uri.path)
|
246
|
-
req.basic_auth response.userid, response.password
|
247
|
-
res = http.request(req)
|
248
|
-
f << res.body
|
249
|
-
f.seek(0)
|
250
|
-
response[:image] = f
|
251
|
-
=end
|
252
|
-
|
253
|
-
# don't allow people to edit the response
|
254
|
-
return response.freeze
|
255
|
-
end
|
377
|
+
# don't allow people to edit the response
|
378
|
+
return response.freeze
|
379
|
+
end
|
256
380
|
|
257
381
|
private
|
258
382
|
|
@@ -278,6 +402,7 @@ module Shipping
|
|
278
402
|
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
279
403
|
b.WeightUnits @weight_units || 'LBS'
|
280
404
|
b.Weight @weight
|
405
|
+
b.ListRate true #tells fedex to return list rates as well as discounted rates
|
281
406
|
b.OriginAddress { |b|
|
282
407
|
b.StateOrProvinceCode self.class.state_from_zip(@sender_zip)
|
283
408
|
b.PostalCode @sender_zip
|
@@ -297,14 +422,14 @@ module Shipping
|
|
297
422
|
get_response @fedex_url
|
298
423
|
end
|
299
424
|
|
300
|
-
|
301
|
-
|
425
|
+
def get_error
|
426
|
+
return if @response.class != REXML::Document
|
302
427
|
|
303
|
-
|
304
|
-
|
428
|
+
code = REXML::XPath.first(@response, "//Error/Code").text
|
429
|
+
message = REXML::XPath.first(@response, "//Error/Message").text
|
305
430
|
|
306
|
-
|
307
|
-
|
431
|
+
return "Error #{code}: #{message}"
|
432
|
+
end
|
308
433
|
|
309
434
|
# The following type hashes are to allow cross-api data retrieval
|
310
435
|
|
@@ -327,30 +452,30 @@ module Shipping
|
|
327
452
|
"international_ground_service" => "INTERNATIONALGROUND"
|
328
453
|
}
|
329
454
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
455
|
+
PackageTypes = {
|
456
|
+
"fedex_envelope" => "FEDEXENVELOPE",
|
457
|
+
"fedex_pak" => "FEDEXPAK",
|
458
|
+
"fedex_box" => "FEDEXBOX",
|
459
|
+
"fedex_tube" => "FEDEXTUBE",
|
460
|
+
"fedex_10_kg_box" => "FEDEX10KGBOX",
|
461
|
+
"fedex_25_kg_box" => "FEDEX25KGBOX",
|
462
|
+
"your_packaging" => "YOURPACKAGING"
|
463
|
+
}
|
464
|
+
|
465
|
+
DropoffTypes = {
|
466
|
+
'regular_pickup' => 'REGULARPICKUP',
|
467
|
+
'request_courier' => 'REQUESTCOURIER',
|
468
|
+
'dropbox' => 'DROPBOX',
|
469
|
+
'business_service_center' => 'BUSINESSSERVICECENTER',
|
470
|
+
'station' => 'STATION'
|
471
|
+
}
|
472
|
+
|
473
|
+
PaymentTypes = {
|
474
|
+
'sender' => 'SENDER',
|
475
|
+
'recipient' => 'RECIPIENT',
|
476
|
+
'third_party' => 'THIRDPARTY',
|
477
|
+
'collect' => 'COLLECT'
|
478
|
+
}
|
354
479
|
|
355
480
|
|
356
481
|
TransactionTypes = {
|
data/test/fedex/fedex_test.rb
CHANGED
@@ -5,11 +5,12 @@ class FedExTest < Test::Unit::TestCase
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def test_price
|
8
|
-
|
8
|
+
assert_in_delta 5.00, @ship.price, 1
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
11
|
+
def test_discount_price
|
12
|
+
assert_in_delta 5.00, @ship.discount_price, 1
|
13
|
+
assert @ship.discount_price < @ship.price
|
13
14
|
end
|
14
15
|
|
15
16
|
def test_fails
|
data/test/ups/ups_test.rb
CHANGED
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
|
|
3
3
|
specification_version: 1
|
4
4
|
name: shipping
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date: 2005-09-
|
6
|
+
version: 1.3.0
|
7
|
+
date: 2005-09-24
|
8
8
|
summary: A general shipping module to find out the shipping prices via UPS or FedEx.
|
9
9
|
require_paths:
|
10
10
|
- lib
|