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