spree_active_shipping 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.
Files changed (66) hide show
  1. data/.gitignore +2 -0
  2. data/README.md +67 -0
  3. data/Rakefile +45 -0
  4. data/app/controllers/admin/shipments_controller_decorator.rb +11 -0
  5. data/app/controllers/admin/shipping_methods_controller_decorator.rb +15 -0
  6. data/app/controllers/checkout_controller_decorator.rb +11 -0
  7. data/app/models/calculator/active_shipping.rb +126 -0
  8. data/app/models/calculator/fedex/base.rb +8 -0
  9. data/app/models/calculator/fedex/base.rb~ +7 -0
  10. data/app/models/calculator/fedex/express_saver.rb +5 -0
  11. data/app/models/calculator/fedex/first_overnight.rb +5 -0
  12. data/app/models/calculator/fedex/ground.rb +5 -0
  13. data/app/models/calculator/fedex/ground_home_delivery.rb +5 -0
  14. data/app/models/calculator/fedex/international_economy.rb +5 -0
  15. data/app/models/calculator/fedex/international_economy_freight.rb +5 -0
  16. data/app/models/calculator/fedex/international_first.rb +5 -0
  17. data/app/models/calculator/fedex/international_ground.rb +5 -0
  18. data/app/models/calculator/fedex/international_priority.rb +5 -0
  19. data/app/models/calculator/fedex/international_priority_freight.rb +5 -0
  20. data/app/models/calculator/fedex/international_priority_saturday_delivery.rb +5 -0
  21. data/app/models/calculator/fedex/one_day_freight.rb +5 -0
  22. data/app/models/calculator/fedex/one_day_freight_saturday_delivery.rb +5 -0
  23. data/app/models/calculator/fedex/priority_overnight.rb +5 -0
  24. data/app/models/calculator/fedex/priority_overnight_saturday_delivery.rb +5 -0
  25. data/app/models/calculator/fedex/saver.rb +5 -0
  26. data/app/models/calculator/fedex/saver.rb~ +5 -0
  27. data/app/models/calculator/fedex/standard_overnight.rb +5 -0
  28. data/app/models/calculator/fedex/three_day_freight.rb +5 -0
  29. data/app/models/calculator/fedex/three_day_freight_saturday_delivery.rb +5 -0
  30. data/app/models/calculator/fedex/two_day.rb +5 -0
  31. data/app/models/calculator/fedex/two_day_freight.rb +5 -0
  32. data/app/models/calculator/fedex/two_day_freight_saturday_delivery.rb +5 -0
  33. data/app/models/calculator/fedex/two_day_saturday_delivery.rb +5 -0
  34. data/app/models/calculator/ups/base.rb +15 -0
  35. data/app/models/calculator/ups/ground.rb +5 -0
  36. data/app/models/calculator/ups/next_day_air.rb +5 -0
  37. data/app/models/calculator/ups/next_day_air_early_am.rb +5 -0
  38. data/app/models/calculator/ups/next_day_air_saver.rb +5 -0
  39. data/app/models/calculator/ups/saver.rb +5 -0
  40. data/app/models/calculator/ups/second_day_air.rb +5 -0
  41. data/app/models/calculator/ups/three_day_select.rb +5 -0
  42. data/app/models/calculator/ups/worldwide_expedited.rb +5 -0
  43. data/app/models/calculator/usps/base.rb +5 -0
  44. data/app/models/calculator/usps/express_mail.rb +5 -0
  45. data/app/models/calculator/usps/express_mail_international.rb +5 -0
  46. data/app/models/calculator/usps/media_mail.rb +5 -0
  47. data/app/models/calculator/usps/priority_mail.rb +5 -0
  48. data/app/models/calculator/usps/priority_mail_international.rb +5 -0
  49. data/app/models/calculator/usps/priority_mail_large_flat_rate_box.rb +5 -0
  50. data/app/models/calculator/usps/priority_mail_regular_medium_flat_rate_boxes.rb +5 -0
  51. data/app/models/calculator/usps/priority_mail_small_flat_rate_box.rb +5 -0
  52. data/app/views/admin/shipping_methods/_form.html.erb +42 -0
  53. data/config/locales/en.yml +3 -0
  54. data/lib/active_shipping_configuration.rb +25 -0
  55. data/lib/spree/active_shipping/config.rb +22 -0
  56. data/lib/spree/active_shipping/ups_override.rb +274 -0
  57. data/lib/spree/shipping_error.rb +3 -0
  58. data/lib/spree_active_shipping.rb +74 -0
  59. data/lib/tasks/active_shipping_extension_tasks.rake +29 -0
  60. data/spec/lib/spree/bogus_calculator.rb +11 -0
  61. data/spec/lib/spree/bogus_carrier.rb +15 -0
  62. data/spec/models/active_shipping_calculator_spec.rb +51 -0
  63. data/spec/spec.opts +6 -0
  64. data/spec/spec_helper.rb +33 -0
  65. data/spree_active_shipping.gemspec +23 -0
  66. metadata +184 -0
@@ -0,0 +1,5 @@
1
+ class Calculator::Fedex::TwoDayFreight < Calculator::Fedex::Base
2
+ def self.description
3
+ "FedEx 2 Day Freight"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Fedex::TwoDayFreightSaturdayDelivery < Calculator::Fedex::Base
2
+ def self.description
3
+ "FedEx 2 Day Freight Saturday Delivery"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Fedex::TwoDaySaturdayDelivery < Calculator::Fedex::Base
2
+ def self.description
3
+ "FedEx 2 Day Saturday Delivery"
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ class Calculator::Ups::Base < Calculator::ActiveShipping
2
+ def carrier
3
+ if Spree::ActiveShipping::Config[:shipper_number].nil?
4
+ ActiveMerchant::Shipping::UPS.new(:login => Spree::ActiveShipping::Config[:ups_login],
5
+ :password => Spree::ActiveShipping::Config[:ups_password],
6
+ :key => Spree::ActiveShipping::Config[:ups_key])
7
+ else
8
+ ActiveMerchant::Shipping::UPS.new(:login => Spree::ActiveShipping::Config[:ups_login],
9
+ :password => Spree::ActiveShipping::Config[:ups_password],
10
+ :key => Spree::ActiveShipping::Config[:ups_key],
11
+ :origin_account => Spree::ActiveShipping::Config[:shipper_number])
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::Ground < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Ground"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::NextDayAir < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Next Day Air"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::NextDayAirEarlyAm < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Next Day Air Early A.M."
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::NextDayAirSaver < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Next Day Air Saver"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::Saver < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Saver"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::SecondDayAir < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Second Day Air"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::ThreeDaySelect < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Three-Day Select"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Ups::WorldwideExpedited < Calculator::Ups::Base
2
+ def self.description
3
+ "UPS Worldwide Expedited"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::Base < Calculator::ActiveShipping
2
+ def carrier
3
+ ActiveMerchant::Shipping::USPS.new(:login => Spree::ActiveShipping::Config[:usps_login])
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::ExpressMail < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Express Mail"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::ExpressMailInternational < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Express Mail International"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::MediaMail < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Media Mail"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::PriorityMail < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Priority Mail"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::PriorityMailInternational < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Priority Mail International"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::PriorityMailLargeFlatRateBox < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Priority Mail Large Flat-Rate Box"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::PriorityMailRegularMediumFlatRateBoxes < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Priority Mail Regular/Medium Flat-Rate Boxes"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Calculator::Usps::PriorityMailSmallFlatRateBox < Calculator::Usps::Base
2
+ def self.description
3
+ "USPS Priority Mail Small Flat-Rate Box"
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ <%- locals = {:f => f} %>
2
+ <%= hook :admin_shipping_method_form_fields, locals do %>
3
+ <%= f.field_container :name do %>
4
+ <%= f.label :name, t("name") %><br />
5
+ <%= f.text_field :name %>
6
+ <%= error_message_on :shipping_method, :name %>
7
+ <% end %>
8
+
9
+ <%= f.field_container :zone_id do %>
10
+ <%= f.label :zone_id, t("zone") %><br />
11
+ <%= f.collection_select(:zone_id, @available_zones, :id, :name, {}, {"style" => "width:200px"}) %>
12
+ <%= error_message_on :shipping_method, :zone_id %>
13
+ <% end %>
14
+ <%= f.field_container :display_on do %>
15
+ <%= f.label :display_on, t("display") %><br />
16
+ <%= select(:shipping_method, :display_on, ShippingMethod::DISPLAY.collect {|display| [t(display), display == :both ? nil : display.to_s]}) %>
17
+ <%= error_message_on :shipping_method, :display_on %>
18
+ <% end %>
19
+ <% end %>
20
+
21
+ <%= hook :admin_shipping_method_form_calculator_fields, locals do %>
22
+ <fieldset id="calculator_fields">
23
+ <legend><%= t('calculator') %></legend>
24
+ <div id="preference-settings">
25
+ <p>
26
+ <%= f.label(:calc_type, t("calculator")) %>
27
+ <%= f.collection_select(:calculator_type, @calculators, :class, :description, {}, {:id => "calc-type"}) %>
28
+ </p>
29
+ <% if !@object.new_record? %>
30
+ <div class="calculator-settings">
31
+ <%= f.fields_for :calculator do |calculator_form| %>
32
+ <%= preference_fields(@object.calculator, calculator_form) %>
33
+ <% end %>
34
+ </div>
35
+ <% if @object.calculator.respond_to?(:preferences) %>
36
+ <div id="calculator-settings-warning"><%= t('calculator_settings_warning')%></div>
37
+ <% end %>
38
+ <% end %>
39
+ </div>
40
+ </fieldset>
41
+ <% end %>
42
+
@@ -0,0 +1,3 @@
1
+ ---
2
+ en:
3
+ shipping_error: Shipping Error
@@ -0,0 +1,25 @@
1
+ class ActiveShippingConfiguration < Configuration
2
+
3
+ preference :ups_login, :string, :default => "aunt_judy"
4
+ preference :ups_password, :string, :default => "secret"
5
+ preference :ups_key, :string, :default => "developer_key"
6
+ preference :shipper_number, :string, :default => nil
7
+
8
+ preference :fedex_login, :string, :default => "meter_no"
9
+ preference :fedex_password, :string, :default => "special_sha1_looking_thing_sent_via_email"
10
+ preference :fedex_account, :string, :default => "account_no"
11
+ preference :fedex_key, :string, :default => "authorization_key"
12
+
13
+ preference :usps_login, :string, :default => "aunt_judy"
14
+
15
+ preference :origin_country, :string, :default => "US"
16
+ preference :origin_state, :string, :default => "PA"
17
+ preference :origin_city, :string, :default => "University Park"
18
+ preference :origin_zip, :string, :default => "16802"
19
+
20
+ preference :units, :string, :default => "imperial"
21
+ preference :unit_multiplier, :integer, :default => 16 # 16 oz./lb - assumes variant weights are in lbs
22
+
23
+ validates_presence_of :name
24
+ validates_uniqueness_of :name
25
+ end
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ module ActiveShipping
3
+ # Singleton class to access the shipping configuration object (ActiveShippingConfiguration.first by default) and it's preferences.
4
+ #
5
+ # Usage:
6
+ # Spree::ActiveShipping::Config[:foo] # Returns the foo preference
7
+ # Spree::ActiveShipping::Config[] # Returns a Hash with all the tax preferences
8
+ # Spree::ActiveShipping::Config.instance # Returns the configuration object (ActiveShippingConfiguration.first)
9
+ # Spree::ActiveShipping::Config.set(preferences_hash) # Set the active shipping preferences as especified in +preference_hash+
10
+ class Config
11
+ include Singleton
12
+ include PreferenceAccess
13
+
14
+ class << self
15
+ def instance
16
+ return nil unless ActiveRecord::Base.connection.tables.include?('configurations')
17
+ ActiveShippingConfiguration.find_or_create_by_name("Default active_shipping configuration")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,274 @@
1
+ module Spree
2
+ module ActiveShipping
3
+ module UpsOverride
4
+ def self.included(base)
5
+
6
+ base.class_eval do
7
+
8
+ def build_rate_request(origin, destination, packages, options={})
9
+ packages = Array(packages)
10
+ xml_request = XmlNode.new('RatingServiceSelectionRequest') do |root_node|
11
+ root_node << XmlNode.new('Request') do |request|
12
+ request << XmlNode.new('RequestAction', 'Rate')
13
+ request << XmlNode.new('RequestOption', 'Shop')
14
+ # not implemented: 'Rate' RequestOption to specify a single service query
15
+ # request << XmlNode.new('RequestOption', ((options[:service].nil? or options[:service] == :all) ? 'Shop' : 'Rate'))
16
+ end
17
+
18
+ pickup_type = options[:pickup_type] || :daily_pickup
19
+
20
+ root_node << XmlNode.new('PickupType') do |pickup_type_node|
21
+ pickup_type_node << XmlNode.new('Code', ActiveMerchant::Shipping::UPS::PICKUP_CODES[pickup_type])
22
+ # not implemented: PickupType/PickupDetails element
23
+ end
24
+ cc = options[:customer_classification] || ActiveMerchant::Shipping::UPS::DEFAULT_CUSTOMER_CLASSIFICATIONS[pickup_type]
25
+ root_node << XmlNode.new('CustomerClassification') do |cc_node|
26
+ cc_node << XmlNode.new('Code', ActiveMerchant::Shipping::UPS::CUSTOMER_CLASSIFICATIONS[cc])
27
+ end
28
+
29
+ root_node << XmlNode.new('Shipment') do |shipment|
30
+ # not implemented: Shipment/Description element
31
+ shipment << build_location_node('Shipper', (options[:shipper] || origin), options)
32
+ shipment << build_location_node('ShipTo', destination, options)
33
+ if options[:shipper] and options[:shipper] != origin
34
+ shipment << build_location_node('ShipFrom', origin, options)
35
+ end
36
+
37
+ # not implemented: * Shipment/ShipmentWeight element
38
+ # * Shipment/ReferenceNumber element
39
+ # * Shipment/Service element
40
+ # * Shipment/PickupDate element
41
+ # * Shipment/ScheduledDeliveryDate element
42
+ # * Shipment/ScheduledDeliveryTime element
43
+ # * Shipment/AlternateDeliveryTime element
44
+ # * Shipment/DocumentsOnly element
45
+
46
+ packages.each do |package|
47
+
48
+
49
+ imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
50
+
51
+ shipment << XmlNode.new("Package") do |package_node|
52
+
53
+ # not implemented: * Shipment/Package/PackagingType element
54
+ # * Shipment/Package/Description element
55
+
56
+ package_node << XmlNode.new("PackagingType") do |packaging_type|
57
+ packaging_type << XmlNode.new("Code", '02')
58
+ end
59
+
60
+ package_node << XmlNode.new("Dimensions") do |dimensions|
61
+ dimensions << XmlNode.new("UnitOfMeasurement") do |units|
62
+ units << XmlNode.new("Code", imperial ? 'IN' : 'CM')
63
+ end
64
+ [:length,:width,:height].each do |axis|
65
+ value = ((imperial ? package.inches(axis) : package.cm(axis)).to_f*1000).round/1000.0 # 3 decimals
66
+ dimensions << XmlNode.new(axis.to_s.capitalize, [value,0.1].max)
67
+ end
68
+ end
69
+
70
+ package_node << XmlNode.new("PackageWeight") do |package_weight|
71
+ package_weight << XmlNode.new("UnitOfMeasurement") do |units|
72
+ units << XmlNode.new("Code", imperial ? 'LBS' : 'KGS')
73
+ end
74
+
75
+ value = ((imperial ? package.lbs : package.kgs).to_f*1000).round/1000.0 # 3 decimals
76
+ package_weight << XmlNode.new("Weight", [value,0.1].max)
77
+ end
78
+
79
+ # not implemented: * Shipment/Package/LargePackageIndicator element
80
+ # * Shipment/Package/ReferenceNumber element
81
+ # * Shipment/Package/PackageServiceOptions element
82
+ # * Shipment/Package/AdditionalHandling element
83
+ end
84
+
85
+ end
86
+
87
+ # not implemented: * Shipment/ShipmentServiceOptions element
88
+ # * Shipment/RateInformation element
89
+
90
+ #SPREE OVERRIDE Negotiated Rates
91
+ if (origin_account = @options[:origin_account] || options[:origin_account])
92
+ shipment << XmlNode.new("RateInformation") do |rate_information|
93
+ rate_information << XmlNode.new("NegotiatedRatesIndicator", '')
94
+ end
95
+ end
96
+ end
97
+
98
+ end
99
+ xml_request.to_s
100
+ end
101
+
102
+ def build_time_in_transit_request(origin, destination, packages, options={})
103
+ packages = Array(packages)
104
+ xml_request = XmlNode.new('TimeInTransitRequest') do |root_node|
105
+ root_node << XmlNode.new('Request') do |request|
106
+ request << XmlNode.new('TransactionReference') do |transaction_reference|
107
+ transaction_reference << XmlNode.new('CustomerContext', 'Time in Transit')
108
+ transaction_reference << XmlNode.new('XpciVersion', '1.0002')
109
+ end
110
+ request << XmlNode.new('RequestAction', 'TimeInTransit')
111
+ end
112
+ root_node << XmlNode.new('TransitFrom') do |transit_from|
113
+ transit_from << XmlNode.new('AddressArtifactFormat') do |address_artifact_format|
114
+ address_artifact_format << XmlNode.new('PoliticalDivision2',origin.city)
115
+ address_artifact_format << XmlNode.new('PoliticalDivision1',origin.state)
116
+ address_artifact_format << XmlNode.new('CountryCode',origin.country_code(:alpha2))
117
+ address_artifact_format << XmlNode.new('PostcodePrimaryLow',origin.postal_code)
118
+ end
119
+ end
120
+
121
+ root_node << XmlNode.new('TransitTo') do |transit_to|
122
+ transit_to << XmlNode.new('AddressArtifactFormat') do |address_artifact_format|
123
+ address_artifact_format << XmlNode.new('PoliticalDivision2',destination.city)
124
+ address_artifact_format << XmlNode.new('PoliticalDivision1',destination.state)
125
+ address_artifact_format << XmlNode.new('CountryCode',destination.country_code(:alpha2))
126
+ address_artifact_format << XmlNode.new('PostcodePrimaryLow',destination.postal_code)
127
+ end
128
+ end
129
+
130
+ root_node << XmlNode.new("ShipmentWeight") do |shipment_weight|
131
+ shipment_weight << XmlNode.new("UnitOfMeasurement") do |units|
132
+ units << XmlNode.new("Code", 'LBS')
133
+ end
134
+
135
+ value = ((packages[0].lbs).to_f*1000).round/1000.0 # 3 decimals
136
+ shipment_weight << XmlNode.new("Weight", [value,0.1].max)
137
+ end
138
+
139
+ root_node << XmlNode.new("InvoiceLineTotal") do |invoice_line_total|
140
+ invoice_line_total << XmlNode.new("CurrencyCode","USD")
141
+ invoice_line_total << XmlNode.new("MonetaryValue","50")
142
+ end
143
+
144
+ root_node << XmlNode.new("PickupDate",Date.today.strftime("%Y%m%d"))
145
+
146
+
147
+ end
148
+ xml_request.to_s
149
+ end
150
+
151
+ def build_location_node(name,location,options={})
152
+ # not implemented: * Shipment/Shipper/Name element
153
+ # * Shipment/(ShipTo|ShipFrom)/CompanyName element
154
+ # * Shipment/(Shipper|ShipTo|ShipFrom)/AttentionName element
155
+ # * Shipment/(Shipper|ShipTo|ShipFrom)/TaxIdentificationNumber element
156
+ location_node = XmlNode.new(name) do |location_node|
157
+ location_node << XmlNode.new('PhoneNumber', location.phone.gsub(/[^\d]/,'')) unless location.phone.blank?
158
+ location_node << XmlNode.new('FaxNumber', location.fax.gsub(/[^\d]/,'')) unless location.fax.blank?
159
+
160
+ if name == 'Shipper' and (origin_account = @options[:origin_account] || options[:origin_account])
161
+ location_node << XmlNode.new('ShipperNumber', origin_account)
162
+ elsif name == 'ShipTo' and (destination_account = @options[:destination_account] || options[:destination_account])
163
+ location_node << XmlNode.new('ShipperAssignedIdentificationNumber', destination_account)
164
+ end
165
+
166
+ location_node << XmlNode.new('Address') do |address|
167
+ address << XmlNode.new("AddressLine1", location.address1) unless location.address1.blank?
168
+ address << XmlNode.new("AddressLine2", location.address2) unless location.address2.blank?
169
+ address << XmlNode.new("AddressLine3", location.address3) unless location.address3.blank?
170
+ address << XmlNode.new("City", location.city) unless location.city.blank?
171
+ address << XmlNode.new("StateProvinceCode", location.state) unless location.state.blank? #SPREE OVERRIDE
172
+ # StateProvinceCode required for negotiated rates but not otherwise, for some reason
173
+ address << XmlNode.new("PostalCode", location.postal_code) unless location.postal_code.blank?
174
+ address << XmlNode.new("CountryCode", location.country_code(:alpha2)) unless location.country_code(:alpha2).blank?
175
+ address << XmlNode.new("ResidentialAddressIndicator", true) unless location.commercial? # the default should be that UPS returns residential rates for destinations that it doesn't know about
176
+ # not implemented: Shipment/(Shipper|ShipTo|ShipFrom)/Address/ResidentialAddressIndicator element
177
+ end
178
+ end
179
+ end
180
+
181
+ def find_time_in_transit(origin, destination, packages, options={})
182
+ origin, destination = upsified_location(origin), upsified_location(destination)
183
+ options = @options.merge(options)
184
+ packages = Array(packages)
185
+ access_request = build_access_request
186
+ rate_request = build_time_in_transit_request(origin, destination, packages, options)
187
+ response = ssl_post("https://www.ups.com/ups.app/xml/TimeInTransit", "<?xml version=\"1.0\"?>"+access_request+"<?xml version=\"1.0\"?>"+rate_request)
188
+ parse_time_in_transit_response(origin, destination, packages,response, options)
189
+ end
190
+
191
+ def find_rates(origin, destination, packages, options={})
192
+ origin, destination = upsified_location(origin), upsified_location(destination)
193
+ options = @options.merge(options)
194
+ packages = Array(packages)
195
+ access_request = build_access_request
196
+ rate_request = build_rate_request(origin, destination, packages, options)
197
+ response = commit(:rates, save_request(access_request + rate_request), (options[:test] || false))
198
+ parse_rate_response(origin, destination, packages, response, options)
199
+ end
200
+
201
+
202
+ def parse_time_in_transit_response(origin, destination, packages, response, options={})
203
+
204
+ time_code_mapping = {
205
+ "1DA" => "01",
206
+ "2DA" => "02",
207
+ "GND" => "03",
208
+ "01" => "07",
209
+ "05" => "08",
210
+ "03" => "11",
211
+ "3DS" => "12",
212
+ "1DP" => "13",
213
+ "1DM" => "14",
214
+ "21" => "54",
215
+ "2DM" => "59"
216
+ }
217
+
218
+ rates = []
219
+ xml = REXML::Document.new(response)
220
+ success = response_success?(xml)
221
+ message = response_message(xml)
222
+ if success
223
+ rate_estimates = {}
224
+ xml.elements.each('/*/TransitResponse/ServiceSummary') do |service_summary|
225
+ service_code = service_summary.get_text('Service/Code').to_s
226
+ service_code_2 = time_code_mapping[service_code]
227
+ service_desc = service_summary.get_text('Service/Description').to_s
228
+ guaranteed_code = service_summary.get_text('Guaranteed/Code').to_s
229
+ business_transit_days = service_summary.get_text('EstimatedArrival/BusinessTransitDays').to_s
230
+ date = service_summary.get_text('EstimatedArrival/Date').to_s
231
+ rate_estimates[service_name_for(origin, service_code_2)] = {:service_code => service_code, :service_code_2 => service_code_2, :service_desc => service_desc,
232
+ :guaranteed_code => guaranteed_code, :business_transit_days => business_transit_days,
233
+ :date => date}
234
+ end
235
+ end
236
+ return rate_estimates
237
+ end
238
+
239
+
240
+
241
+ def parse_rate_response(origin, destination, packages, response, options={})
242
+ rates = []
243
+ xml = REXML::Document.new(response)
244
+ success = response_success?(xml)
245
+ message = response_message(xml)
246
+ transits = options[:transit]
247
+ if success
248
+ rate_estimates = []
249
+
250
+ xml.elements.each('/*/RatedShipment') do |rated_shipment|
251
+ service_code = rated_shipment.get_text('Service/Code').to_s
252
+ service = rated_shipment.get_text('Service/Code').to_s
253
+ negotiated_rate = rated_shipment.get_text('NegotiatedRates/NetSummaryCharges/GrandTotal/MonetaryValue').to_s
254
+ total_price = negotiated_rate.blank? ? rated_shipment.get_text('TotalCharges/MonetaryValue').to_s.to_f : negotiated_rate.to_f
255
+ currency = negotiated_rate.blank? ? rated_shipment.get_text('TotalCharges/CurrencyCode').to_s : rated_shipment.get_text('NegotiatedRates/NetSummaryCharges/GrandTotal/CurrencyCode').to_s
256
+
257
+ rate_estimates << ActiveMerchant::Shipping::RateEstimate.new(origin, destination, ActiveMerchant::Shipping::UPS.name,
258
+ service_name_for(origin, service_code),
259
+ :total_price => total_price,
260
+ :currency => currency,
261
+ :service_code => service_code,
262
+ :packages => packages
263
+ )
264
+ end
265
+ end
266
+ ActiveMerchant::Shipping::RateResponse.new(success, message, Hash.from_xml(response).values.first, :rates => rate_estimates, :xml => response, :request => last_request)
267
+ end
268
+ end
269
+ end
270
+
271
+
272
+ end
273
+ end
274
+ end