simple_shipping 0.4.6

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 (101) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.metrics +6 -0
  4. data/.rspec +4 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.simplecov +43 -0
  8. data/Gemfile +26 -0
  9. data/Gemfile.lock +201 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.markdown +207 -0
  12. data/Rakefile +68 -0
  13. data/VERSION +1 -0
  14. data/coverage/.resultset.json +1579 -0
  15. data/lib/colorized_text.rb +34 -0
  16. data/lib/simple_shipping.rb +27 -0
  17. data/lib/simple_shipping/abstract.rb +11 -0
  18. data/lib/simple_shipping/abstract/builder.rb +47 -0
  19. data/lib/simple_shipping/abstract/client.rb +111 -0
  20. data/lib/simple_shipping/abstract/model.rb +40 -0
  21. data/lib/simple_shipping/abstract/request.rb +27 -0
  22. data/lib/simple_shipping/abstract/response.rb +26 -0
  23. data/lib/simple_shipping/address.rb +22 -0
  24. data/lib/simple_shipping/contact.rb +24 -0
  25. data/lib/simple_shipping/demo.rb +9 -0
  26. data/lib/simple_shipping/demo/base.rb +71 -0
  27. data/lib/simple_shipping/demo/fedex.rb +46 -0
  28. data/lib/simple_shipping/demo/ups.rb +68 -0
  29. data/lib/simple_shipping/exceptions.rb +40 -0
  30. data/lib/simple_shipping/fedex.rb +14 -0
  31. data/lib/simple_shipping/fedex/client.rb +41 -0
  32. data/lib/simple_shipping/fedex/package_builder.rb +24 -0
  33. data/lib/simple_shipping/fedex/party_builder.rb +50 -0
  34. data/lib/simple_shipping/fedex/request.rb +54 -0
  35. data/lib/simple_shipping/fedex/response.rb +5 -0
  36. data/lib/simple_shipping/fedex/shipment_builder.rb +123 -0
  37. data/lib/simple_shipping/fedex/shipment_request.rb +14 -0
  38. data/lib/simple_shipping/fedex/shipment_response.rb +12 -0
  39. data/lib/simple_shipping/package.rb +43 -0
  40. data/lib/simple_shipping/party.rb +21 -0
  41. data/lib/simple_shipping/shipment.rb +42 -0
  42. data/lib/simple_shipping/ups.rb +24 -0
  43. data/lib/simple_shipping/ups/client.rb +46 -0
  44. data/lib/simple_shipping/ups/package_builder.rb +101 -0
  45. data/lib/simple_shipping/ups/party_builder.rb +38 -0
  46. data/lib/simple_shipping/ups/request.rb +27 -0
  47. data/lib/simple_shipping/ups/response.rb +63 -0
  48. data/lib/simple_shipping/ups/ship_accept_request.rb +21 -0
  49. data/lib/simple_shipping/ups/ship_accept_response.rb +6 -0
  50. data/lib/simple_shipping/ups/ship_client.rb +70 -0
  51. data/lib/simple_shipping/ups/ship_confirm_request.rb +22 -0
  52. data/lib/simple_shipping/ups/ship_confirm_response.rb +6 -0
  53. data/lib/simple_shipping/ups/shipment_builder.rb +66 -0
  54. data/lib/simple_shipping/ups/shipment_request.rb +22 -0
  55. data/lib/simple_shipping/ups/shipment_response.rb +6 -0
  56. data/lib/simple_shipping/ups/void_client.rb +50 -0
  57. data/lib/simple_shipping/ups/void_request.rb +42 -0
  58. data/lib/simple_shipping/ups/void_response.rb +6 -0
  59. data/lib/tasks/demo.rake +58 -0
  60. data/script/ups_certification.rb +140 -0
  61. data/simple_shipping.gemspec +168 -0
  62. data/spec/fixtures/fedex_shipment_request.soap.xml.erb +85 -0
  63. data/spec/fixtures/fedex_shipment_response.soap.xml.erb +182 -0
  64. data/spec/fixtures/ups_shipment_request.soap.xml.erb +88 -0
  65. data/spec/fixtures/ups_shipment_response.soap.xml.erb +58 -0
  66. data/spec/fixtures/ups_shipment_response_with_faked_label_data.soap.xml.erb +54 -0
  67. data/spec/fixtures/ups_void_request.soap.xml.erb +29 -0
  68. data/spec/fixtures/ups_void_response.soap.xml.erb +21 -0
  69. data/spec/lib/simple_shipping/address_spec.rb +19 -0
  70. data/spec/lib/simple_shipping/contact_spec.rb +28 -0
  71. data/spec/lib/simple_shipping/exceptions_spec.rb +58 -0
  72. data/spec/lib/simple_shipping/fedex/package_builder_spec.rb +5 -0
  73. data/spec/lib/simple_shipping/fedex/party_builder_spec.rb +5 -0
  74. data/spec/lib/simple_shipping/fedex/response/shipment_reponse_spec.rb +5 -0
  75. data/spec/lib/simple_shipping/fedex/response_spec.rb +5 -0
  76. data/spec/lib/simple_shipping/fedex/shipment_builder_spec.rb +23 -0
  77. data/spec/lib/simple_shipping/package_spec.rb +32 -0
  78. data/spec/lib/simple_shipping/party_spec.rb +18 -0
  79. data/spec/lib/simple_shipping/shipment_spec.rb +35 -0
  80. data/spec/lib/simple_shipping/ups/package_builder_spec.rb +26 -0
  81. data/spec/lib/simple_shipping/ups/party_builder_spec.rb +47 -0
  82. data/spec/lib/simple_shipping/ups/response/shipment_response_spec.rb +5 -0
  83. data/spec/lib/simple_shipping/ups/response_spec.rb +33 -0
  84. data/spec/lib/simple_shipping/ups/shipment_builder_spec.rb +19 -0
  85. data/spec/requests/fedex_spec.rb +47 -0
  86. data/spec/requests/ups_spec.rb +75 -0
  87. data/spec/spec_helper.rb +47 -0
  88. data/spec/support/custom_matchers/basic_matcher.rb +13 -0
  89. data/spec/support/custom_matchers/have_attribute_matcher.rb +22 -0
  90. data/spec/support/custom_matchers/have_default_value_matcher.rb +26 -0
  91. data/spec/support/custom_matchers/have_errors_on_matcher.rb +23 -0
  92. data/spec/support/custom_matchers/validate_inclusion_of_matcher.rb +37 -0
  93. data/spec/support/custom_matchers/validate_presence_of_matcher.rb +24 -0
  94. data/spec/support/custom_matchers/validate_submodel_matcher.rb +44 -0
  95. data/spec/support/shared_behaviours/builders_behaviour.rb +9 -0
  96. data/spec/support/shared_behaviours/responses_behaviour.rb +10 -0
  97. data/tmp/metric_fu/_data/20131210.yml +9964 -0
  98. data/wsdl/fedex/ship_service_v10.wsdl +5566 -0
  99. data/wsdl/ups/Ship.wsdl +120 -0
  100. data/wsdl/ups/Void.wsdl +58 -0
  101. metadata +308 -0
@@ -0,0 +1,46 @@
1
+ # Helper object to send demo requests to FedEx in order to test credentials
2
+ # and the library.
3
+ #
4
+ # @example
5
+ # demo = SimpleShipping::Demo::Fedex.new(credentials)
6
+ # response = demo.shipment_request
7
+ class SimpleShipping::Demo::Fedex < SimpleShipping::Demo::Base
8
+ attr_reader :credentials
9
+
10
+ def initialize(options = {})
11
+ @options = options.reverse_merge(:log => false)
12
+ end
13
+
14
+ # Build the package object.
15
+ #
16
+ # @return [SimpleShipping::Package]
17
+ def package
18
+ @package ||= SimpleShipping::Package.new(
19
+ :weight => 1,
20
+ :length => 2,
21
+ :height => 3,
22
+ :dimension_units => :in, # you can use :kg as well
23
+ :weight_units => :lb, # you can use :cm as well
24
+ :width => 4,
25
+ :packaging_type => :your
26
+ )
27
+ end
28
+
29
+ # Initialize the FedEx client.
30
+ #
31
+ # @return [SimpleShipping::Fedex::Client]
32
+ def fedex_client
33
+ @fedex_client ||= SimpleShipping::Fedex::Client.new(
34
+ :credentials => options.slice(:key, :password, :account_number, :meter_number),
35
+ :log => options[:log],
36
+ :live => options[:live]
37
+ )
38
+ end
39
+
40
+ # Send the shipment request to FedEx.
41
+ #
42
+ # @return [SimpleShipping::Fedex::Response]
43
+ def shipment_request
44
+ fedex_client.shipment_request(shipper, recipient, package)
45
+ end
46
+ end
@@ -0,0 +1,68 @@
1
+ # Helper object to send demo requests to UPS in order to test credentials
2
+ # and the library.
3
+ #
4
+ # @example
5
+ # demo = SimpleShipping::Demo::Ups.new(credentials)
6
+ # response = demo.shipment_request
7
+ class SimpleShipping::Demo::Ups < SimpleShipping::Demo::Base
8
+ attr_reader :credentials
9
+
10
+ def initialize(options = {})
11
+ @options = options.reverse_merge(
12
+ :log => false,
13
+ :service_type => :second_day_air
14
+ )
15
+ end
16
+
17
+ # Build the package object.
18
+ #
19
+ # @return [SimpleShipping::Package]
20
+ def package
21
+ @package ||= SimpleShipping::Package.new(
22
+ :weight => 0.5,
23
+ :packaging_type => :envelope
24
+ )
25
+ end
26
+
27
+ # Initialize the UPS client for shipment requests.
28
+ #
29
+ # @return [SimpleShipping::Ups::ShipClient]
30
+ def ship_client
31
+ @ship_client ||= SimpleShipping::Ups::ShipClient.new(
32
+ :credentials => options.slice(:username, :password, :access_license_number),
33
+ :log => options[:log]
34
+ )
35
+ end
36
+
37
+ # Initialize the UPS client for void requests.
38
+ #
39
+ # @return [SimpleShipping::Ups::VoidClient]
40
+ def void_client
41
+ @void_client ||= SimpleShipping::Ups::VoidClient.new(
42
+ :credentials => options.slice(:username, :password, :access_license_number),
43
+ :log => options[:log],
44
+ :live => options[:live]
45
+ )
46
+ end
47
+
48
+ # Shipment Id. The number is picked randomly.
49
+ #
50
+ # @return [String]
51
+ def shipment_identification_number
52
+ @shipment_identification_number ||= '1234567890'
53
+ end
54
+
55
+ # Send a shipment request.
56
+ #
57
+ # @return [ShipClient::Ups::ShipmentResponse]
58
+ def shipment_request
59
+ ship_client.shipment_request(shipper, recipient, package, :service_type => options[:service_type])
60
+ end
61
+
62
+ # Send a request to void a shipment.
63
+ #
64
+ # @return [ShipClient::Ups::VoidResponse]
65
+ def void_request
66
+ void_client.void_request(shipment_identification_number)
67
+ end
68
+ end
@@ -0,0 +1,40 @@
1
+ module SimpleShipping
2
+ # Parent error for all SimpleShipping errors.
3
+ class Error < StandardError; end
4
+
5
+ # Error raises when response does not contain a label.
6
+ class NoLabelError < Error ; end
7
+
8
+ # Raises when some data is invalid or missing to build a request.
9
+ class ValidationError < Error
10
+ # @param model_or_msg [Abstract::Model, String]
11
+ def initialize(model_or_msg)
12
+ @message = case model_or_msg
13
+ when Abstract::Model
14
+ "Invalid model #{model_or_msg.class}. Validation errors: #{model_or_msg.errors.full_messages.join(', ')}"
15
+ when String
16
+ model_or_msg
17
+ end
18
+
19
+ super(@message)
20
+ end
21
+ end
22
+
23
+ # Raised when a remote request fails.
24
+ class RequestError < Error
25
+ # @param [Savon::SOAPFault] savon_fault Savon exception
26
+ def initialize(savon_fault)
27
+ fault = savon_fault.to_hash[:fault]
28
+
29
+ @message =
30
+ if fault[:faultcode] # SOAP 1.1 fault.
31
+ detail = fault[:detail][:errors][:error_detail][:primary_error_code]
32
+ "#{fault[:faultstring]} (#{detail[:code]}) #{detail[:description]}"
33
+ elsif fault[:code] # SOAP 1.2 fault.
34
+ "(#{fault[:code][:value]}) #{fault[:reason][:text]}"
35
+ end
36
+
37
+ super(@message)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ # Namespace for FedEx provider.
2
+ module SimpleShipping::Fedex
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Client
6
+ autoload :Request
7
+ autoload :Response
8
+ autoload :ShipmentBuilder
9
+ autoload :PackageBuilder
10
+ autoload :PartyBuilder
11
+
12
+ autoload :ShipmentRequest
13
+ autoload :ShipmentResponse
14
+ end
@@ -0,0 +1,41 @@
1
+ module SimpleShipping::Fedex
2
+ # Required credentials:
3
+ # * _key_
4
+ # * _password_
5
+ # * _account_number_
6
+ # * _meter_number_
7
+ #
8
+ # = Usage
9
+ # client = SimpleShipping::Fedex::Client.new(:key => "KEY",
10
+ # :password => "PASSWORD",
11
+ # :account_number => "ACCOUNT NUMBER",
12
+ # :METER_NUMBER => "METER NUMBER")
13
+ # client.request(shipper, recipient, package) # => #<SimpleShipping::Fedex::Response ...>
14
+ class Client < SimpleShipping::Abstract::Client
15
+ set_required_credentials :key, :password, :account_number, :meter_number
16
+
17
+ set_wsdl_document File.join(SimpleShipping::WSDL_DIR, "fedex/ship_service_v10.wsdl")
18
+ set_testing_address "https://wsbeta.fedex.com:443/web-services/ship"
19
+ set_production_address "https://wsbeta.fedex.com:443/web-services/ship" # Not configured
20
+
21
+ # Send the shipment request to FedEx.
22
+ def shipment_request(shipper, recipient, package, opts = {})
23
+ shipment = create_shipment(shipper, recipient, package, opts)
24
+ request = ShipmentRequest.new(@credentials, shipment)
25
+ execute(request)
26
+ end
27
+
28
+ # Send the shipment confirmation request.
29
+ def ship_confirm_request(shipper, recipient, package, opts = {})
30
+ fail "Not Implemented"
31
+ end
32
+
33
+ # Send the ProcessShipmentRequest request to the FedEx service and return
34
+ # the response wrapped in a {Fedex::Response} object.
35
+ def execute(request)
36
+ savon_response = @client.call(request.type, :message => request.body)
37
+ request.response(savon_response)
38
+ end
39
+ private :execute
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ module SimpleShipping::Fedex
2
+ # Knows how to convert {Package} model to SOAP element for FedEx.
3
+ class PackageBuilder < SimpleShipping::Abstract::Builder
4
+ # FedEx mapping for weight units.
5
+ WEIGHT_UNITS = {:kg => 'KG',
6
+ :lb => 'LB'}
7
+ # FedEx mapping for dimension units.
8
+ DIMENSION_UNITS = {:in => 'IN',
9
+ :cm => 'CM'}
10
+
11
+ # Build a SOAP package element as a hash for Savon.
12
+ def build
13
+ { 'Weight' => {'Units' => WEIGHT_UNITS[@model.weight_units],
14
+ 'Value' => @model.weight,
15
+ :order! => ['Units', 'Value']},
16
+ 'Dimensions' => {'Length' => @model.length,
17
+ 'Width' => @model.width,
18
+ 'Height' => @model.height,
19
+ 'Units' => DIMENSION_UNITS[@model.dimension_units],
20
+ :order! => ['Length', 'Width', 'Height', 'Units']},
21
+ :order! => ['Weight', 'Dimensions']}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ module SimpleShipping::Fedex
2
+ # Knows how to convert {Party} model to SOAP element for FedEx.
3
+ class PartyBuilder < SimpleShipping::Abstract::Builder
4
+ # Build a SOAP party element as a hash for Savon.
5
+ def build
6
+ {'Contact' => build_contact,
7
+ 'Address' => build_address,
8
+ :order! => ['Contact', 'Address']}
9
+ end
10
+
11
+ # Build the body for a Contact element.
12
+ #
13
+ # @return [Hash]
14
+ def build_contact
15
+ result = {:order! => []}
16
+ contact = @model.contact
17
+ if contact.company_name
18
+ result['CompanyName'] = contact.company_name
19
+ result[:order!] << 'CompanyName'
20
+ end
21
+ if contact.person_name
22
+ result['PersonName'] = contact.person_name
23
+ result[:order!] << 'PersonName'
24
+ end
25
+ result['PhoneNumber'] = contact.phone_number
26
+ result[:order!] << 'PhoneNumber'
27
+ result
28
+ end
29
+ private :build_contact
30
+
31
+ # Build body for Address element.
32
+ #
33
+ # @return [Hash]
34
+ def build_address
35
+ addr = @model.address
36
+ {'StreetLines' => addr.street_line,
37
+ 'City' => addr.city,
38
+ 'StateOrProvinceCode' => addr.state_code,
39
+ 'PostalCode' => addr.postal_code,
40
+ 'CountryCode' => addr.country_code,
41
+ :order! => [ 'StreetLines',
42
+ 'City',
43
+ 'StateOrProvinceCode',
44
+ 'PostalCode',
45
+ 'CountryCode' ]
46
+ }
47
+ end
48
+ private :build_address
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ module SimpleShipping::Fedex
2
+ # Builds a complete request for the FedEx service.
3
+ class Request < SimpleShipping::Abstract::Request
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :ShipmentRequest
7
+
8
+ def initialize(credentials, shipment)
9
+ super(credentials)
10
+ @shipment = shipment
11
+ end
12
+
13
+ # Build a complete request from a {Shipment shipment} object.
14
+ def body(opts = {})
15
+ {'WebAuthenticationDetail' => web_authentication_detail,
16
+ 'ClientDetail' => client_detail,
17
+ 'Version' => version,
18
+ 'RequestedShipment' => ShipmentBuilder.build(@shipment, opts),
19
+ :order! => ['WebAuthenticationDetail', 'ClientDetail', 'Version', 'RequestedShipment'] }
20
+ end
21
+
22
+ # Build the body for the WebAuthenticationDetail element.
23
+ #
24
+ # @return [Hash]
25
+ def web_authentication_detail
26
+ { 'UserCredential' => {'Key' => @credentials.key,
27
+ 'Password' => @credentials.password,
28
+ :order! => ['Key', 'Password']}}
29
+ end
30
+ private :web_authentication_detail
31
+
32
+ # Build the body for the UserCredential element.
33
+ #
34
+ # @return [Hash]
35
+ def client_detail
36
+ {'AccountNumber' => @credentials.account_number,
37
+ 'MeterNumber' => @credentials.meter_number,
38
+ :order! => ['AccountNumber', 'MeterNumber']}
39
+ end
40
+ private :client_detail
41
+
42
+ # Build the body for the Version element.
43
+ #
44
+ # @return [Hash]
45
+ def version
46
+ {'ServiceId' => 'ship',
47
+ 'Major' => '10',
48
+ 'Intermediate' => '0',
49
+ 'Minor' => '0',
50
+ :order! => ['ServiceId', 'Major', 'Intermediate', 'Minor']}
51
+ end
52
+ private :version
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ module SimpleShipping::Fedex
2
+ # A wrapper for FedEx response.
3
+ class Response < SimpleShipping::Abstract::Response
4
+ end
5
+ end
@@ -0,0 +1,123 @@
1
+ module SimpleShipping::Fedex
2
+ # Builds a shipment element for FedEx SOAP service.
3
+ class ShipmentBuilder < SimpleShipping::Abstract::Builder
4
+ # Value for RateRequestTypes XML element.
5
+ RATE_REQUEST_TYPE = 'ACCOUNT'
6
+
7
+ # Number of packages.
8
+ PACKAGE_COUNT = '1'
9
+
10
+ # Mapping for package types
11
+ PACKAGING_TYPES = {
12
+ :box_10kg => 'FEDEX_10KG_BOX',
13
+ :box_25kg => 'FEDEX_25KG_BOX',
14
+ :box => 'FEDEX_BOX',
15
+ :envelope => 'FEDEX_ENVELOPE',
16
+ :pak => 'FEDEX_PAK',
17
+ :tube => 'FEDEX_TUBE',
18
+ :your => 'YOUR_PACKAGING'
19
+ }
20
+
21
+ # Mapping for service types
22
+ SERVICE_TYPES = {
23
+ :europe_first_international_priority => 'EUROPE_FIRST_INTERNATIONAL_PRIORITY',
24
+ :fedex_1_day_freight => 'FEDEX_1_DAY_FREIGHT',
25
+ :fedex_2_day_am => 'FEDEX_2_DAY',
26
+ :fedex_2_day_freight => 'FEDEX_2_DAY_AM',
27
+ :fedex_3_day_freight => 'FEDEX_2_DAY_FREIGHT',
28
+ :fedex_3_day_freight => 'FEDEX_3_DAY_FREIGHT',
29
+ :fedex_express_saver => 'FEDEX_EXPRESS_SAVER',
30
+ :fedex_freight_economy => 'FEDEX_FIRST_FREIGHT',
31
+ :fedex_freight_economy => 'FEDEX_FREIGHT_ECONOMY',
32
+ :fedex_freight_priority => 'FEDEX_FREIGHT_PRIORITY',
33
+ :fedex_ground => 'FEDEX_GROUND',
34
+ :first_overnight => 'FIRST_OVERNIGHT',
35
+ :ground_home_delivery => 'GROUND_HOME_DELIVERY',
36
+ :international_economy => 'INTERNATIONAL_ECONOMY',
37
+ :international_economy_freight => 'INTERNATIONAL_ECONOMY_FREIGHT',
38
+ :international_first => 'INTERNATIONAL_FIRST',
39
+ :international_priority => 'INTERNATIONAL_PRIORITY',
40
+ :international_priority_freight => 'INTERNATIONAL_PRIORITY_FREIGHT',
41
+ :priority_overnight => 'PRIORITY_OVERNIGHT',
42
+ :smart_post => 'SMART_POST',
43
+ :standard_overnight => 'STANDARD_OVERNIGHT'
44
+ }
45
+
46
+ # Mapping for dropoff types.
47
+ DROPOFF_TYPES = {
48
+ :business_service_center => 'BUSINESS_SERVICE_CENTER',
49
+ :drop_box => 'DROP_BOX',
50
+ :regular_pickup => 'REGULAR_PICKUP',
51
+ :request_courier => 'REQUEST_COURIER',
52
+ :station => 'STATION'
53
+ }
54
+
55
+ set_default_opts :dropoff_type => :business_service_center,
56
+ :service_type => :fedex_ground
57
+
58
+ # Build the shipment representation as a hash for the Savon client.
59
+ def build
60
+ {'ShipTimestamp' => ship_timestamp,
61
+ 'DropoffType' => DROPOFF_TYPES[@opts[:dropoff_type]],
62
+ 'ServiceType' => SERVICE_TYPES[@opts[:service_type]],
63
+ 'PackagingType' => PACKAGING_TYPES[@model.package.packaging_type],
64
+ 'Shipper' => PartyBuilder.build(@model.shipper),
65
+ 'Recipient' => PartyBuilder.build(@model.recipient),
66
+ 'ShippingChargesPayment' => shipping_charges_payment,
67
+ 'LabelSpecification' => label_specification,
68
+ 'RateRequestTypes' => RATE_REQUEST_TYPE,
69
+ 'PackageCount' => PACKAGE_COUNT,
70
+ 'RequestedPackageLineItems' => PackageBuilder.build(@model.package),
71
+ :order! => ['ShipTimestamp' , 'DropoffType' , 'ServiceType' , 'PackagingType',
72
+ 'Shipper' , 'Recipient' , 'ShippingChargesPayment', 'LabelSpecification',
73
+ 'RateRequestTypes', 'PackageCount' , 'RequestedPackageLineItems']
74
+ }
75
+ end
76
+
77
+ # Perform validations.
78
+ def validate
79
+ validate_inclusion_of(:dropoff_type , DROPOFF_TYPES)
80
+ validate_inclusion_of(:service_type , SERVICE_TYPES)
81
+ end
82
+
83
+ # Get the shipping timestamps in a specific format.
84
+ #
85
+ # @example
86
+ # ship_timestamp # => "2014-01-08T14:41:53+02:00"
87
+ #
88
+ # @return [String]
89
+ def ship_timestamp
90
+ Time.new.strftime('%Y-%m-%dT%H:%M:%S%z').tap{|str| str[-2,0] = ':' }
91
+ end
92
+ private :ship_timestamp
93
+
94
+ # Build the hash for the ShippingChargesPayment element.
95
+ #
96
+ # @return [Hash]
97
+ def shipping_charges_payment
98
+ {'PaymentType' => payment_type,
99
+ 'Payor' => {'AccountNumber' => @model.payor_account_number},
100
+ :order! => ['PaymentType', 'Payor']}
101
+ end
102
+ private :shipping_charges_payment
103
+
104
+ # Get the payment type.
105
+ #
106
+ # @return [String]
107
+ def payment_type
108
+ (@model.payor == :shipper) ? 'SENDER' : 'RECIPIENT'
109
+ end
110
+ private :payment_type
111
+
112
+ # Build the label parameters according to FedEx's API.
113
+ #
114
+ # @return [Hash]
115
+ def label_specification
116
+ { 'LabelFormatType' => 'COMMON2D',
117
+ 'ImageType' => 'PNG',
118
+ 'LabelStockType' => 'PAPER_4X6',
119
+ :order! => ['LabelFormatType', 'ImageType', 'LabelStockType'] }
120
+ end
121
+ private :label_specification
122
+ end
123
+ end