active_shipping 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  .DS_Store
2
2
  test.xml
3
+ test/fixtures.yml
3
4
  sample.rb
4
5
  *.orig
5
6
  *.swp
data/CHANGELOG CHANGED
@@ -1,3 +1,4 @@
1
+ * Require active_support instead of activesupport to avoid deprecation warning in Rails 2.3.5 [cody]
1
2
  * Remove ftools for Rails 1.9 compatibility and remove xml logging, as logging is now included in the connection [cody]
2
3
  * Update connection code from ActiveMerchant [cody]
3
4
  * Fix space-ridden USPS usernames when validating credentials [james]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.0
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{active_shipping}
8
- s.version = "0.0.2"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["James MacAulay", "Tobias Luetke", "Cody Fauser", "Jimmy Baker"]
12
- s.date = %q{2009-11-11}
12
+ s.date = %q{2010-01-21}
13
13
  s.description = %q{Shipping API extension for Active Merchant.}
14
14
  s.email = %q{jmacaulay@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -41,11 +41,16 @@ Gem::Specification.new do |s|
41
41
  "lib/active_shipping/shipping/carriers/shipwire.rb",
42
42
  "lib/active_shipping/shipping/carriers/ups.rb",
43
43
  "lib/active_shipping/shipping/carriers/usps.rb",
44
+ "lib/active_shipping/shipping/contact.rb",
45
+ "lib/active_shipping/shipping/label.rb",
44
46
  "lib/active_shipping/shipping/location.rb",
45
47
  "lib/active_shipping/shipping/package.rb",
48
+ "lib/active_shipping/shipping/party.rb",
46
49
  "lib/active_shipping/shipping/rate_estimate.rb",
47
50
  "lib/active_shipping/shipping/rate_response.rb",
48
51
  "lib/active_shipping/shipping/response.rb",
52
+ "lib/active_shipping/shipping/return_shipment.rb",
53
+ "lib/active_shipping/shipping/shipment.rb",
49
54
  "lib/active_shipping/shipping/shipment_event.rb",
50
55
  "lib/active_shipping/shipping/tracking_response.rb",
51
56
  "lib/certs/cacert.pem",
@@ -68,7 +73,7 @@ Gem::Specification.new do |s|
68
73
  "lib/vendor/xml_node/lib/xml_node.rb",
69
74
  "lib/vendor/xml_node/test/test_generating.rb",
70
75
  "lib/vendor/xml_node/test/test_parsing.rb",
71
- "test/fixtures.yml",
76
+ "test/fixtures.example.yml",
72
77
  "test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_request.xml",
73
78
  "test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_response.xml",
74
79
  "test/fixtures/xml/fedex/tracking_request.xml",
@@ -86,10 +91,12 @@ Gem::Specification.new do |s|
86
91
  "test/fixtures/xml/usps/beverly_hills_to_ottawa_wii_rate_response.xml",
87
92
  "test/fixtures/xml/usps/example_tracking_response.xml",
88
93
  "test/fixtures/xml/usps/multi_tracking_example.xml",
94
+ "test/party_factory.rb",
89
95
  "test/remote/fedex_test.rb",
90
96
  "test/remote/shipwire_test.rb",
91
97
  "test/remote/ups_test.rb",
92
98
  "test/remote/usps_test.rb",
99
+ "test/shipment_factory.rb",
93
100
  "test/test_helper.rb",
94
101
  "test/unit/base_test.rb",
95
102
  "test/unit/carriers/fedex_test.rb",
@@ -98,7 +105,9 @@ Gem::Specification.new do |s|
98
105
  "test/unit/carriers/usps_test.rb",
99
106
  "test/unit/location_test.rb",
100
107
  "test/unit/package_test.rb",
101
- "test/unit/response_test.rb"
108
+ "test/unit/party_test.rb",
109
+ "test/unit/response_test.rb",
110
+ "test/unit/shipment_test.rb"
102
111
  ]
103
112
  s.homepage = %q{http://github.com/Shopify/active_shipping}
104
113
  s.rdoc_options = ["--charset=UTF-8"]
@@ -106,10 +115,12 @@ Gem::Specification.new do |s|
106
115
  s.rubygems_version = %q{1.3.5}
107
116
  s.summary = %q{Shipping API extension for Active Merchant.}
108
117
  s.test_files = [
109
- "test/remote/fedex_test.rb",
118
+ "test/party_factory.rb",
119
+ "test/remote/fedex_test.rb",
110
120
  "test/remote/shipwire_test.rb",
111
121
  "test/remote/ups_test.rb",
112
122
  "test/remote/usps_test.rb",
123
+ "test/shipment_factory.rb",
113
124
  "test/test_helper.rb",
114
125
  "test/unit/base_test.rb",
115
126
  "test/unit/carriers/fedex_test.rb",
@@ -118,7 +129,9 @@ Gem::Specification.new do |s|
118
129
  "test/unit/carriers/usps_test.rb",
119
130
  "test/unit/location_test.rb",
120
131
  "test/unit/package_test.rb",
121
- "test/unit/response_test.rb"
132
+ "test/unit/party_test.rb",
133
+ "test/unit/response_test.rb",
134
+ "test/unit/shipment_test.rb"
122
135
  ]
123
136
 
124
137
  if s.respond_to? :specification_version then
@@ -134,3 +147,4 @@ Gem::Specification.new do |s|
134
147
  s.add_dependency(%q<activesupport>, [">= 0"])
135
148
  end
136
149
  end
150
+
@@ -45,6 +45,15 @@ require 'active_shipping/shipping/tracking_response'
45
45
  require 'active_shipping/shipping/package'
46
46
  require 'active_shipping/shipping/location'
47
47
  require 'active_shipping/shipping/rate_estimate'
48
+
49
+ require 'active_shipping/shipping/contact'
50
+ require 'active_shipping/shipping/party'
51
+ require 'active_shipping/shipping/label'
52
+
53
+ require 'active_shipping/shipping/shipment'
54
+ require 'active_shipping/shipping/return_shipment'
55
+
48
56
  require 'active_shipping/shipping/shipment_event'
49
57
  require 'active_shipping/shipping/carrier'
50
58
  require 'active_shipping/shipping/carriers'
59
+
@@ -4,11 +4,8 @@ module ActiveMerchant
4
4
  mattr_accessor :mode
5
5
  self.mode = :production
6
6
 
7
- ALLCAPS_NAMES = ['ups','usps','dhl'] # is the class name allcaps like USPS or camelcase like FedEx?
8
-
9
7
  def self.carrier(name)
10
- name = name.to_s.downcase
11
- ActiveMerchant::Shipping.const_get(ALLCAPS_NAMES.include?(name) ? name.upcase : name.camelize)
8
+ ActiveMerchant::Shipping::Carriers.all.find {|c| c.name.downcase == name}
12
9
  end
13
10
  end
14
11
  end
@@ -32,7 +32,7 @@ module ActiveMerchant
32
32
  # alternate functionality, such as checking for test_mode to use test servers, etc.
33
33
  def valid_credentials?
34
34
  location = self.class.default_location
35
- find_rates(location,location,Package.new(100, [5,15,30]))
35
+ find_rates(location,location,Package.new(100, [5,15,30]), :test => test_mode)
36
36
  rescue ActiveMerchant::Shipping::ResponseError
37
37
  false
38
38
  else
@@ -9,7 +9,7 @@ module ActiveMerchant
9
9
  module Carriers
10
10
  class <<self
11
11
  def all
12
- [BogusCarrier, UPS, USPS, FedEx]
12
+ [BogusCarrier, UPS, USPS, FedEx, Shipwire]
13
13
  end
14
14
  end
15
15
  end
@@ -106,8 +106,145 @@ module ActiveMerchant
106
106
  response = commit(save_request(tracking_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
107
107
  parse_tracking_response(response, options)
108
108
  end
109
-
109
+
110
+ def get_return_label(shipment, options = {})
111
+ req = build_return_label_request(shipment)
112
+ response = commit(save_request(req), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
113
+
114
+ parse_return_label_response(response, shipment)
115
+ end
116
+
117
+ def validate_location(location, options = {})
118
+ req = build_location_validation_request(location)
119
+ response = commit(save_request(req), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
120
+
121
+ parse_location_validation_request(response, location)
122
+ response
123
+ end
124
+
110
125
  protected
126
+ def build_location_validation_request(location)
127
+ xml_request = XmlNode.new('AddressValidationRequest',
128
+ 'xmlns' => 'http://fedex.com/ws/addressvalidation/v2') do |root_node|
129
+
130
+ root_node << build_request_header
131
+ root_node << XmlNode.new('Version') do |version_node|
132
+ version_node << XmlNode.new('ServiceId', 'aval')
133
+ version_node << XmlNode.new('Major', '2')
134
+ version_node << XmlNode.new('Intermediate', '0')
135
+ version_node << XmlNode.new('Minor', '0')
136
+ end
137
+
138
+ root_node << XmlNode.new('RequestTimestamp', Time.now)
139
+ root_node << XmlNode.new('Options', '')
140
+ root_node << XmlNode.new('AddressesToValidate') do |v|
141
+ v << build_party_location_node('Address', location)
142
+ end
143
+ end
144
+
145
+ xml_request.to_s
146
+ end
147
+
148
+ def parse_location_validation_request(response, location)
149
+ xml = REXML::Document.new(response)
150
+ success = response_success?(xml)
151
+ message = response_message(xml)
152
+
153
+ if success
154
+ parent = xml.elements.first
155
+ location.valid = parent.elements['//DeliveryPointValidation'][0] == 'CONFIRMED'
156
+ location.score = parent.elements['//Score'][0].to_s.to_i
157
+ else
158
+ location.valid = false
159
+ location.score = 0
160
+ message
161
+ end
162
+ end
163
+
164
+ def parse_return_label_response(response, shipment)
165
+ xml = REXML::Document.new(response)
166
+ success = response_success?(xml)
167
+ message = response_message(xml)
168
+
169
+ if success
170
+ parent = xml.elements.first
171
+ shipment.label.image = Base64.decode64(parent.elements['//Image'][0].to_s)
172
+ shipment.tracking_number = parent.elements['//TrackingNumber'][0].to_s
173
+ shipment.transit_time = parent.elements['//TransitTime'][0].to_s
174
+
175
+ shipment
176
+ else
177
+ message
178
+ end
179
+ end
180
+
181
+ def build_return_label_request(shipment)
182
+ xml_request = XmlNode.new('ProcessShipmentRequest',
183
+ 'xmlns' => 'http://fedex.com/ws/ship/v7') do |root_node|
184
+ root_node << build_request_header
185
+ root_node << XmlNode.new('Version') do |version_node|
186
+ version_node << XmlNode.new('ServiceId', 'ship')
187
+ version_node << XmlNode.new('Major', '7')
188
+ version_node << XmlNode.new('Intermediate', '0')
189
+ version_node << XmlNode.new('Minor', '0')
190
+ end
191
+
192
+ root_node << XmlNode.new('RequestedShipment') do |rs|
193
+ rs << XmlNode.new('ShipTimestamp', shipment.ship_at || Time.now)
194
+ rs << XmlNode.new('DropoffType', shipment.dropoff_type)
195
+ rs << XmlNode.new('ServiceType', shipment.service)
196
+ rs << XmlNode.new('PackagingType', shipment.packaging_type)
197
+ rs << XmlNode.new('TotalWeight') do |t|
198
+ t << XmlNode.new('Units', shipment.total_weight_units)
199
+ t << XmlNode.new('Value', shipment.total_weight_value)
200
+ end
201
+ if shipment.total_insured_amount
202
+ rs << XmlNode.new('TotalInsuredValue') do |ins|
203
+ ins << XmlNode.new('Currency', shipment.total_insured_currency)
204
+ ins << XmlNode.new('Amount', shipment.total_insured_amount)
205
+ end
206
+ end
207
+ rs << build_party_node('Shipper', shipment.shipper)
208
+ rs << build_party_node('Recipient', shipment.recipient)
209
+ rs << XmlNode.new('ShippingChargesPayment') do |payment|
210
+ payment << XmlNode.new('PaymentType', shipment.payment_type)
211
+ payment << XmlNode.new('Payor') do |payor|
212
+ payor << XmlNode.new('AccountNumber', shipment.payor_account_number)
213
+ payor << XmlNode.new('CountryCode', shipment.payor_account_country.code(:alpha2))
214
+ end
215
+ end
216
+
217
+ rs << XmlNode.new('SpecialServicesRequested') do |s|
218
+ s << XmlNode.new('SpecialServiceTypes', 'RETURN_SHIPMENT')
219
+ s << XmlNode.new('ReturnShipmentDetail') do |d|
220
+ d << XmlNode.new('ReturnType', shipment.return_type)
221
+ d << XmlNode.new('Rma') do |rma|
222
+ rma << XmlNode.new('Number', shipment.rma_number)
223
+ end
224
+ end
225
+ end
226
+
227
+ rs << XmlNode.new('LabelSpecification') do |spec|
228
+ spec << XmlNode.new('LabelFormatType',
229
+ shipment.label.format_type || 'COMMON2D')
230
+ spec << XmlNode.new('ImageType', shipment.label.image_type)
231
+ spec << XmlNode.new('LabelStockType', 'PAPER_4X6')
232
+ end
233
+ rs << XmlNode.new('RateRequestTypes', shipment.rate_request_type)
234
+ rs << XmlNode.new('PackageCount', shipment.package_count)
235
+ rs << XmlNode.new('PackageDetail', 'PACKAGE_SUMMARY')
236
+ (1..shipment.package_count).each do |package_index|
237
+ rs << XmlNode.new('RequestedPackageLineItems') do |p|
238
+ p << XmlNode.new('SequenceNumber', package_index)
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ xml_request.to_s
245
+ end
246
+
247
+
111
248
  def build_rate_request(origin, destination, packages, options={})
112
249
  imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
113
250
 
@@ -205,6 +342,47 @@ module ActiveMerchant
205
342
  [web_authentication_detail, client_detail, trasaction_detail]
206
343
  end
207
344
 
345
+ def build_party_node(name, party)
346
+ XmlNode.new(name) do |xml_node|
347
+ if party.account_number
348
+ xml_node << XmlNode.new('AccountNumber', party.account_number)
349
+ end
350
+
351
+ if party.contact
352
+ xml_node << XmlNode.new('Contact') do |c|
353
+ c << XmlNode.new('PersonName', party.contact.name)
354
+ c << XmlNode.new('Title', party.contact.title)
355
+ c << XmlNode.new('CompanyName', party.contact.company_name)
356
+ c << XmlNode.new('PhoneNumber', party.contact.phone_number)
357
+ c << XmlNode.new('FaxNumber', party.contact.fax_number)
358
+ c << XmlNode.new('EMailAddress', party.contact.email_address)
359
+ end
360
+ end
361
+
362
+
363
+ if party.location
364
+ xml_node << build_party_location_node('Address', party.location)
365
+ end
366
+ end
367
+ end
368
+
369
+ def build_party_location_node(name, location)
370
+ XmlNode.new(name) do |a|
371
+ if location.address1
372
+ a << XmlNode.new('StreetLines', location.address1)
373
+ end
374
+
375
+ if location.address2
376
+ a << XmlNode.new('StreetLines', location.address2)
377
+ end
378
+
379
+ a << XmlNode.new('City', location.city)
380
+ a << XmlNode.new('StateOrProvinceCode', location.state)
381
+ a << XmlNode.new('PostalCode', location.zip)
382
+ a << XmlNode.new('CountryCode', location.country.code(:alpha2))
383
+ end
384
+ end
385
+
208
386
  def build_location_node(name, location)
209
387
  location_node = XmlNode.new(name) do |xml_node|
210
388
  xml_node << XmlNode.new('Address') do |address_node|
@@ -17,7 +17,7 @@ module ActiveMerchant
17
17
  :track => 'ups.app/xml/Track'
18
18
  }
19
19
 
20
- PICKUP_CODES = {
20
+ PICKUP_CODES = HashWithIndifferentAccess.new({
21
21
  :daily_pickup => "01",
22
22
  :customer_counter => "03",
23
23
  :one_time_pickup => "06",
@@ -25,7 +25,25 @@ module ActiveMerchant
25
25
  :suggested_retail_rates => "11",
26
26
  :letter_center => "19",
27
27
  :air_service_center => "20"
28
- }
28
+ })
29
+
30
+ CUSTOMER_CLASSIFICATIONS = HashWithIndifferentAccess.new({
31
+ :wholesale => "01",
32
+ :occasional => "03",
33
+ :retail => "04"
34
+ })
35
+
36
+ # these are the defaults described in the UPS API docs,
37
+ # but they don't seem to apply them under all circumstances,
38
+ # so we need to take matters into our own hands
39
+ DEFAULT_CUSTOMER_CLASSIFICATIONS = Hash.new do |hash,key|
40
+ hash[key] = case key.to_sym
41
+ when :daily_pickup then :wholesale
42
+ when :customer_counter then :retail
43
+ else
44
+ :occasional
45
+ end
46
+ end
29
47
 
30
48
  DEFAULT_SERVICES = {
31
49
  "01" => "UPS Next Day Air",
@@ -127,11 +145,18 @@ module ActiveMerchant
127
145
  # not implemented: 'Rate' RequestOption to specify a single service query
128
146
  # request << XmlNode.new('RequestOption', ((options[:service].nil? or options[:service] == :all) ? 'Shop' : 'Rate'))
129
147
  end
130
- root_node << XmlNode.new('PickupType') do |pickup_type|
131
- pickup_type << XmlNode.new('Code', PICKUP_CODES[options[:pickup_type] || :daily_pickup])
148
+
149
+ pickup_type = options[:pickup_type] || :daily_pickup
150
+
151
+ root_node << XmlNode.new('PickupType') do |pickup_type_node|
152
+ pickup_type_node << XmlNode.new('Code', PICKUP_CODES[pickup_type])
132
153
  # not implemented: PickupType/PickupDetails element
133
154
  end
134
- # not implemented: CustomerClassification element
155
+ cc = options[:customer_classification] || DEFAULT_CUSTOMER_CLASSIFICATIONS[pickup_type]
156
+ root_node << XmlNode.new('CustomerClassification') do |cc_node|
157
+ cc_node << XmlNode.new('Code', CUSTOMER_CLASSIFICATIONS[cc])
158
+ end
159
+
135
160
  root_node << XmlNode.new('Shipment') do |shipment|
136
161
  # not implemented: Shipment/Description element
137
162
  shipment << build_location_node('Shipper', (options[:shipper] || origin), options)
@@ -150,7 +175,6 @@ module ActiveMerchant
150
175
  # * Shipment/DocumentsOnly element
151
176
 
152
177
  packages.each do |package|
153
- debugger if package.nil?
154
178
 
155
179
 
156
180
  imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
@@ -342,7 +366,7 @@ module ActiveMerchant
342
366
  end
343
367
 
344
368
  def response_message(xml)
345
- xml.get_text('/*/Response/ResponseStatusDescription | /*/Response/Error/ErrorDescription').to_s
369
+ xml.get_text('/*/Response/Error/ErrorDescription | /*/Response/ResponseStatusDescription').to_s
346
370
  end
347
371
 
348
372
  def commit(action, request, test = false)
@@ -0,0 +1,18 @@
1
+ module ActiveMerchant
2
+ module Shipping
3
+ class Contact
4
+ attr_accessor :name
5
+ attr_accessor :title
6
+ attr_accessor :company_name
7
+ attr_accessor :phone_number
8
+ attr_accessor :fax_number
9
+ attr_accessor :email_address
10
+
11
+ def initialize(attrs = {})
12
+ attrs.each do |key, value|
13
+ self.send("#{key}=", value) if self.respond_to?("#{key}=")
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveMerchant
2
+ module Shipping
3
+ class Label
4
+ FEDEX_FORMATS = [
5
+ 'COMMON2D',
6
+ 'LABEL_DATA_ONLY'
7
+ ]
8
+
9
+ FEDEX_IMAGE_TYPES = [
10
+ 'DPL',
11
+ 'EPL2',
12
+ 'PDF',
13
+ 'PNG',
14
+ 'ZPLII'
15
+ ]
16
+
17
+ attr_accessor :format_type
18
+ attr_accessor :image_type
19
+ attr_accessor :image
20
+
21
+ def initialize(attrs = {})
22
+ attrs.each do |key, value|
23
+ self.send("#{key}=", value) if self.respond_to?("#{key}=")
24
+ end
25
+
26
+ self.image_type ||= 'PNG'
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -19,6 +19,9 @@ module ActiveMerchant #:nodoc:
19
19
  alias_method :state, :province
20
20
  alias_method :territory, :province
21
21
  alias_method :region, :province
22
+
23
+ attr_accessor :valid
24
+ attr_accessor :score
22
25
 
23
26
  def initialize(options = {})
24
27
  @country = (options[:country].nil? or options[:country].is_a?(ActiveMerchant::Country)) ?
@@ -97,4 +100,4 @@ module ActiveMerchant #:nodoc:
97
100
  end
98
101
 
99
102
  end
100
- end
103
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveMerchant
2
+ module Shipping
3
+ class Party
4
+ attr_accessor :location
5
+ attr_accessor :contact
6
+ attr_accessor :account_number
7
+
8
+ def initialize(attrs = {})
9
+ attrs.each do |key, value|
10
+ self.send("#{key}=", value) if self.respond_to?("#{key}=")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module ActiveMerchant
2
+ module Shipping
3
+ class ReturnShipment < Shipment
4
+ attr_accessor :rma_number
5
+ attr_reader :return_type
6
+ def initialize(opts = {})
7
+ super
8
+ self.rma_number = opts[:rma_number]
9
+ self.payment_type ||= 'RECIPIENT'
10
+ @return_type = opts[:return_type] || 'PRINT_RETURN_LABEL'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ module ActiveMerchant
2
+ module Shipping
3
+ class Shipment
4
+ include ActiveMerchant::Shipping
5
+
6
+ FEDEX_PAYMENT_TYPES = [
7
+ 'COLLECT',
8
+ 'RECIPIENT',
9
+ 'SENDER',
10
+ 'THIRD_PARTY'
11
+ ]
12
+
13
+ FEDEX_DROPOFF_TYPES = [
14
+ "REGULAR_PICKUP",
15
+ "REQUEST_COURIER",
16
+ "DROP_BOX",
17
+ "BUSINESS_SERVICE_CENTER",
18
+ "STATION"
19
+ ]
20
+
21
+ FEDEX_RATE_REQUEST_TYPES = [
22
+ "ACCOUNT",
23
+ "LIST"
24
+ ]
25
+
26
+ attr_accessor :ship_at
27
+ attr_accessor :service
28
+ attr_accessor :packaging_type
29
+ attr_accessor :shipper
30
+ attr_accessor :total_insured_amount
31
+ attr_accessor :total_insured_currency
32
+ attr_accessor :recipient
33
+ attr_accessor :payment_type
34
+ attr_accessor :special_services
35
+ attr_accessor :label
36
+ attr_accessor :requested_packages
37
+ attr_accessor :dropoff_type
38
+ attr_accessor :rate_request_type
39
+ attr_accessor :package_count
40
+ attr_accessor :total_weight_value
41
+ attr_accessor :total_weight_units
42
+ attr_accessor :payor_account_number
43
+
44
+ attr_accessor :tracking_number
45
+ attr_accessor :transit_time
46
+
47
+ attr_reader :payor_account_country
48
+
49
+ def initialize(attrs = {})
50
+ attrs.each do |key, value|
51
+ self.send("#{key}=", value) if self.respond_to?("#{key}=")
52
+ end
53
+
54
+ self.label ||= Label.new(:format_type => 'COMMON2D')
55
+ self.requested_packages ||= 1
56
+ self.dropoff_type ||= 'REGULAR_PICKUP'
57
+ self.service ||= 'FEDEX_GROUND'
58
+ self.packaging_type ||= 'YOUR_PACKAGING'
59
+ self.ship_at ||= Time.now
60
+ self.package_count ||= 1
61
+ self.rate_request_type ||= 'ACCOUNT'
62
+ self.total_insured_currency ||= 'USD' if self.total_insured_amount
63
+ self.total_weight_units ||= 'LB' if self.total_weight_value
64
+ end
65
+ end
66
+
67
+ def payor_account_country=(c)
68
+ @payor_account_country = (c.nil? || c.is_a?(ActiveMerchant::Country)) ?
69
+ c : ActiveMerchant::Country.find(c)
70
+ @payor_account_country
71
+ end
72
+ end
73
+ end
@@ -1,6 +1,6 @@
1
1
  $: << File.dirname(__FILE__)
2
2
  require 'rubygems'
3
- require 'activesupport'
3
+ require 'active_support'
4
4
  require 'bigdecimal'
5
5
 
6
6
  require 'quantified/attribute'
@@ -5,8 +5,8 @@ ups:
5
5
  login: UPSDotComLogin
6
6
  password: UPSDotComPassword
7
7
  fedex:
8
- login: FedExAccountNumber
9
- password: FedExMeterNumber
8
+ login: FEDEXAccountNumber
9
+ password: FEDEXMeterNumber
10
10
  test: true
11
11
  shipwire:
12
12
  login: EmailAddress
@@ -0,0 +1,29 @@
1
+ class PartyFactory
2
+ include ActiveMerchant::Shipping
3
+ def self.build(carrier = :fedex, attrs = {})
4
+ default_location = Location.new(
5
+ :country => 'US',
6
+ :state => 'MA',
7
+ :address1 => '4 Yawkey Way',
8
+ :city => 'Boston',
9
+ :state => 'MA',
10
+ :zip => '02215'
11
+ )
12
+
13
+ default_contact = Contact.new(
14
+ :name => 'John Smith',
15
+ :company => 'Red Sox',
16
+ :phone_number => '1-555-867-5309',
17
+ :fax_number => '1-555-867-5310',
18
+ :email_address => 'user@example.com'
19
+ )
20
+
21
+ default_attrs = {
22
+ :location => default_location,
23
+ :contact => default_contact,
24
+ :account_number => '5555555'
25
+ }
26
+
27
+ Party.new(default_attrs.merge(attrs))
28
+ end
29
+ end
@@ -5,8 +5,12 @@ class FedExTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @packages = TestFixtures.packages
7
7
  @locations = TestFixtures.locations
8
- @carrier = FedEx.new(fixtures(:fedex))
9
- FedEx.logger = Logger.new('/Users/james/.active_merchant/fedex.log')
8
+ @carrier = FedEx.new(fixtures(:fedex).merge(:test => true))
9
+ #FedEx.logger = Logger.new('/Users/james/.active_merchant/fedex.log')
10
+ end
11
+
12
+ def test_valid_credentials
13
+ assert @carrier.valid_credentials?
10
14
  end
11
15
 
12
16
  def test_us_to_canada
@@ -15,8 +19,7 @@ class FedExTest < Test::Unit::TestCase
15
19
  response = @carrier.find_rates(
16
20
  @locations[:beverly_hills],
17
21
  @locations[:ottawa],
18
- @packages.values_at(:wii),
19
- :test => true
22
+ @packages.values_at(:wii)
20
23
  )
21
24
  assert !response.rates.blank?
22
25
  response.rates.each do |rate|
@@ -31,8 +34,7 @@ class FedExTest < Test::Unit::TestCase
31
34
  @carrier.find_rates(
32
35
  Location.new(:zip => 40524),
33
36
  Location.new(:zip => 40515),
34
- @packages[:wii],
35
- :test => true
37
+ @packages[:wii]
36
38
  )
37
39
  rescue ResponseError => e
38
40
  assert_match /country\s?code/i, e.message
@@ -45,8 +47,7 @@ class FedExTest < Test::Unit::TestCase
45
47
  response = @carrier.find_rates(
46
48
  @locations[:bare_beverly_hills],
47
49
  @locations[:bare_ottawa],
48
- @packages.values_at(:wii),
49
- :test => true
50
+ @packages.values_at(:wii)
50
51
  )
51
52
 
52
53
  assert response.rates.size > 0
@@ -57,8 +58,7 @@ class FedExTest < Test::Unit::TestCase
57
58
  response = @carrier.find_rates(
58
59
  @locations[:bare_beverly_hills],
59
60
  Location.new(:country => 'CA'),
60
- @packages.values_at(:wii),
61
- :test => true
61
+ @packages.values_at(:wii)
62
62
  )
63
63
  rescue ResponseError => e
64
64
  assert_match /postal code/i, e.message
@@ -71,8 +71,7 @@ class FedExTest < Test::Unit::TestCase
71
71
  response = @carrier.find_rates(
72
72
  @locations[:bare_beverly_hills],
73
73
  Location.new(:country => 'JP', :zip => '108-8361'),
74
- @packages.values_at(:wii),
75
- :test => true
74
+ @packages.values_at(:wii)
76
75
  )
77
76
  rescue ResponseError => e
78
77
  assert_match /postal code/i, e.message
@@ -86,8 +85,7 @@ class FedExTest < Test::Unit::TestCase
86
85
  response = @carrier.find_rates(
87
86
  @locations[:ottawa],
88
87
  @locations[:beverly_hills],
89
- @packages.values_at(:book, :wii),
90
- :test => true
88
+ @packages.values_at(:book, :wii)
91
89
  )
92
90
  assert !response.rates.blank?
93
91
  response.rates.each do |rate|
@@ -103,8 +101,7 @@ class FedExTest < Test::Unit::TestCase
103
101
  response = @carrier.find_rates(
104
102
  @locations[:ottawa],
105
103
  @locations[:london],
106
- @packages.values_at(:book, :wii),
107
- :test => true
104
+ @packages.values_at(:book, :wii)
108
105
  )
109
106
  assert !response.rates.blank?
110
107
  response.rates.each do |rate|
@@ -120,8 +117,7 @@ class FedExTest < Test::Unit::TestCase
120
117
  response = @carrier.find_rates(
121
118
  @locations[:beverly_hills],
122
119
  @locations[:london],
123
- @packages.values_at(:book, :wii),
124
- :test => true
120
+ @packages.values_at(:book, :wii)
125
121
  )
126
122
  assert !response.rates.blank?
127
123
  response.rates.each do |rate|
@@ -137,4 +133,28 @@ class FedExTest < Test::Unit::TestCase
137
133
  end
138
134
  end
139
135
 
140
- end
136
+ def test_return_label
137
+ shipment = ShipmentFactory.build
138
+ shipment.shipper.account_number = fixtures(:fedex)[:account]
139
+ shipment.payor_account_number = fixtures(:fedex)[:account]
140
+ shipment.payor_account_country = 'US'
141
+ shipment.label.image_type = 'PDF'
142
+ @carrier.get_return_label(shipment, :test => true)
143
+ assert_not_nil shipment.label.image
144
+ assert_not_nil shipment.tracking_number
145
+ end
146
+
147
+ def test_address_validation
148
+ party = PartyFactory.build
149
+ @carrier.validate_location(party.location, :test => true)
150
+ assert party.location.valid
151
+ assert_equal 100, party.location.score
152
+ end
153
+
154
+ def test_address_validation_with_bunk_address
155
+ party = PartyFactory.build
156
+ party.location.stubs(:address1).returns('44 Foeawrfsadfasd Street')
157
+ @carrier.validate_location(party.location, :test => true)
158
+ assert !party.location.valid
159
+ end
160
+ end
@@ -114,6 +114,26 @@ class UPSTest < Test::Unit::TestCase
114
114
  end
115
115
  end
116
116
 
117
+ def test_us_to_uk_with_different_pickup_types
118
+ assert_nothing_raised do
119
+ daily_response = @carrier.find_rates(
120
+ @locations[:beverly_hills],
121
+ @locations[:london],
122
+ @packages.values_at(:book, :wii),
123
+ :pickup_type => :daily_pickup,
124
+ :test => true
125
+ )
126
+ one_time_response = @carrier.find_rates(
127
+ @locations[:beverly_hills],
128
+ @locations[:london],
129
+ @packages.values_at(:book, :wii),
130
+ :pickup_type => :one_time_pickup,
131
+ :test => true
132
+ )
133
+ assert_not_equal daily_response.rates.map(&:price), one_time_response.rates.map(&:price)
134
+ end
135
+ end
136
+
117
137
  def test_bare_packages
118
138
  response = nil
119
139
  p = Package.new(0,0)
@@ -0,0 +1,27 @@
1
+ class ShipmentFactory
2
+ include ActiveMerchant::Shipping
3
+
4
+ def self.build(carrier = :fedex)
5
+ shipment = ReturnShipment.new
6
+ if carrier == :fedex
7
+ shipment.ship_at = Time.now
8
+ shipment.service = 'FEDEX_GROUND'
9
+ shipment.payment_type = 'RECIPIENT'
10
+ shipment.packaging_type = FedEx::PackageTypes['your_packaging']
11
+ shipment.recipient = PartyFactory.build(:fedex)
12
+ jillians_boston = Location.new(
13
+ :address1 => '145 Ipswitch Street',
14
+ :city => 'Boston',
15
+ :state => 'MA',
16
+ :zip => '02215',
17
+ :country => 'US'
18
+ )
19
+ shipment.shipper = PartyFactory.build(:fedex, :location => jillians_boston)
20
+ shipment.total_weight_value = 10
21
+ shipment.payment_type = 'SENDER'
22
+ shipment.total_weight_units = 'LB'
23
+ end
24
+
25
+ shipment
26
+ end
27
+ end
data/test/test_helper.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
  $:.unshift(File.dirname(__FILE__) + '/../lib')
3
+ $:.unshift(File.dirname(__FILE__))
3
4
 
4
5
  require 'test/unit'
5
6
  require 'active_shipping'
6
7
  require 'mocha'
7
8
 
9
+ require 'shipment_factory'
10
+ require 'party_factory'
11
+
8
12
  module Test
9
13
  module Unit
10
14
  class TestCase
@@ -164,4 +168,4 @@ module ActiveMerchant
164
168
 
165
169
  end
166
170
  end
167
- end
171
+ end
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ class ActiveMerchant::Shipping::PartyTest < Test::Unit::TestCase
4
+ include ActiveMerchant::Shipping
5
+ def setup
6
+ @party = PartyFactory.build(:fedex)
7
+ end
8
+
9
+ def test_has_location
10
+ assert_not_nil @party.location
11
+ end
12
+
13
+ def test_has_contact
14
+ assert_not_nil @party.contact
15
+ end
16
+
17
+ def test_has_account_number
18
+ assert_not_nil @party.account_number
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ class ShipmentTest < Test::Unit::TestCase
4
+ def setup
5
+ @shipment = ShipmentFactory.build(:fedex)
6
+ end
7
+
8
+ def test_should_have_ship_at
9
+ assert_not_nil @shipment.ship_at
10
+ end
11
+
12
+ def test_should_have_service
13
+ assert_not_nil @shipment.service
14
+ end
15
+
16
+ def test_should_have_valid_service
17
+ end
18
+
19
+ def test_should_have_packaging_type
20
+ assert_not_nil @shipment.packaging_type
21
+ end
22
+
23
+ def test_should_have_shipper
24
+ assert_not_nil @shipment.shipper
25
+ end
26
+
27
+ def test_should_have_recipient
28
+ assert_not_nil @shipment.recipient
29
+ end
30
+
31
+ def test_should_have_payment_type
32
+ assert_not_nil @shipment.payment_type
33
+ end
34
+
35
+ def test_should_have_label
36
+ assert_not_nil @shipment.label
37
+ end
38
+
39
+ def test_should_have_requested_packages
40
+ assert_not_nil @shipment.requested_packages
41
+ end
42
+
43
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_shipping
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James MacAulay
@@ -12,7 +12,7 @@ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
14
 
15
- date: 2009-11-11 00:00:00 -05:00
15
+ date: 2010-01-21 00:00:00 -05:00
16
16
  default_executable:
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -59,11 +59,16 @@ files:
59
59
  - lib/active_shipping/shipping/carriers/shipwire.rb
60
60
  - lib/active_shipping/shipping/carriers/ups.rb
61
61
  - lib/active_shipping/shipping/carriers/usps.rb
62
+ - lib/active_shipping/shipping/contact.rb
63
+ - lib/active_shipping/shipping/label.rb
62
64
  - lib/active_shipping/shipping/location.rb
63
65
  - lib/active_shipping/shipping/package.rb
66
+ - lib/active_shipping/shipping/party.rb
64
67
  - lib/active_shipping/shipping/rate_estimate.rb
65
68
  - lib/active_shipping/shipping/rate_response.rb
66
69
  - lib/active_shipping/shipping/response.rb
70
+ - lib/active_shipping/shipping/return_shipment.rb
71
+ - lib/active_shipping/shipping/shipment.rb
67
72
  - lib/active_shipping/shipping/shipment_event.rb
68
73
  - lib/active_shipping/shipping/tracking_response.rb
69
74
  - lib/certs/cacert.pem
@@ -86,7 +91,7 @@ files:
86
91
  - lib/vendor/xml_node/lib/xml_node.rb
87
92
  - lib/vendor/xml_node/test/test_generating.rb
88
93
  - lib/vendor/xml_node/test/test_parsing.rb
89
- - test/fixtures.yml
94
+ - test/fixtures.example.yml
90
95
  - test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_request.xml
91
96
  - test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_response.xml
92
97
  - test/fixtures/xml/fedex/tracking_request.xml
@@ -104,10 +109,12 @@ files:
104
109
  - test/fixtures/xml/usps/beverly_hills_to_ottawa_wii_rate_response.xml
105
110
  - test/fixtures/xml/usps/example_tracking_response.xml
106
111
  - test/fixtures/xml/usps/multi_tracking_example.xml
112
+ - test/party_factory.rb
107
113
  - test/remote/fedex_test.rb
108
114
  - test/remote/shipwire_test.rb
109
115
  - test/remote/ups_test.rb
110
116
  - test/remote/usps_test.rb
117
+ - test/shipment_factory.rb
111
118
  - test/test_helper.rb
112
119
  - test/unit/base_test.rb
113
120
  - test/unit/carriers/fedex_test.rb
@@ -116,7 +123,9 @@ files:
116
123
  - test/unit/carriers/usps_test.rb
117
124
  - test/unit/location_test.rb
118
125
  - test/unit/package_test.rb
126
+ - test/unit/party_test.rb
119
127
  - test/unit/response_test.rb
128
+ - test/unit/shipment_test.rb
120
129
  has_rdoc: true
121
130
  homepage: http://github.com/Shopify/active_shipping
122
131
  licenses: []
@@ -146,10 +155,12 @@ signing_key:
146
155
  specification_version: 3
147
156
  summary: Shipping API extension for Active Merchant.
148
157
  test_files:
158
+ - test/party_factory.rb
149
159
  - test/remote/fedex_test.rb
150
160
  - test/remote/shipwire_test.rb
151
161
  - test/remote/ups_test.rb
152
162
  - test/remote/usps_test.rb
163
+ - test/shipment_factory.rb
153
164
  - test/test_helper.rb
154
165
  - test/unit/base_test.rb
155
166
  - test/unit/carriers/fedex_test.rb
@@ -158,4 +169,6 @@ test_files:
158
169
  - test/unit/carriers/usps_test.rb
159
170
  - test/unit/location_test.rb
160
171
  - test/unit/package_test.rb
172
+ - test/unit/party_test.rb
161
173
  - test/unit/response_test.rb
174
+ - test/unit/shipment_test.rb