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,62 @@
1
+ require 'savon'
2
+ require 'active_support/all'
3
+
4
+ require "ship_compliant/version"
5
+ require "ship_compliant/configuration"
6
+ require "ship_compliant/client"
7
+ require "ship_compliant/base_result"
8
+ require "ship_compliant/error_result"
9
+
10
+ require "ship_compliant/address"
11
+ require "ship_compliant/address/suggested_address"
12
+
13
+ require "ship_compliant/shipment"
14
+ require "ship_compliant/package"
15
+ require "ship_compliant/channel_details"
16
+
17
+ # SEARCH SALES ORDERS
18
+ require "ship_compliant/order_search"
19
+ require "ship_compliant/search_sales_orders"
20
+ require "ship_compliant/search_sales_orders_result"
21
+ require "ship_compliant/search_sales_order_summary"
22
+
23
+ require "ship_compliant/get_sales_order_extended"
24
+ require "ship_compliant/get_sales_order_extended_result"
25
+
26
+ # VOID SALES ORDER
27
+ require "ship_compliant/void_sales_order"
28
+ require "ship_compliant/void_sales_order_result"
29
+
30
+ # ADD UPDATE PRODUCT
31
+ require "ship_compliant/product_attributes"
32
+ require "ship_compliant/add_update_product"
33
+ require "ship_compliant/add_update_product_result"
34
+
35
+ # ADD UPDATE BRAND
36
+ require "ship_compliant/add_update_brand"
37
+ require "ship_compliant/add_update_brand_result"
38
+
39
+ # SALES TAX
40
+ require "ship_compliant/shipment_sales_tax_rate"
41
+ require "ship_compliant/sales_tax_rate"
42
+ require "ship_compliant/product_sales_tax_rate"
43
+ require "ship_compliant/freight_sales_tax_rate"
44
+
45
+ # CHECK COMPLIANCE OF SALES ORDER WITH ADDRESS VALIDATION
46
+ require "ship_compliant/check_compliance"
47
+ require "ship_compliant/check_compliance_result"
48
+
49
+ require "ship_compliant/shipment_compliance"
50
+ require "ship_compliant/compliance_rule"
51
+
52
+ require "ship_compliant/commit_sales_order"
53
+ require "ship_compliant/commit_sales_order_result"
54
+
55
+ require "ship_compliant/inventory_product"
56
+
57
+ require "ship_compliant/get_inventory_details"
58
+ require "ship_compliant/get_inventory_details_result"
59
+
60
+ module ShipCompliant
61
+ # Your code goes here...
62
+ end
@@ -0,0 +1,50 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::AddUpdateBrand
3
+ #
4
+ # Checks to see if a brand with the same key already exists. If one does not
5
+ # exist, it will add the brand with the specified name. If a brand exists
6
+ # with the same brand key then the brand name will be updated.
7
+ #
8
+ # Use this method to add a new brand or update an existing brand in the
9
+ # system. A brand must be defined before adding products.
10
+ #
11
+ # ShipCompliant::AddUpdateBrand.brand({
12
+ # key: 'DENSNW',
13
+ # name: 'Denver Snow',
14
+ #
15
+ # this_brand_is_bottled_by_a_third_party: false,
16
+ # this_brand_is_produced_by_a_third_party: false,
17
+ # this_brand_operates_under_a_trade_name: false,
18
+ # this_brand_was_acquired_from_a_third_party: false
19
+ # })
20
+ class AddUpdateBrand
21
+
22
+ # Adds or updates a brand depending on +:update_mode+.
23
+ #
24
+ # +brand+ parameter must be a Hash.
25
+ #
26
+ # === Options
27
+ #
28
+ # You can specify the update mode by passing a hash as the second argument.
29
+ # The default is +ErrorOnExisting+.
30
+ #
31
+ # Available options are.
32
+ #
33
+ # * UpdateExisting - Existing brand information is updated.
34
+ # * IgnoreExisting - Pre-existing information is not updated.
35
+ # * ErrorOnExisting - An error message is returned if the brand already exists.
36
+ #
37
+ # ShipCompliant::AddUpdateBrand.product({
38
+ # # brand attributes
39
+ # }, update_mode: 'UpdateExisting')
40
+ def self.brand(brand, options = {})
41
+ result = ShipCompliant.client.call(:add_update_brand, {
42
+ 'Brand' => brand.deep_transform_keys { |key| key.to_s.camelize },
43
+ 'UpdateMode' => options.fetch(:update_mode, 'ErrorOnExisting')
44
+ })
45
+
46
+ AddUpdateBrandResult.new(result)
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,6 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::AddUpdateBrandResult
3
+ class AddUpdateBrandResult < Struct.new(:response)
4
+ include BaseResult
5
+ end
6
+ end
@@ -0,0 +1,66 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::AddUpdateProduct
3
+ #
4
+ # This method checks to see if a product with the same product key and brand
5
+ # key already exists.
6
+ #
7
+ # If one does not exist, it will add the product with the specified
8
+ # information.
9
+ #
10
+ # If a product exists with the same product key and brand key, but is not
11
+ # referenced by any shipments, the product will be updated with the specified
12
+ # information.
13
+ #
14
+ # If a product exists with the same product key and brand key, and is
15
+ # referenced by one or more shipments, the product will not be updated.
16
+ #
17
+ # result = ShipCompliant::AddUpdateProduct.product({
18
+ # # product attributes
19
+ # }, update_mode: 'IgnoreExisting')
20
+ #
21
+ # if result.success?
22
+ # puts "Product added successfully.
23
+ # else
24
+ # result.errors.each do |error|
25
+ # puts error.message
26
+ # end
27
+ # end
28
+ class AddUpdateProduct
29
+
30
+ # Adds or updates product depending on +:update_mode+.
31
+ #
32
+ # +product+ parameter must be a Hash. The keys must be related to the the
33
+ # keys located in ShipCompliant::ProductAttributes.
34
+ #
35
+ # === Options
36
+ #
37
+ # You can specify the update mode by passing a Hash as the second argument.
38
+ # The default is +ErrorOnExisting+.
39
+ #
40
+ # Available options are.
41
+ #
42
+ # * UpdateExisting - Existing product information is updated.
43
+ # * IgnoreExisting - Pre-existing information is not updated.
44
+ # * ErrorOnExisting - An error message is returned if the product already exists.
45
+ #
46
+ # ShipCompliant::AddUpdateProduct.product({
47
+ # # product attributes
48
+ # }, update_mode: 'UpdateExisting')
49
+ def self.product(product, options = {})
50
+ details = {
51
+ 'Product' => ProductAttributes.new(product).to_h,
52
+ 'UpdateMode' => options.fetch(:update_mode, 'ErrorOnExisting')
53
+ }
54
+
55
+ result = add_update_product(details)
56
+ AddUpdateProductResult.new(result)
57
+ end
58
+
59
+ private
60
+
61
+ def self.add_update_product(request)
62
+ ShipCompliant.client.call(:add_update_product, request)
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,6 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::AddUpdateProductResult
3
+ class AddUpdateProductResult < Struct.new(:response)
4
+ include BaseResult
5
+ end
6
+ end
@@ -0,0 +1,87 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::Address
3
+ #
4
+ # This is a value object that wraps an address node. +BillTo+ and +ShipTo+.
5
+ #
6
+ # address = ShipCompliant::Address.new(city: 'Boston', state: 'MA')
7
+ # address.city #=> 'Boston'
8
+ # address.state #=> 'MA'
9
+ class Address < Struct.new(:address)
10
+
11
+ # Returns the suggested city.
12
+ def city
13
+ address[:city]
14
+ end
15
+
16
+ # Returns the suggested county.
17
+ def county
18
+ address[:county]
19
+ end
20
+
21
+ # Returns the suggested state.
22
+ def state
23
+ address[:state]
24
+ end
25
+
26
+ # Returns the country.
27
+ def country
28
+ address[:country]
29
+ end
30
+
31
+ # Returns the suggested street.
32
+ def street1
33
+ address[:street1]
34
+ end
35
+
36
+ # Returns the suggested street line 2.
37
+ def street2
38
+ address[:street2]
39
+ end
40
+
41
+ # Returns the suggested zip code.
42
+ def zip1
43
+ address[:zip1].to_i
44
+ end
45
+
46
+ # Returns the suggested zip code extension.
47
+ def zip2
48
+ return nil if address[:zip2].blank?
49
+ address[:zip2].to_i
50
+ end
51
+
52
+ # Returns the company.
53
+ def company
54
+ address[:company]
55
+ end
56
+
57
+ # Returns the first name.
58
+ def first_name
59
+ address[:first_name]
60
+ end
61
+
62
+ # Returns the last name.
63
+ def last_name
64
+ address[:last_name]
65
+ end
66
+
67
+ # Returns the phone.
68
+ def phone
69
+ address[:phone]
70
+ end
71
+
72
+ # Returns the fax.
73
+ def fax
74
+ address[:fax]
75
+ end
76
+
77
+ # Returns the email address.
78
+ def email
79
+ address[:email]
80
+ end
81
+
82
+ # Returns the date of birth.
83
+ def date_of_birth
84
+ address[:date_of_birth]
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,37 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::SuggestedAddress
3
+ #
4
+ # This is a value object that wraps the +SuggestedAddress+ node. It inherits
5
+ # methods from Address, but it also as methods to access suggested address
6
+ # details and parts.
7
+ class SuggestedAddress < Address
8
+
9
+ # Returns a Hash of the address location details.
10
+ #
11
+ # - +city_abbreviation+
12
+ # - +congressional_district+
13
+ # - +county_fips+
14
+ # - +time_zone+
15
+ # - +time_zone_code+
16
+ def details
17
+ address[:details]
18
+ end
19
+
20
+ # Returns a Hash of the address parts.
21
+ #
22
+ # - +company+
23
+ # - +mail_box_name+
24
+ # - +mail_box_number+
25
+ # - +post_direction+
26
+ # - +pre_direction+
27
+ # - +street_name+
28
+ # - +street_number+
29
+ # - +street_suffix+
30
+ # - +suite_name+
31
+ # - +suite_number+
32
+ def parts
33
+ address[:parts]
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::BaseResult
3
+ #
4
+ # This class provides methods to are relevant to every
5
+ # API request. All requests made through the
6
+ # ShipCompliant API client include these methods.
7
+ module BaseResult
8
+
9
+ # Returns boolean whether order was successfully voided.
10
+ #
11
+ # puts "SUCCESS" if result.success?
12
+ def success?
13
+ response[:response_status] == "Success"
14
+ end
15
+
16
+ # Returns true if order failed to be voided.
17
+ #
18
+ # puts "FAILED" if result.failure?
19
+ def failure?
20
+ !success?
21
+ end
22
+
23
+ # An array of +ErrorResult+ items or an empty array if the response was
24
+ # successful.
25
+ #
26
+ # result.errors.each do |error|
27
+ # puts "#{error.message} [#error.key]"
28
+ # end
29
+ def errors
30
+ return [] if success?
31
+ @errors ||= Array.wrap(response[:errors]).map do |error|
32
+ ErrorResult.new(error[:error])
33
+ end
34
+ end
35
+
36
+ # The number of errors in the response.
37
+ def errors_count
38
+ errors.length
39
+ end
40
+ alias_method :error_count, :errors_count
41
+
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::ChannelDetails
3
+ #
4
+ # A value object that wraps the +OrderChanelDetails+ node.
5
+ #
6
+ # channel_details = sales_order.channel_details
7
+ # puts channel_details.order_channel #=> 'MyOrders'
8
+ class ChannelDetails < Struct.new(:channel)
9
+
10
+ # Returns +OrderChannel+.
11
+ def order_channel
12
+ channel[:order_channel]
13
+ end
14
+
15
+ # Returns +AdvertiserKey+.
16
+ def advertiser_key
17
+ channel[:advertiser_key]
18
+ end
19
+
20
+ # Returns +AdvertiserName+.
21
+ def advertiser_name
22
+ channel[:advertiser_name]
23
+ end
24
+
25
+ # Returns +Meta+.
26
+ def meta
27
+ channel[:meta]
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::CheckCompliance
3
+ #
4
+ # The CheckComplianceOfSalesOrderWithAddressValidation API method is used for
5
+ # real time compliance checks from the order point of entry (eCommerce, POS).
6
+ #
7
+ # The method can do three things in one call:
8
+ #
9
+ # - Calculate Sales Tax Due for order and return product level tax rates
10
+ # (IncludeSalesTaxRates = true)
11
+ # - Validate the ShipTo address and get normalized address result
12
+ # - Check compliance of the order
13
+ #
14
+ # This method takes requires specific order information from the order point
15
+ # of entry for ShipCompliant to check the order for compliance. The sales
16
+ # order will not be committed via this method. This API call is usually
17
+ # paired with CommitSalesOrder() to save the sales order to ShipCompliant.
18
+ #
19
+ # Look at the following example request: http://git.io/xMNOvw
20
+ class CheckCompliance
21
+
22
+ # Checks the compliance of a sales order from the data specified.
23
+ #
24
+ # An example of all the required fields can be seen here: http://git.io/xMNOvw
25
+ #
26
+ # ShipCompliant::CheckCompliance.of_sales_order({
27
+ # address_option: {
28
+ # ignore_street_level_errors: true,
29
+ # reject_if_address_suggested: 'false' # Savon bug? Passes the value as an attribute when a boolean
30
+ # },
31
+ # include_sales_tax_rates: true,
32
+ # # ...
33
+ # })
34
+ def self.of_sales_order(data)
35
+ camel_cased_keys = data.deep_transform_keys { |key| key.to_s.camelize }
36
+ result = ShipCompliant.client.call(:check_compliance_of_sales_order_with_address_validation, camel_cased_keys)
37
+ CheckComplianceResult.new(result)
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,95 @@
1
+ module ShipCompliant
2
+ # == ShipCompliant::CheckComplianceResult
3
+ #
4
+ # CheckComplianceResult wraps the response from the
5
+ # +CheckComplianceOfSalesOrderWithAddressValidation+ API method.
6
+ #
7
+ # It provides methods to access nested objects and easily iterate over taxes
8
+ # rates for shipments.
9
+ #
10
+ # compliant_status = ShipCompliant::CheckCompliance.of_sales_order({
11
+ # # attributes here
12
+ # })
13
+ #
14
+ # compliant_status.compliant? #=> true
15
+ class CheckComplianceResult < Struct.new(:response)
16
+ include BaseResult
17
+
18
+ # Returns true if all shipments of a SalesOrder are compliant.
19
+ def compliant?
20
+ response[:sales_order][:is_compliant] == true
21
+ end
22
+
23
+ # Returns a float of the recommended sales tax due for a SalesOrder.
24
+ def recommended_tax_due
25
+ response[:sales_order][:sales_tax_rates][:recommended_sales_tax_due].to_f
26
+ end
27
+
28
+ # Access the tax information for a shipment. Returns an instance of
29
+ # ShipmentSalesTaxRate.
30
+ def taxes_for_shipment(shipment_key)
31
+ shipment = shipment_sales_tax_rates.select { |s| s[:@shipment_key] == shipment_key }.first
32
+
33
+ # convert attribute keys to symbols
34
+ freight = attributes_to_symbols(shipment[:freight_sales_tax_rate])
35
+
36
+ # wrap products in ProductSalesTaxRate
37
+ products = wrap_products(shipment[:product_sales_tax_rates])
38
+
39
+ ShipmentSalesTaxRate.new(shipment_key, FreightSalesTaxRate.new(freight), products)
40
+ end
41
+
42
+ # This method returns an Array containing information about each shipment of a
43
+ # SalesOrder.
44
+ def shipment_sales_tax_rates
45
+ Array.wrap(response[:sales_order][:sales_tax_rates][:shipment_sales_tax_rates][:shipment_sales_tax_rate])
46
+ end
47
+
48
+ # Returns an array of the +ShipmentComplianceResponse+ node as a Hash.
49
+ #
50
+ # compliance_result.shipment_compliance_rules.each do |shipment|
51
+ # puts "SHIPMENT '#{shipment[:key]}' IS NOT COMPLIANT" unless shipment[:is_compliant]
52
+ # end
53
+ def shipment_compliance_rules
54
+ Array.wrap(response[:sales_order][:shipments][:shipment_compliance_response])
55
+ end
56
+
57
+ # Finds all the compliance rules for a shipment.
58
+ # Returns an instance of ShipmentCompliance.
59
+ #
60
+ # shipment_compliance = compliance_result.compliance_rules_for_shipment('SHIPMENT-KEY')
61
+ # puts shipment_compliance.compliant? #=> false
62
+ def compliance_rules_for_shipment(shipment_key)
63
+ shipment = shipment_compliance_rules.select { |s| s[:key] == shipment_key }.first
64
+ ShipmentCompliance.new(shipment)
65
+ end
66
+
67
+ # Returns the +AddressValidationResult+.
68
+ def address_validation_result
69
+ response[:address_validation_result]
70
+ end
71
+
72
+ # Returns an instance of ShipCompliant::SuggestedAddress.
73
+ def suggested_address
74
+ SuggestedAddress.new(response[:suggested_address])
75
+ end
76
+
77
+ private
78
+
79
+ # Nori returns XML attributes as a key beginning with a spiral (@).
80
+ # This removes the spiral and changes key to a symbol.
81
+ def attributes_to_symbols(object)
82
+ object.transform_keys do |key|
83
+ key.to_s.gsub('@', '').to_sym
84
+ end
85
+ end
86
+
87
+ # Wraps each +ProductSalesTaxRates+ node with
88
+ # ShipCompliant::ProductSalesTaxRate
89
+ def wrap_products(products)
90
+ Array.wrap(products[:product_sales_tax_rate]).map do |product|
91
+ ProductSalesTaxRate.new(attributes_to_symbols(product))
92
+ end
93
+ end
94
+ end
95
+ end