ship_compliant 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +7 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +50 -0
  7. data/Rakefile +47 -0
  8. data/cucumber.yml +9 -0
  9. data/features/add_update_brand.feature +20 -0
  10. data/features/add_update_product.feature +24 -0
  11. data/features/check_compliance_of_sales_order_with_address_validations.feature +18 -0
  12. data/features/commit_sales_order.feature +6 -0
  13. data/features/get_inventory_details.feature +7 -0
  14. data/features/get_sales_order_extended.feature +15 -0
  15. data/features/search_sales_orders.feature +13 -0
  16. data/features/step_definitions/brand_steps.rb +73 -0
  17. data/features/step_definitions/commit_sales_order/all_shipments.rb +15 -0
  18. data/features/step_definitions/compliance_check/available_product_steps.rb +175 -0
  19. data/features/step_definitions/compliance_check/missing_product_steps.rb +102 -0
  20. data/features/step_definitions/compliance_check/noncompliant_product.rb +102 -0
  21. data/features/step_definitions/credential_steps.rb +8 -0
  22. data/features/step_definitions/inventory_steps.rb +36 -0
  23. data/features/step_definitions/product_steps.rb +140 -0
  24. data/features/step_definitions/sales_order_extended_steps.rb +79 -0
  25. data/features/step_definitions/search_order_steps.rb +87 -0
  26. data/features/step_definitions/void_order_steps.rb +27 -0
  27. data/features/support/env.rb +14 -0
  28. data/features/void_sales_order.feature +11 -0
  29. data/fixtures/vcr_cassettes/brand_already_exists.yml +1038 -0
  30. data/fixtures/vcr_cassettes/brand_ignore_existing.yml +1037 -0
  31. data/fixtures/vcr_cassettes/brand_update_existing.yml +1037 -0
  32. data/fixtures/vcr_cassettes/brand_valid.yml +1037 -0
  33. data/fixtures/vcr_cassettes/commit_salesorder_all_shipments.yml +1034 -0
  34. data/fixtures/vcr_cassettes/compliance_available_product.yml +1075 -0
  35. data/fixtures/vcr_cassettes/compliance_missing_product.yml +1046 -0
  36. data/fixtures/vcr_cassettes/compliance_noncompliant_product.yml +1082 -0
  37. data/fixtures/vcr_cassettes/ignore_existing_product.yml +1035 -0
  38. data/fixtures/vcr_cassettes/invalid_search_sales_orders.yml +1038 -0
  39. data/fixtures/vcr_cassettes/inventory_details_for_everything.yml +1037 -0
  40. data/fixtures/vcr_cassettes/product_already_exists.yml +1036 -0
  41. data/fixtures/vcr_cassettes/product_invalid_brand.yml +1036 -0
  42. data/fixtures/vcr_cassettes/product_valid_brand.yml +1035 -0
  43. data/fixtures/vcr_cassettes/sales_order_extended.yml +1042 -0
  44. data/fixtures/vcr_cassettes/sales_order_missing.yml +1034 -0
  45. data/fixtures/vcr_cassettes/search_sales_orders.yml +1039 -0
  46. data/fixtures/vcr_cassettes/update_product.yml +1035 -0
  47. data/fixtures/vcr_cassettes/void_order.yml +1033 -0
  48. data/fixtures/vcr_cassettes/void_voided_order.yml +1034 -0
  49. data/lib/ship_compliant.rb +62 -0
  50. data/lib/ship_compliant/add_update_brand.rb +50 -0
  51. data/lib/ship_compliant/add_update_brand_result.rb +6 -0
  52. data/lib/ship_compliant/add_update_product.rb +66 -0
  53. data/lib/ship_compliant/add_update_product_result.rb +6 -0
  54. data/lib/ship_compliant/address.rb +87 -0
  55. data/lib/ship_compliant/address/suggested_address.rb +37 -0
  56. data/lib/ship_compliant/base_result.rb +43 -0
  57. data/lib/ship_compliant/channel_details.rb +31 -0
  58. data/lib/ship_compliant/check_compliance.rb +41 -0
  59. data/lib/ship_compliant/check_compliance_result.rb +95 -0
  60. data/lib/ship_compliant/client.rb +54 -0
  61. data/lib/ship_compliant/commit_sales_order.rb +48 -0
  62. data/lib/ship_compliant/commit_sales_order_result.rb +30 -0
  63. data/lib/ship_compliant/compliance_rule.rb +30 -0
  64. data/lib/ship_compliant/configuration.rb +46 -0
  65. data/lib/ship_compliant/error_result.rb +41 -0
  66. data/lib/ship_compliant/freight_sales_tax_rate.rb +8 -0
  67. data/lib/ship_compliant/get_inventory_details.rb +31 -0
  68. data/lib/ship_compliant/get_inventory_details_result.rb +41 -0
  69. data/lib/ship_compliant/get_sales_order_extended.rb +23 -0
  70. data/lib/ship_compliant/get_sales_order_extended_result.rb +65 -0
  71. data/lib/ship_compliant/inventory_product.rb +95 -0
  72. data/lib/ship_compliant/order_search.rb +92 -0
  73. data/lib/ship_compliant/package.rb +13 -0
  74. data/lib/ship_compliant/product_attributes.rb +98 -0
  75. data/lib/ship_compliant/product_sales_tax_rate.rb +23 -0
  76. data/lib/ship_compliant/sales_tax_rate.rb +22 -0
  77. data/lib/ship_compliant/search_sales_order_summary.rb +30 -0
  78. data/lib/ship_compliant/search_sales_orders.rb +53 -0
  79. data/lib/ship_compliant/search_sales_orders_result.rb +106 -0
  80. data/lib/ship_compliant/shipment.rb +59 -0
  81. data/lib/ship_compliant/shipment_compliance.rb +28 -0
  82. data/lib/ship_compliant/shipment_sales_tax_rate.rb +15 -0
  83. data/lib/ship_compliant/version.rb +3 -0
  84. data/lib/ship_compliant/void_sales_order.rb +42 -0
  85. data/lib/ship_compliant/void_sales_order_result.rb +20 -0
  86. data/ship_compliant.gemspec +33 -0
  87. data/spec/fixtures/add_update_product.xml +22 -0
  88. data/spec/fixtures/check_compliance.xml +125 -0
  89. data/spec/fixtures/coreservice.wsdl +1341 -0
  90. data/spec/fixtures/search_sales_orders.xml +52 -0
  91. data/spec/fixtures/void_order_failure.xml +31 -0
  92. data/spec/fixtures/void_order_success.xml +22 -0
  93. data/spec/lib/ship_compliant/add_update_brand_result_spec.rb +7 -0
  94. data/spec/lib/ship_compliant/add_update_brand_spec.rb +58 -0
  95. data/spec/lib/ship_compliant/add_update_product_result_spec.rb +7 -0
  96. data/spec/lib/ship_compliant/add_update_product_spec.rb +52 -0
  97. data/spec/lib/ship_compliant/address/suggested_address_spec.rb +28 -0
  98. data/spec/lib/ship_compliant/address_spec.rb +123 -0
  99. data/spec/lib/ship_compliant/base_result_spec.rb +127 -0
  100. data/spec/lib/ship_compliant/channel_details_spec.rb +40 -0
  101. data/spec/lib/ship_compliant/check_compliance_result_spec.rb +135 -0
  102. data/spec/lib/ship_compliant/check_compliance_spec.rb +43 -0
  103. data/spec/lib/ship_compliant/client_spec.rb +73 -0
  104. data/spec/lib/ship_compliant/commit_sales_order_result_spec.rb +32 -0
  105. data/spec/lib/ship_compliant/commit_sales_order_spec.rb +38 -0
  106. data/spec/lib/ship_compliant/compliance_rule_spec.rb +47 -0
  107. data/spec/lib/ship_compliant/configuration_spec.rb +47 -0
  108. data/spec/lib/ship_compliant/error_result_spec.rb +47 -0
  109. data/spec/lib/ship_compliant/freight_sales_tax_rate_spec.rb +7 -0
  110. data/spec/lib/ship_compliant/get_inventory_details_result_spec.rb +87 -0
  111. data/spec/lib/ship_compliant/get_inventory_details_spec.rb +35 -0
  112. data/spec/lib/ship_compliant/get_sales_order_extended_result_spec.rb +84 -0
  113. data/spec/lib/ship_compliant/get_sales_order_extended_spec.rb +39 -0
  114. data/spec/lib/ship_compliant/inventory_product_spec.rb +116 -0
  115. data/spec/lib/ship_compliant/order_search_spec.rb +21 -0
  116. data/spec/lib/ship_compliant/package_spec.rb +26 -0
  117. data/spec/lib/ship_compliant/product_attributes_spec.rb +36 -0
  118. data/spec/lib/ship_compliant/product_sales_tax_rate_spec.rb +22 -0
  119. data/spec/lib/ship_compliant/sales_tax_rate_spec.rb +21 -0
  120. data/spec/lib/ship_compliant/search_sales_order_summary_spec.rb +56 -0
  121. data/spec/lib/ship_compliant/search_sales_orders_result_spec.rb +121 -0
  122. data/spec/lib/ship_compliant/search_sales_orders_spec.rb +42 -0
  123. data/spec/lib/ship_compliant/shipment_compliance_spec.rb +46 -0
  124. data/spec/lib/ship_compliant/shipment_sales_tax_rate_spec.rb +20 -0
  125. data/spec/lib/ship_compliant/shipment_spec.rb +106 -0
  126. data/spec/lib/ship_compliant/void_sales_order_result_spec.rb +7 -0
  127. data/spec/lib/ship_compliant/void_sales_order_spec.rb +41 -0
  128. data/spec/spec_helper.rb +50 -0
  129. metadata +366 -0
@@ -0,0 +1,54 @@
1
+ module ShipCompliant
2
+ class << self
3
+ attr_accessor :ship_compliant_client
4
+ end
5
+
6
+ # Returns an instance of +Client+.
7
+ def self.client
8
+ self.ship_compliant_client ||= new_client_from_wsdl(configuration.wsdl)
9
+ end
10
+
11
+ # Replaces #client with custom WSDL
12
+ #
13
+ # ShipCompliant.wsdl = 'https://ws-dev.shipcompliant.com/Services/1.2/ProductService.asmx?WSDL'
14
+ def self.wsdl=(wsdl)
15
+ self.ship_compliant_client = new_client_from_wsdl(wsdl)
16
+ end
17
+
18
+ private
19
+
20
+ # Creates a new client from a WSDL url.
21
+ def self.new_client_from_wsdl(wsdl)
22
+ Client.new(wsdl: wsdl, log: configuration.log, filters: %W[PartnerKey Username Password])
23
+ end
24
+
25
+ class Client < Savon::Client
26
+ # "Backup" original #call from Savon::Client
27
+ alias_method :savon_call, :call
28
+
29
+ # Adds the required security credentials and formats
30
+ # the message to match the ShipCompliant structure.
31
+ #
32
+ # ShipCompliant.client.call(:some_operation, {
33
+ # 'SomeKey' => 'SomeValue'
34
+ # })
35
+ def call(operation, locals = {})
36
+ locals['Security'] = ShipCompliant.configuration.credentials
37
+
38
+ response = savon_call(operation, message: {
39
+ 'Request' => locals
40
+ })
41
+
42
+ get_result_from_response(operation, response)
43
+ end
44
+
45
+ private
46
+
47
+ def get_result_from_response(operation, response)
48
+ key = operation.to_s
49
+ resp_key = (key + '_response').to_sym
50
+ result_key = (key + '_result').to_sym
51
+ response.to_hash.fetch(resp_key, {}).fetch(result_key, {})
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,48 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::CommitSalesOrder
3
+ #
4
+ # This method takes in a Sales Order Key in the request and saves the order
5
+ # if it has previously been checked for compliance.
6
+ #
7
+ # - If the CommitOption is set to “AllShipments”, all the shipments in the sales order will be committed.
8
+ # - If it is set to “CompliantShipments”, just the non-committed compliant sales orders will be committed.
9
+ #
10
+ # Please keep in mind that if “CompliantShipments” is selected, it will be of
11
+ # great value to the customer if the non-compliant, non-committed orders were
12
+ # flagged as such for further review.
13
+ #
14
+ # If the CommitOption is not specified, zero shipments will be committed.
15
+ # This allows the integrator to get the commit status of the shipments in a
16
+ # sales order without committing any of them.
17
+ #
18
+ # result = ShipCompliant::CommitSalesOrder.call({
19
+ # commit_options: 'AllShipments',
20
+ # payments: nil,
21
+ # sales_tax_collected: 0,
22
+ # sales_order_key: 'ORDER-KEY'
23
+ # })
24
+ #
25
+ # puts result.success? #=> true
26
+ # puts "The following shipments were committed."
27
+ #
28
+ # puts result.committed_shipments #=> ['1', '2', ...]
29
+ class CommitSalesOrder
30
+
31
+ # Marks a SalesOrder as committed.
32
+ #
33
+ # Returns an instance of ShipCompliant::CommitSalesOrderResult.
34
+ #
35
+ # ShipCompliant::CommitSalesOrder.call({
36
+ # commit_options: 'CompliantShipments',
37
+ # payments: nil,
38
+ # sales_tax_collected: 0,
39
+ # sales_order_key: 'ORDER-KEY'
40
+ # })
41
+ def self.call(commit_data)
42
+ commit_data.deep_transform_keys! { |key| key.to_s.camelize }
43
+ result = ShipCompliant.client.call(:commit_sales_order, commit_data)
44
+ CommitSalesOrderResult.new(result)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::CommitSalesOrderResult
3
+ #
4
+ # Wraps the response of +CommitSalesOrder+ and provides methods to quickly
5
+ # access shipments, and the committed shipment keys.
6
+ class CommitSalesOrderResult < Struct.new(:response)
7
+ include BaseResult
8
+
9
+ # Returns an array of shipment objects.
10
+ #
11
+ # puts result.shipments #=> [
12
+ # {
13
+ # key: 'SHIPMENT-KEY',
14
+ # is_committed: true/false
15
+ # }
16
+ # ]
17
+ def shipments
18
+ Array.wrap(response[:shipments][:shipment_commit_response])
19
+ end
20
+
21
+ # Gets the keys for committed shipments.
22
+ #
23
+ # puts result.committed_shipments #=> ['SHIPMENT-1', 'SHIPMENT-2', ...]
24
+ def committed_shipments
25
+ shipments
26
+ .select { |s| s[:is_committed] == true }
27
+ .map { |s| s[:key] }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module ShipCompliant
2
+ class ComplianceRule < Struct.new(:response)
3
+
4
+ # Returns the +ComplianceDescription+.
5
+ def compliance_description
6
+ response[:compliance_description]
7
+ end
8
+
9
+ # Checks if the current compliance rule was met.
10
+ def compliant?
11
+ response[:is_compliant]
12
+ end
13
+
14
+ # Returns the +RuleDescription+.
15
+ def rule_description
16
+ response[:rule_description]
17
+ end
18
+
19
+ # Returns the +LicenseRelationship+.
20
+ def license_relationship
21
+ response[:license_relationship]
22
+ end
23
+
24
+ # Returns the +RuleType+.
25
+ def rule_type
26
+ response[:rule_type]
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ module ShipCompliant
2
+ class << self
3
+ attr_accessor :configuration
4
+ end
5
+
6
+ def self.configure
7
+ yield(configuration)
8
+ end
9
+
10
+ def self.configuration
11
+ @configuration ||= Configuration.new
12
+ end
13
+
14
+ # Stores runtime configuration to authenticate user.
15
+ #
16
+ # ShipCompliant.configure do |c|
17
+ # c.partner_key = 'XXXX-XX-XXXXX'
18
+ # c.username = 'bob@example.com'
19
+ # c.password = 'secret'
20
+ # c.log = true # savon log
21
+ # end
22
+ class Configuration
23
+ attr_accessor :partner_key, :username, :password
24
+ attr_accessor :wsdl, :log
25
+
26
+ def initialize
27
+ @log = true
28
+ @wsdl = 'https://ws-dev.shipcompliant.com/services/1.2/coreservice.asmx?WSDL'
29
+ end
30
+
31
+ # Returns a +Hash+ for authenticating each API request.
32
+ #
33
+ # {
34
+ # 'PartnerKey' => 'XXXX-XX-XXXXX',
35
+ # 'Username' => 'bob@example.com',
36
+ # 'Password' => 'secret'
37
+ # }
38
+ def credentials
39
+ {
40
+ 'PartnerKey' => partner_key,
41
+ 'Username' => username,
42
+ 'Password' => password
43
+ }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ module ShipCompliant
2
+ class ErrorResult < Struct.new(:error)
3
+
4
+ # Gets the error code as an integer.
5
+ #
6
+ # puts "ERROR ##{error.code}" #=> 404
7
+ def code
8
+ error[:code].to_i
9
+ end
10
+
11
+ # Gets the key of the item with an error.
12
+ #
13
+ # puts error.key #=> 'ORDER-123'
14
+ def key
15
+ error[:key]
16
+ end
17
+
18
+ # Gets the error message.
19
+ #
20
+ # puts "ERROR: #{error.message}" #=> 'I don't think it's a good idea,
21
+ # Yogi'
22
+ def message
23
+ error[:message]
24
+ end
25
+
26
+ # The object type that had the error.
27
+ #
28
+ # puts error.target #=> 'SalesOrder'
29
+ def target
30
+ error[:target]
31
+ end
32
+
33
+ # The type of error that occured.
34
+ #
35
+ # puts error.type #=> 'Validation'
36
+ def type
37
+ error[:type]
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,8 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::FreightSalesTaxRate
3
+ #
4
+ # FreightSalesTaxRate wraps the SalesTaxRate value object. It currently
5
+ # doesn't do anything special.
6
+ class FreightSalesTaxRate < SalesTaxRate
7
+ end
8
+ end
@@ -0,0 +1,31 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::GetInventoryDetails
3
+ #
4
+ # Provides inventory details for each Product on a Fulfillment Location,
5
+ # Fulfillment Account basis.
6
+ #
7
+ # result = ShipCompliant::GetInventoryDetails.call
8
+ # product = result.products_for_location('WineShipping').first
9
+ #
10
+ # product.inventory_levels #=> {
11
+ # available: 25,
12
+ # reserved: 13
13
+ # }
14
+ class GetInventoryDetails
15
+
16
+ # Returns inventory details for the specified criteria. The method may be
17
+ # called without any parameters to return all products with Available
18
+ # inventory.
19
+ #
20
+ # ShipCompliant::GetInventoryDetails.call({
21
+ # brand_key: 'BRAND-KEY',
22
+ # fulfillment_location: 'WineShipping'
23
+ # })
24
+ def self.call(query = {})
25
+ query.deep_transform_keys! { |k| k.to_s.camelize }
26
+ result = ShipCompliant.client.call(:get_inventory_details, query)
27
+ GetInventoryDetailsResult.new(result)
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::GetInventoryDetailsResult
3
+ #
4
+ # Wraps the result of +GetInventoryDetails+ and provides methods to access
5
+ # locations and products for a location
6
+ class GetInventoryDetailsResult < Struct.new(:response)
7
+ include BaseResult
8
+
9
+ # Returns an array of +InventoryLocation+ nodes.
10
+ #
11
+ # result.locations[0][:sub_inventory_code] #=> 'SUB-CODE'
12
+ def locations
13
+ Array.wrap(response[:inventory_locations]).map do |location|
14
+ location[:inventory_location]
15
+ end
16
+ end
17
+
18
+ # Finds a location by +FulfillmentLocation+.
19
+ #
20
+ # result.location('WineShipping')[:supplier] #=> 'LOCATION-SUPPLIER'
21
+ def location(key)
22
+ location = locations.select { |l| l[:fulfillment_location] == key }.first
23
+
24
+ return {} if location.nil?
25
+ location
26
+ end
27
+
28
+ # Returns an array of ShipCompliant::InventoryProduct.
29
+ #
30
+ # result.products_for_location('WineShipping').each do |product|
31
+ # puts product.product_key
32
+ # puts "\t#{product.description}"
33
+ # puts "---\n"
34
+ # end
35
+ def products_for_location(key)
36
+ Array.wrap(location(key)[:inventory_products]).map do |product|
37
+ InventoryProduct.new(product[:inventory_product])
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::GetSalesOrderExtended
3
+ #
4
+ # This method takes in a Sales Order Key and returns detailed data about the
5
+ # sales order, including compliance status.
6
+ #
7
+ # Use this method to query the full details of a sales order and the
8
+ # shipments in the sales order.
9
+ class GetSalesOrderExtended
10
+
11
+ # Finds a +SalesOrder+ by the +SalesOrderKey+.
12
+ #
13
+ # Returns an instance of ShipCompliant::GetSalesOrderExtendedResult.
14
+ def self.by_order_key(order_key)
15
+ result = ShipCompliant.client.call(:get_sales_order_extended, {
16
+ 'SalesOrderKey' => order_key
17
+ })
18
+
19
+ GetSalesOrderExtendedResult.new(result)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,65 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::GetSalesOrderExtendedResult
3
+ #
4
+ # Wraps the response of +GetSalesOrderExtended+ and provides methods to
5
+ # quickly access compliace rules of a shipment, the billing address,
6
+ # shipments, and channel details.
7
+ class GetSalesOrderExtendedResult < Struct.new(:response)
8
+ include BaseResult
9
+
10
+ # Returns an array of the +ShipmentComplianceResponse+ node as a Hash.
11
+ #
12
+ # sales_order.shipment_compliance_rules.each do |shipment|
13
+ # puts "SHIPMENT '#{shipment[:key]}' IS NOT COMPLIANT" unless shipment[:is_compliant]
14
+ # end
15
+ def shipment_compliance_rules
16
+ Array.wrap(response[:compliance_results][:sales_order][:shipments][:shipment_compliance_response]).map do |shipment|
17
+ shipment
18
+ end
19
+ end
20
+
21
+ # Finds all the compliance rules for a shipment.
22
+ # Returns an instance of ShipmentCompliance.
23
+ #
24
+ # shipment_compliance = sales_order.compliance_rules_for_shipment('SHIPMENT-KEY')
25
+ # puts shipment_compliance.compliant? #=> false
26
+ def compliance_rules_for_shipment(shipment_key)
27
+ shipment = shipment_compliance_rules.select { |s| s[:key] == shipment_key }.first
28
+ ShipmentCompliance.new(shipment)
29
+ end
30
+
31
+ # Returns a ShipCompliant::Address with the customer's billing information.
32
+ #
33
+ # puts sales_order.bill_to.state #=> 'AK'
34
+ def bill_to
35
+ Address.new(response[:sales_order][:bill_to])
36
+ end
37
+
38
+ # Returns an array of +Shipment+ nodes as a Hash.
39
+ #
40
+ # sales_order.shipments.each do |shipment|
41
+ # puts shipment[:ship_date] #=> DateTime
42
+ # end
43
+ def shipments
44
+ Array.wrap(response[:sales_order][:shipments]).map do |shipment|
45
+ shipment[:shipment]
46
+ end
47
+ end
48
+
49
+ # Finds a shipment by +ShipmentKey+. Returns an instance of ShipCompliant::Shipment.
50
+ #
51
+ # shipment = sales_order.find_shipment('1')
52
+ # puts shipment.ship_date #=> DateTime
53
+ def find_shipment(shipment_key)
54
+ shipment = shipments.select { |s| s[:shipment_key] == shipment_key }.first
55
+ Shipment.new(shipment)
56
+ end
57
+
58
+ # Returns the +OrderChannelDetails+ node as an instance of ShipCompliant::ChannelDetails.
59
+ #
60
+ # puts sales_order.channel_details.order_channel #=> 'MyOrders'
61
+ def channel_details
62
+ ChannelDetails.new(response[:order_channel_details])
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,95 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::InventoryProduct
3
+ #
4
+ # InventoryProduct is a value object that has methods to access product data from GetInventoryDetails.
5
+ #
6
+ # products = @inventory_results.products_for_location('WineShipping')
7
+ #
8
+ # products.each do |product|
9
+ # puts "[%s] %s" % [product.product_key, product.description]
10
+ #
11
+ # product.inventory_levels.each do |type, quantity|
12
+ # puts "%s are %s" % [quantity, type]
13
+ # end
14
+ # end
15
+ class InventoryProduct < Struct.new(:product)
16
+
17
+ # Returns the +DefaultCase+ of a product.
18
+ def default_case
19
+ product[:default_case]
20
+ end
21
+
22
+ # Returns the +ProductKey+ of a product.
23
+ def product_key
24
+ product[:product_key]
25
+ end
26
+
27
+ # Returns the +ProductType+ of a product.
28
+ def product_type
29
+ product[:product_type]
30
+ end
31
+
32
+ # Returns the +Description+ of a product.
33
+ def description
34
+ product[:description]
35
+ end
36
+
37
+ # Returns the +UnitPrice+ of a product as a float.
38
+ def unit_price
39
+ product[:unit_price].to_f
40
+ end
41
+
42
+ # Returns the +FulfillmentSku+ of a product.
43
+ def fulfillment_sku
44
+ product[:fulfillment_sku]
45
+ end
46
+
47
+ # Returns the +PercentAlcohol+ of a product as a float.
48
+ def percent_alcohol
49
+ product[:percent_alcohol].to_f
50
+ end
51
+
52
+ # Returns the +Vintage+ of a product as an integer.
53
+ def vintage
54
+ product[:vintage].to_i
55
+ end
56
+
57
+ # Returns the +VolumeAmount+ of a product as a float.
58
+ def volume_amount
59
+ product[:volume_amount].to_f
60
+ end
61
+
62
+ # Returns the +VolumeML+ of a product as a float.
63
+ def volume_ml
64
+ product[:volume_ml].to_f
65
+ end
66
+
67
+ # Returns the +VolumeUnit+ of a product.
68
+ def volume_unit
69
+ product[:volume_unit]
70
+ end
71
+
72
+ # Returns a Hash of inventory levels.
73
+ #
74
+ # - The key is the +InventoryType+.
75
+ # - The value is +Quantity+ as a float.
76
+ #
77
+ # product.inventory_levels #=> {
78
+ # available: 2,
79
+ # on_hold: 2,
80
+ # back_order: 4
81
+ # }
82
+ def inventory_levels
83
+ levels = {}
84
+
85
+ product[:inventory_levels][:inventory_level].each do |level|
86
+ key = level[:inventory_type].underscore.to_sym
87
+ value = level[:quantity].to_f
88
+
89
+ levels[key] = value
90
+ end
91
+
92
+ levels
93
+ end
94
+ end
95
+ end