Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added PendingShipment request call to this repository #37

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
11 changes: 11 additions & 0 deletions lib/fedex/pending_shipment_label.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

module Fedex
class PendingShipmentLabel
attr_accessor :email_label_url
def initialize(label_details = {})
@email_label_url = label_details[:completed_shipment_detail][:access_detail][:email_label_url]
end


end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like quite much extra space, IMHO :)

I think it would be a bit more readable if we'll put a line in between function definition and attr_accessor instead. What do you think?

end
3 changes: 2 additions & 1 deletion lib/fedex/rate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Rate
# @total_net_freight #The freight charge minus dicounts
# @total_surcharges #The total amount of all surcharges applied to this shipment
# @total_base_charge #The total base charge
attr_accessor :rate_type, :rate_zone, :total_bilint_weight, :total_freight_discounts, :total_net_charge, :total_taxes, :total_net_freight, :total_surcharges, :total_base_charge
attr_accessor :rate_type, :rate_zone, :total_bilint_weight, :total_freight_discounts, :total_net_charge, :total_taxes, :total_net_freight, :total_surcharges, :total_base_charge, :response_details
def initialize(options = {})
@rate_type = options[:rate_type]
@rate_zone = options[:rate_zone]
Expand All @@ -31,6 +31,7 @@ def initialize(options = {})
@total_base_charge = options[:total_base_charge][:amount]
@total_net_fedex_charge = (options[:total_net_fe_dex_charge]||{})[:amount]
@total_rebates = (options[:total_rebates]||{})[:amount]
@response_details = options[:response_details]
end
end
end
10 changes: 9 additions & 1 deletion lib/fedex/request/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ def add_shipping_charges_payment(xml)
}
end





Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need those?

# Add packages to xml request
def add_packages(xml)
package_count = @packages.size
Expand All @@ -185,6 +189,9 @@ def add_packages(xml)
xml.Units package[:dimensions][:units]
}
end
if(package[:item_description])
xml.ItemDescription package[:item_description]
end
add_customer_references(xml, package)
if package[:special_services_requested] && package[:special_services_requested][:special_service_types]
xml.SpecialServicesRequested{
Expand Down Expand Up @@ -229,6 +236,7 @@ def add_packages(xml)
if package[:special_services_requested][:priority_alert_detail]
xml.PriorityAlertDetail package[:special_services_requested][:priority_alert_detail]
end

}
end
}
Expand Down Expand Up @@ -314,7 +322,7 @@ def sanitize_response_keys(response)

def service
raise NotImplementedError,
"Override service in subclass: {:id => 'service', :version => 1}"
"Override service in subclass: {:id => 'service', :version => 1}"
end

# Use GROUND_HOME_DELIVERY for shipments going to a residential address within the US.
Expand Down
111 changes: 111 additions & 0 deletions lib/fedex/request/pending_shipment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
require 'fedex/request/base'
require 'fedex/pending_shipment_label'

module Fedex
module Request
class PendingShipment < Shipment
attr_reader :response_details

def initialize(credentials, options={})
super(credentials, options)
@special_service_details= options[:special_service_details]
end

# Sends post request to Fedex web service and parse the response.
# A label file is created with the label at the specified location.
# The parsed Fedex response is available in #response_details
# e.g. response_details[:completed_shipment_detail][:completed_package_details][:tracking_ids][:tracking_number]
def process_request
api_response = self.class.post api_url, :body => build_xml

puts api_response if @debug
response = parse_response(api_response)
if success?(response)
success_response(api_response, response)
else
failure_response(api_response, response)
end
end

# Callback used after a failed shipment response.
def failure_response(api_response, response)
error_message = if response[:create_pending_shipment_reply]
[response[:create_pending_shipment_reply][:notifications]].flatten.first[:message]
else
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
end rescue $1
raise RateError, error_message
end

# Callback used after a successful shipment response.
def success_response(api_response, response)
@response_details = response[:create_pending_shipment_reply]
Fedex::PendingShipmentLabel.new @response_details
end

# Add information for shipments
def add_requested_shipment(xml)
xml.RequestedShipment{
xml.ShipTimestamp Time.now.utc.iso8601(2)
xml.DropoffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
xml.ServiceType service_type
xml.PackagingType @shipping_options[:packaging_type] ||= "YOUR_PACKAGING"
add_shipper(xml)
add_recipient(xml)
add_shipping_charges_payment(xml)
add_special_services_for_return(xml)
add_customs_clearance(xml) if @customs_clearance
add_custom_components(xml)
xml.RateRequestTypes "ACCOUNT"
add_packages(xml)
}
end

def add_special_services_for_return(xml)
xml.SpecialServicesRequested{
xml.SpecialServiceTypes 'RETURN_SHIPMENT'
xml.SpecialServiceTypes 'PENDING_SHIPMENT'
xml.ReturnShipmentDetail{
xml.ReturnType 'PENDING'
xml.ReturnEMailDetail{
xml.MerchantPhoneNumber @special_service_details[:special_services_requested][:return_shipment_detail][:return_email_detail][:merchant_phone_number]
}
}
xml.PendingShipmentDetail{
xml.Type "EMAIL"
xml.ExpirationDate @special_service_details[:special_services_requested][:pending_shipment_detail][:expiration_date]
xml.EmailLabelDetail{
xml.NotificationEMailAddress @special_service_details[:special_services_requested][:pending_shipment_detail][:email_label_detail][:notification_email_address]
xml.NotificationMessage @special_service_details[:special_services_requested][:pending_shipment_detail][:email_label_detail][:notification_message]
}
}

}

end

# Build xml Fedex Web Service request
def build_xml
builder = Nokogiri::XML::Builder.new do |xml|
xml.CreatePendingShipmentRequest(:xmlns => "http://fedex.com/ws/ship/v12"){
add_web_authentication_detail(xml)
add_client_detail(xml)
add_version(xml)
add_requested_shipment(xml)
}
end
builder.doc.root.to_xml
end

def service
{ :id => 'ship', :version => 12 }
end

def success?(response)
response[:create_pending_shipment_reply] &&
%w{SUCCESS WARNING NOTE}.include?(response[:create_pending_shipment_reply][:highest_severity])
end

end
end
end
5 changes: 3 additions & 2 deletions lib/fedex/request/rate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def process_request
response = parse_response(api_response)
if success?(response)
rate_details = [response[:rate_reply][:rate_reply_details][:rated_shipment_details]].flatten.first[:shipment_rate_detail]
Fedex::Rate.new(rate_details)
Fedex::Rate.new(rate_details.merge(response_details: response[:rate_reply]))
else
error_message = if response[:rate_reply]
[response[:rate_reply][:notifications]].flatten.first[:message]
Expand Down Expand Up @@ -46,14 +46,15 @@ def build_xml
add_web_authentication_detail(xml)
add_client_detail(xml)
add_version(xml)
xml.ReturnTransitAndCommit true
add_requested_shipment(xml)
}
end
builder.doc.root.to_xml
end

def service
{ :id => 'crs', :version => 10 }
{ :id => 'crs', :version => 13 }
end

# Successful request
Expand Down
30 changes: 20 additions & 10 deletions lib/fedex/request/shipment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ def initialize(credentials, options={})
requires! options
# Label specification is required even if we're not using it.
@label_specification = {
:label_format_type => 'COMMON2D',
:image_type => 'PDF',
:label_stock_type => 'PAPER_LETTER'
:label_format_type => 'COMMON2D',
:image_type => 'PDF',
:label_stock_type => 'PAPER_LETTER'
}
@label_specification.merge! options[:label_specification] if options[:label_specification]
end
Expand All @@ -32,6 +32,8 @@ def process_request
end
end



private

# Add information for shipments
Expand Down Expand Up @@ -68,22 +70,26 @@ def add_label_specification(xml)
# Callback used after a failed shipment response.
def failure_response(api_response, response)
error_message = if response[:process_shipment_reply]
[response[:process_shipment_reply][:notifications]].flatten.first[:message]
else
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
end rescue $1
[response[:process_shipment_reply][:notifications]].flatten.first[:message]
else
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
end rescue $1
raise RateError, error_message
end



Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see, according to the original formatting pattern, the line space was 1 line between function definitions.
I believe that having common formatting pattern improves readability :)

# Callback used after a successful shipment response.
def success_response(api_response, response)
@response_details = response[:process_shipment_reply]
end



# Build xml Fedex Web Service request
def build_xml
builder = Nokogiri::XML::Builder.new do |xml|
xml.ProcessShipmentRequest(:xmlns => "http://fedex.com/ws/ship/v12"){
xml.ProcessShipmentRequest(:xmlns => "http://fedex.com/ws/ship/v10"){
add_web_authentication_detail(xml)
add_client_detail(xml)
add_version(xml)
Expand All @@ -93,16 +99,20 @@ def build_xml
builder.doc.root.to_xml
end



def service
{ :id => 'ship', :version => 12 }
{ :id => 'ship', :version => 10 }
end

# Successful request
def success?(response)
response[:process_shipment_reply] &&
%w{SUCCESS WARNING NOTE}.include?(response[:process_shipment_reply][:highest_severity])
%w{SUCCESS WARNING NOTE}.include?(response[:process_shipment_reply][:highest_severity])
end



end
end
end
9 changes: 9 additions & 0 deletions lib/fedex/shipment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'fedex/request/rate'
require 'fedex/request/tracking_information'
require 'fedex/request/address'
require 'fedex/request/pending_shipment'

module Fedex
class Shipment
Expand Down Expand Up @@ -51,6 +52,14 @@ def ship(options = {})
Request::Shipment.new(@credentials, options).process_request
end

# @param [Hash] shipper, A hash containing the shipper information
# @param [Hash] recipient, A hash containing the recipient information
# @param [Array] packages, An arrary including a hash for each package being shipped
# @param [String] service_type, A valid fedex service type, to view a complete list of services Fedex::Shipment::SERVICE_TYPES
def pending_shipment(options = {})
Request::PendingShipment.new(@credentials, options).process_request
end

# @param [Hash] package_id, A string with the requested tracking number
# @param [Hash] package_type, A string identifitying the type of tracking number used. Full list Fedex::Track::PACKAGE_IDENTIFIER_TYPES
def track(options = {})
Expand Down
9 changes: 5 additions & 4 deletions spec/config/fedex_credentials.example.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
development:
:key: 'xxx'

:key: 'xxx'
:password: 'xxx'
:account_number: 'xxx'
:meter: 'xxx'
:mode: 'test'
:meter: 'xxx'
:mode: 'test'

production:
:key: 'xxx'
:password: 'xxx'
Expand Down
74 changes: 74 additions & 0 deletions spec/lib/fedex/pending_shipment_label_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require 'rspec'
require 'spec_helper'
require 'tmpdir'

module Fedex
describe PendingShipmentLabel do
describe "service for email label" do
let(:fedex) { Fedex::Shipment.new(fedex_production_credentials)}
let(:shipper) do
{:name => "Sender", :company => "Company", :phone_number => "555-555-5555", :address => "Main Street", :city => "Harrison", :state => "AR", :postal_code => "72601", :country_code => "US"}
end
let(:recipient) do
{:name => "Recipient", :company => "Company", :phone_number => "555-555-5555", :address => "Main Street", :city => "Frankin Park", :state => "IL", :postal_code => "60131", :country_code => "US", :residential => true }
end
let(:packages) do
[
{
:weight => {:units => "LB", :value => 2},:dimensions => {:length => 10, :width => 5, :height => 4, :units => "IN" },:item_description=>'Test'
}
]
end
let(:shipping_options) do
{ :packaging_type => "YOUR_PACKAGING", :drop_off_type => "REGULAR_PICKUP" }
end

let(:label_specification) do
{ :label_format_type => 'COMMON2D',
:image_type => 'PNG',
}
end

let(:filename) {
require 'tmpdir'
File.join(Dir.tmpdir, "label#{rand(15000)}.pdf")
}

let(:special_service_details) do
{
:special_services_requested=>{:return_shipment_detail=>{:return_email_detail=>{:merchant_phone_number=>'3101321223'}},:pending_shipment_detail=>{:expiration_date=>'2014-01-31',:email_label_detail=>{:notification_email_address=>'[email protected]',:notification_message=>'Test'}}}

}
end



let(:options) do
{ :shipper => shipper,
:recipient => recipient,
:packages => packages,
:service_type => "FEDEX_GROUND",
:special_service_details=>special_service_details,
:customs_clearance=> nil,
:shipping_details => shipping_options,
:label_specification => label_specification,
:filename => filename
}
end

describe "pending_shipment", :vcr do
before do
@pending_shipment_label = fedex.pending_shipment(options)
end


it "should return email label url" do
@pending_shipment_label.should respond_to('email_label_url')
end



end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, sorry to be annoying, but you know :)

end
end
end