simple_shipping 0.4.6

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