spree_active_shipping 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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