ship_compliant 0.1.0

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 (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