netsuite 0.8.10 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/dependabot.yml +14 -0
  4. data/.github/workflows/codeql-analysis.yml +70 -0
  5. data/.github/workflows/main.yml +7 -4
  6. data/Gemfile +5 -3
  7. data/HISTORY.md +56 -2
  8. data/README.md +94 -38
  9. data/lib/netsuite/actions/add.rb +7 -2
  10. data/lib/netsuite/actions/attach_file.rb +87 -0
  11. data/lib/netsuite/actions/delete.rb +18 -1
  12. data/lib/netsuite/actions/delete_list.rb +1 -1
  13. data/lib/netsuite/actions/get.rb +1 -1
  14. data/lib/netsuite/actions/get_deleted.rb +1 -1
  15. data/lib/netsuite/actions/get_list.rb +1 -1
  16. data/lib/netsuite/actions/initialize.rb +2 -2
  17. data/lib/netsuite/actions/search.rb +20 -7
  18. data/lib/netsuite/configuration.rb +16 -3
  19. data/lib/netsuite/records/account.rb +0 -1
  20. data/lib/netsuite/records/accounting_period.rb +1 -1
  21. data/lib/netsuite/records/assembly_component.rb +0 -2
  22. data/lib/netsuite/records/assembly_item.rb +2 -2
  23. data/lib/netsuite/records/assembly_unbuild.rb +0 -1
  24. data/lib/netsuite/records/cash_refund.rb +0 -1
  25. data/lib/netsuite/records/cash_refund_item.rb +1 -1
  26. data/lib/netsuite/records/cash_sale.rb +1 -2
  27. data/lib/netsuite/records/contact.rb +0 -1
  28. data/lib/netsuite/records/credit_memo.rb +1 -1
  29. data/lib/netsuite/records/currency_rate.rb +0 -1
  30. data/lib/netsuite/records/custom_record.rb +1 -1
  31. data/lib/netsuite/records/customer.rb +224 -23
  32. data/lib/netsuite/records/customer_deposit.rb +0 -1
  33. data/lib/netsuite/records/customer_payment.rb +0 -1
  34. data/lib/netsuite/records/customer_refund.rb +1 -2
  35. data/lib/netsuite/records/deposit.rb +0 -1
  36. data/lib/netsuite/records/deposit_application.rb +0 -1
  37. data/lib/netsuite/records/description_item.rb +3 -3
  38. data/lib/netsuite/records/discount_item.rb +1 -1
  39. data/lib/netsuite/records/employee.rb +1 -1
  40. data/lib/netsuite/records/estimate.rb +0 -1
  41. data/lib/netsuite/records/file.rb +1 -1
  42. data/lib/netsuite/records/gift_certificate_item.rb +1 -1
  43. data/lib/netsuite/records/inbound_shipment.rb +0 -1
  44. data/lib/netsuite/records/inventory_item.rb +237 -38
  45. data/lib/netsuite/records/inventory_number.rb +0 -1
  46. data/lib/netsuite/records/invoice.rb +2 -2
  47. data/lib/netsuite/records/item_availability.rb +46 -0
  48. data/lib/netsuite/records/item_fulfillment.rb +4 -2
  49. data/lib/netsuite/records/item_fulfillment_package_fed_ex.rb +28 -0
  50. data/lib/netsuite/records/item_fulfillment_package_fed_ex_list.rb +32 -0
  51. data/lib/netsuite/records/item_fulfillment_package_ups.rb +27 -0
  52. data/lib/netsuite/records/item_fulfillment_package_ups_list.rb +32 -0
  53. data/lib/netsuite/records/item_fulfillment_package_usps.rb +26 -0
  54. data/lib/netsuite/records/item_fulfillment_package_usps_list.rb +32 -0
  55. data/lib/netsuite/records/item_group.rb +3 -3
  56. data/lib/netsuite/records/item_option_custom_field.rb +52 -0
  57. data/lib/netsuite/records/item_receipt.rb +0 -1
  58. data/lib/netsuite/records/item_vendor.rb +10 -1
  59. data/lib/netsuite/records/job.rb +0 -1
  60. data/lib/netsuite/records/kit_item.rb +2 -2
  61. data/lib/netsuite/records/location.rb +0 -1
  62. data/lib/netsuite/records/lot_numbered_assembly_item.rb +1 -1
  63. data/lib/netsuite/records/lot_numbered_inventory_item.rb +226 -82
  64. data/lib/netsuite/records/matrix_option_list.rb +16 -0
  65. data/lib/netsuite/records/non_inventory_purchase_item.rb +1 -1
  66. data/lib/netsuite/records/non_inventory_resale_item.rb +157 -21
  67. data/lib/netsuite/records/non_inventory_sale_item.rb +134 -22
  68. data/lib/netsuite/records/null_field_list.rb +15 -0
  69. data/lib/netsuite/records/opportunity.rb +0 -1
  70. data/lib/netsuite/records/other_charge_sale_item.rb +2 -2
  71. data/lib/netsuite/records/payment_item.rb +3 -3
  72. data/lib/netsuite/records/payroll_item.rb +0 -1
  73. data/lib/netsuite/records/purchase_order.rb +0 -1
  74. data/lib/netsuite/records/record_ref.rb +1 -1
  75. data/lib/netsuite/records/return_authorization.rb +1 -0
  76. data/lib/netsuite/records/sales_order.rb +2 -2
  77. data/lib/netsuite/records/sales_order_item.rb +2 -1
  78. data/lib/netsuite/records/serialized_assembly_item.rb +1 -1
  79. data/lib/netsuite/records/serialized_inventory_item.rb +2 -2
  80. data/lib/netsuite/records/serialized_inventory_item_location.rb +0 -1
  81. data/lib/netsuite/records/service_resale_item.rb +125 -21
  82. data/lib/netsuite/records/service_sale_item.rb +1 -2
  83. data/lib/netsuite/records/{customer_subscription.rb → subscription.rb} +1 -1
  84. data/lib/netsuite/records/subscriptions_list.rb +10 -0
  85. data/lib/netsuite/records/subsidiary.rb +0 -1
  86. data/lib/netsuite/records/subtotal_item.rb +3 -4
  87. data/lib/netsuite/records/transfer_order.rb +0 -1
  88. data/lib/netsuite/records/translation.rb +17 -0
  89. data/lib/netsuite/records/translation_list.rb +11 -0
  90. data/lib/netsuite/records/vendor.rb +0 -1
  91. data/lib/netsuite/records/vendor_bill.rb +0 -1
  92. data/lib/netsuite/records/work_order.rb +0 -1
  93. data/lib/netsuite/records/work_order_item.rb +0 -1
  94. data/lib/netsuite/support/actions.rb +2 -0
  95. data/lib/netsuite/support/fields.rb +2 -0
  96. data/lib/netsuite/support/records.rb +22 -5
  97. data/lib/netsuite/support/sublist.rb +2 -2
  98. data/lib/netsuite/utilities/strings.rb +15 -0
  99. data/lib/netsuite/utilities.rb +25 -13
  100. data/lib/netsuite/version.rb +1 -1
  101. data/lib/netsuite.rb +15 -3
  102. data/netsuite.gemspec +5 -2
  103. data/spec/netsuite/actions/add_spec.rb +39 -1
  104. data/spec/netsuite/actions/attach_file_spec.rb +59 -0
  105. data/spec/netsuite/actions/delete_spec.rb +74 -14
  106. data/spec/netsuite/actions/get_select_value_spec.rb +43 -0
  107. data/spec/netsuite/actions/search_spec.rb +205 -0
  108. data/spec/netsuite/configuration_spec.rb +35 -0
  109. data/spec/netsuite/records/cash_refund_item_spec.rb +1 -1
  110. data/spec/netsuite/records/credit_memo_spec.rb +14 -0
  111. data/spec/netsuite/records/customer_spec.rb +287 -20
  112. data/spec/netsuite/records/inventory_item_spec.rb +239 -22
  113. data/spec/netsuite/records/invoice_spec.rb +43 -0
  114. data/spec/netsuite/records/item_availability_spec.rb +59 -0
  115. data/spec/netsuite/records/item_fulfillment_package_fed_ex_list_spec.rb +27 -0
  116. data/spec/netsuite/records/item_fulfillment_package_ups_list_spec.rb +27 -0
  117. data/spec/netsuite/records/item_fulfillment_package_usps_list_spec.rb +27 -0
  118. data/spec/netsuite/records/item_option_custom_field_spec.rb +27 -0
  119. data/spec/netsuite/records/item_vendor_list_spec.rb +2 -5
  120. data/spec/netsuite/records/item_vendor_spec.rb +14 -2
  121. data/spec/netsuite/records/lot_numbered_inventory_item_spec.rb +247 -0
  122. data/spec/netsuite/records/matrix_option_list_spec.rb +26 -0
  123. data/spec/netsuite/records/non_inventory_resale_item_spec.rb +159 -24
  124. data/spec/netsuite/records/non_inventory_sale_item_spec.rb +135 -22
  125. data/spec/netsuite/records/null_field_list_spec.rb +30 -0
  126. data/spec/netsuite/records/return_authorization_spec.rb +62 -0
  127. data/spec/netsuite/records/sales_order_item_spec.rb +4 -3
  128. data/spec/netsuite/records/sales_order_spec.rb +46 -0
  129. data/spec/netsuite/records/service_resale_item_spec.rb +122 -17
  130. data/spec/netsuite/records/{customer_subscription_spec.rb → subscription_spec.rb} +2 -2
  131. data/spec/netsuite/records/{customer_subscriptions_list_spec.rb → subscriptions_list_spec.rb} +2 -2
  132. data/spec/netsuite/records/translation_list_spec.rb +34 -0
  133. data/spec/netsuite/records/translation_spec.rb +28 -0
  134. data/spec/netsuite/support/fields_spec.rb +16 -4
  135. data/spec/netsuite/support/records_spec.rb +33 -7
  136. data/spec/netsuite/support/search_result_spec.rb +12 -0
  137. data/spec/netsuite/utilities_spec.rb +10 -2
  138. data/spec/support/fixtures/add/add_file.xml +20 -0
  139. data/spec/support/fixtures/add/add_invoice.xml +9 -5
  140. data/spec/support/fixtures/attach/attach_file_to_sales_order.xml +16 -0
  141. data/spec/support/fixtures/attach/attach_file_to_sales_order_error.xml +20 -0
  142. data/spec/support/fixtures/delete/delete_customer_error.xml +21 -0
  143. data/spec/support/fixtures/delete/delete_customer_multiple_errors.xml +25 -0
  144. data/spec/support/fixtures/get_item_availability/get_item_availability.xml +46 -0
  145. data/spec/support/fixtures/get_select_value/empty_result.xml +22 -0
  146. data/spec/support/fixtures/get_select_value/item_fulfillment_ship_method.xml +43 -0
  147. data/spec/support/fixtures/search/basic_search_contact.xml +39 -0
  148. data/spec/support/fixtures/search/single_search_result.xml +46 -0
  149. metadata +78 -17
  150. data/lib/netsuite/core_ext/string/lower_camelcase.rb +0 -9
  151. data/lib/netsuite/records/customer_subscriptions_list.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b4128e448acd323b0ed8828e4974270806ea84372925ba266b6dd71014e3105
4
- data.tar.gz: 676b94d3445e96b7064826f70e393a92f44814e3c2e0ffe5cf5ae4bdd87a4f74
3
+ metadata.gz: 53e37d7468526bab26109064a50e3d393735e7fbb0758b56867de985e3230170
4
+ data.tar.gz: 318ed153297d54f2d19c821677e5d8bfffe89d7f688ea31aa324f93e1293f411
5
5
  SHA512:
6
- metadata.gz: 4ee18c44870a2ee0be4bb9ab21aeb912c682cf861830909780f2601620c57a0bda210a91855490d882a637a561e0c4ee363fae694de633de7fe0d3d0f07c3cd9
7
- data.tar.gz: fbbd65d4baeafb3b94605ee875189264f1811bdb532c93cb9ac7d80c192784c57c3770da0a1e113ac38941680bd12db5f9eefa3afecc982bd4b3dec462107fa6
6
+ metadata.gz: 0df38ba22afe3361daace766b033e947f20962ab0ac6d0f336e706b2bbd06bfae091478c856ce9b33ff18f28f07a3e796a774d1a78a1fc6766348538b89afb0a
7
+ data.tar.gz: c3021d78e3718dd30332accc69de70e3f88df394e33648a49eb995d4d9a5177ad669c5fdb269485a75cd29c13a186c5b863e84a83dfee8eb88ea7c7ba4c9765e
@@ -0,0 +1 @@
1
+ github: [NetSweet]
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+
4
+ # Maintain dependencies for GitHub Actions
5
+ - package-ecosystem: "github-actions"
6
+ directory: "/"
7
+ schedule:
8
+ interval: "daily"
9
+
10
+ # Maintain dependencies for rubygems
11
+ - package-ecosystem: "bundler"
12
+ directory: "/"
13
+ schedule:
14
+ interval: "daily"
@@ -0,0 +1,70 @@
1
+ # For most projects, this workflow file will not need changing; you simply need
2
+ # to commit it to your repository.
3
+ #
4
+ # You may wish to alter this file to override the set of languages analyzed,
5
+ # or to provide custom queries or build logic.
6
+ #
7
+ # ******** NOTE ********
8
+ # We have attempted to detect the languages in your repository. Please check
9
+ # the `language` matrix defined below to confirm you have the correct set of
10
+ # supported CodeQL languages.
11
+ #
12
+ name: "CodeQL"
13
+
14
+ on:
15
+ push:
16
+ branches: [ master ]
17
+ pull_request:
18
+ # The branches below must be a subset of the branches above
19
+ branches: [ master ]
20
+ schedule:
21
+ - cron: '32 15 * * 6'
22
+
23
+ jobs:
24
+ analyze:
25
+ name: Analyze
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ actions: read
29
+ contents: read
30
+ security-events: write
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ language: [ 'ruby' ]
36
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
38
+
39
+ steps:
40
+ - name: Checkout repository
41
+ uses: actions/checkout@v3
42
+
43
+ # Initializes the CodeQL tools for scanning.
44
+ - name: Initialize CodeQL
45
+ uses: github/codeql-action/init@v2
46
+ with:
47
+ languages: ${{ matrix.language }}
48
+ # If you wish to specify custom queries, you can do so here or in a config file.
49
+ # By default, queries listed here will override any specified in a config file.
50
+ # Prefix the list here with "+" to use these queries and those in the config file.
51
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
52
+
53
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
+ # If this step fails, then you should remove it and run the build manually (see below)
55
+ - name: Autobuild
56
+ uses: github/codeql-action/autobuild@v2
57
+
58
+ # ℹ️ Command-line programs to run using the OS shell.
59
+ # 📚 https://git.io/JvXDl
60
+
61
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62
+ # and modify them (or add more) to build your code if your project
63
+ # uses a compiled language
64
+
65
+ #- run: |
66
+ # make bootstrap
67
+ # make release
68
+
69
+ - name: Perform CodeQL Analysis
70
+ uses: github/codeql-action/analyze@v2
@@ -1,15 +1,18 @@
1
1
  name: Ruby
2
2
 
3
- on: [push,pull_request]
3
+ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  build:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby-version: [2.7, 2.6, 2.5, 2.4, 2.3, 2.2, 2.1]
10
+ ruby-version: [3.1, 3.0, 2.7, 2.6, 2.5, 2.4, 2.3, 2.2, 2.1]
11
+ bundle-tzinfo: [true, false]
12
+ env:
13
+ BUNDLE_TZINFO: "${{ matrix.bundle-tzinfo }}"
11
14
  steps:
12
- - uses: actions/checkout@v2
15
+ - uses: actions/checkout@v3
13
16
  - name: Set up Ruby ${{ matrix.ruby-version }}
14
17
  uses: ruby/setup-ruby@v1
15
18
  with:
@@ -17,4 +20,4 @@ jobs:
17
20
  - name: Install dependencies
18
21
  run: bundle install
19
22
  - name: Run tests
20
- run: bundle exec rake
23
+ run: bundle exec rake
data/Gemfile CHANGED
@@ -6,6 +6,8 @@ gem 'simplecov', :require => false
6
6
  gem 'pry-nav'
7
7
  gem 'pry-rescue'
8
8
 
9
- # optional dependency for more accurate timezone conversion
10
- gem 'tzinfo', '1.2.5'
11
- # gem 'tzinfo', '2.0.0'
9
+
10
+ if ENV.fetch('BUNDLE_TZINFO', 'false') == 'true'
11
+ # optional dependency for more accurate timezone conversion
12
+ gem 'tzinfo', '>= 1.2.5'
13
+ end
data/HISTORY.md CHANGED
@@ -1,10 +1,64 @@
1
1
  ## Unreleased
2
2
 
3
+
4
+ ### Added
5
+ ### Fixed
6
+ ### Breaking Changes
7
+
8
+ ## 0.9.0
9
+
10
+ ### Added
11
+
12
+ * Update `Customer` record fields/record refs for 2021.2. (#535)
13
+ The following were moved from `fields` to `record_refs`: `buying_reason`, `buying_time_frame`, `campaign_category`, `image`, `opening_balance_account`, `pref_cc_processor`, `representing_subsidiary`, `sales_group`, `sales_readiness`
14
+ The following were removed as `fields` since their sublist class is not yet implemented: `download_list`, `group_pricing_list`, `item_pricing_list`
15
+ * Add search-only fields to `Customer` (#535)
16
+ * Add `attach_file` action to `Customer` records (#544)
17
+ * Add `update` action to `File` records (#544)
18
+ * Expose `errors` after calls to `delete` action (#545)
19
+ * Add `update_list` action where missing on supported item records (#546)
20
+ * Ignore `after_submit_failed` status details (>= 2018.2) when collating errors in add action (#550)
21
+ * Add `NullFieldList` to `SalesOrder` (#552)
22
+ * Add thread safety to NetSuite configuration and utilities (#549)
23
+
24
+ ### Breaking Changes
25
+ * Rename `CustomerSubscriptionsList` to `SubscriptionsList` and `CustomerSubscription` to `Subscription` to match NetSuite naming (#535)
26
+
27
+ ## 0.8.12
28
+
29
+ ### Added
30
+
31
+ * Add NullFieldList record (to credit memos and invoices) (#529)
32
+ * Add `get_deleted` action to item records (#530)
33
+ * Add `get_deleted` action to Employee records (#531)
34
+ * Remove monkey patched `lower_camelcase` method on String (#533)
35
+
36
+ ## 0.8.11
37
+
3
38
  ### Added
4
- *
39
+
40
+ * Update ServiceResaleItem record fields/record refs for 2021.2. `item_options_list`, `presentation_item_list`, `site_category_list`, `translations_list` were all removed as fields as the are not simple fields, they require special classes. (#500)
41
+ * Dependabot to CI
42
+ * CI run for Ruby 3.0 & 3.1
43
+ * Add CI run for an environment with and without `tzinfo` installed
44
+ * Update NonInventorySaleItem record fields/record refs for 2021.2. `item_options_list`, `presentation_item_list`, `product_feed_list`, `site_category_list`, `translations_list` were all removed as fields as the are not simple fields, they require special classes. (#503)
45
+ * Implement MatrixOptionList#to_record (#504)
46
+ * Update ItemVendor record fields/record refs for 2021.1. `vendor` is now a record_ref instead of a field. (#505)
47
+ * Update InventoryItem record fields/record refs for 2021.2. `member_list` was removed as a field as it doesn't belong to InventoryItem. (#506)
48
+ * Update LotNumberedInventoryItem record fields/record refs for 2021.2. (#507)
49
+ * Update NonInventoryResaleItem record fields/record refs for 2021.2. `item_options_list`, `presentation_item_list`, `product_feed_list`, `site_category_list`, `translations_list` were all removed as fields as the are not simple fields, they require special classes. (#508)
50
+ * Add `attach_file` action for Invoice and SalesOrder. (#509)
51
+ * Add ItemOptionCustomField recrd (#512)
52
+ * Add Ship Address to Return Authorization (#525)
53
+ * Support translations records (#516)
5
54
 
6
55
  ### Fixed
7
- *
56
+
57
+ * Fix "undefined method `[]` for #<Nori::StringIOFile>" when adding File (#495)
58
+ * Moved definition of `search_joins` attribute from records to search action. The attribute was removed for AssemblyComponent, SerializedInventoryItemLocation, and WorkOrderItem as they don't offer the search action. (#511)
59
+ * Consider externalId in search criteria when using RecordRef as value (#517)
60
+ * Retry http client error subclasses
61
+ * Add upsert list action for cash sales (#523)
8
62
 
9
63
  ## 0.8.10
10
64
 
data/README.md CHANGED
@@ -1,34 +1,16 @@
1
- <!-- START doctoc generated TOC please keep comment here to allow auto update -->
2
- <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
3
- **Table of Contents**
4
-
5
- - [NetSuite SuiteTalk API Ruby Gem](#netsuite-suitetalk-api-ruby-gem)
6
- - [Help & Support](#help--support)
7
- - [Testing](#testing)
8
- - [Installation](#installation)
9
- - [Configuration](#configuration)
10
- - [Token based Authentication](#token-based-authentication)
11
- - [Usage](#usage)
12
- - [CRUD Operations](#crud-operations)
13
- - [Custom Records & Fields](#custom-records--fields)
14
- - [Searching](#searching)
15
- - [Non-standard Operations](#non-standard-operations)
16
-
17
- <!-- END doctoc generated TOC please keep comment here to allow auto update -->
18
-
19
1
  [![Ruby](https://github.com/NetSweet/netsuite/actions/workflows/main.yml/badge.svg)](https://github.com/NetSweet/netsuite/actions/workflows/main.yml)
20
2
  [![Slack Status](https://opensuite-slackin.herokuapp.com/badge.svg)](http://opensuite-slackin.herokuapp.com)
21
3
  [![Gem Version](https://badge.fury.io/rb/netsuite.svg)](http://badge.fury.io/rb/netsuite)
22
4
 
23
5
  # NetSuite SuiteTalk API Ruby Gem
24
6
 
25
- * This gem will act as a wrapper around the NetSuite SuiteTalk WebServices API.
7
+ * This gem will act as a wrapper around the NetSuite SuiteTalk Web Services API.
26
8
  * The gem does not cover the entire API, only the subset contributors have used so far. Please submit a PR for any functionality that's missing!
27
9
  * NetSuite is a complex system. There's a lot to learn and sparse resources available to learn from. Here's a list of [NetSuite Development Resources](https://github.com/NetSweet/netsuite/wiki/NetSuite-Development-Resources).
28
10
 
29
11
  # Help & Support
30
12
 
31
- Join the [slack channel](http://opensuite-slackin.herokuapp.com) for help with any NetSuite issues. Please do not post usage questions as issues in GitHub.
13
+ Join the [Slack channel](http://opensuite-slackin.herokuapp.com) for help with any NetSuite issues. Please do not post usage questions as issues in GitHub.
32
14
 
33
15
  There is some additional helpful resources for NetSuite development [listed here](https://dashboard.suitesync.io/docs/resources#netsuite).
34
16
 
@@ -54,20 +36,20 @@ gem 'netsuite'
54
36
 
55
37
  If you'd like more accurate time conversion support, include the `tzinfo` gem.
56
38
 
57
- This gem is built for ruby 2.6.x+, but should work on older versions down to 1.9. There's a [1-8-stable](https://github.com/NetSweet/netsuite/tree/1-8-stable) branch for ruby 1.8.x support.
39
+ This gem is built for Ruby 2.6.x+, but should work on older versions down to 1.9. There's a [1-8-stable](https://github.com/NetSweet/netsuite/tree/1-8-stable) branch for Ruby 1.8.x support.
58
40
 
59
41
  ## Configuration
60
42
 
61
- The most important thing you'll need is your NetSuite account ID. Not sure how to find your account id? [Here's a guide.](http://mikebian.co/find-netsuite-web-services-account-number/)
43
+ The most important thing you'll need is your NetSuite account ID. Not sure how to find your account ID? [Here's a guide.](http://mikebian.co/find-netsuite-web-services-account-number/)
62
44
 
63
45
  How you connect to NetSuite has changed a lot over the years and differs between API versions. For instance:
64
46
 
65
47
  * Older API versions (~2015) allowed authentication via username and password
66
- * Newever API versions (> 2016) still allowed for username and password authentication, but required an application ID
48
+ * Newer API versions (> 2016) still allowed for username and password authentication, but required an application ID
67
49
  * "OAuth", which requires four separate keys to be manually generated, was supported sometime after 2015
68
50
  * API versions greater than 2018_2 require `endpoint` to be set directly ([more info](https://github.com/NetSweet/netsuite/pull/473))
69
51
 
70
- Here's an example connection configuration. You don't want to actually use username + password config; token based authentication is detailed below in a separate section:
52
+ Here's an example connection configuration. You don't want to actually use username + password config; Token Based Authentication is detailed [in a separate section](#token-based-authentication):
71
53
 
72
54
  ```ruby
73
55
  NetSuite.configure do
@@ -78,13 +60,13 @@ NetSuite.configure do
78
60
  api_version '2018_2'
79
61
 
80
62
  # password-based login information
81
- # in most cases you should use token based authentication instead
63
+ # in most cases you should use Token Based Authentication instead
82
64
  email 'email@example.com'
83
65
  password 'password'
84
66
  role 10
85
67
 
86
- # recent API versions require a account-specific endpoint o be set
87
- # use `NetSuite::Utilities.data_center_url('TSTDRV1576318')` to retrieve wsdl URL
68
+ # recent API versions require an account-specific endpoint to be set
69
+ # use `NetSuite::Utilities.data_center_url('TSTDRV1576318')` to retrieve WSDL URL
88
70
  # you'll want to do this in a background process and strip the protocol out of the return string
89
71
  wsdl_domain 'tstdrv1576318.suitetalk.api.netsuite.com'
90
72
 
@@ -105,17 +87,17 @@ NetSuite.configure do
105
87
 
106
88
  api_version '2018_2'
107
89
 
108
- # optionally specify full wsdl URL (to switch to sandbox, for example)
90
+ # optionally specify full WSDL URL (to switch to sandbox, for example)
109
91
  wsdl "https://webservices.sandbox.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl"
110
92
 
111
- # if your datacenter is being switched, you'll have to manually set your wsdl location
93
+ # if your datacenter is being switched, you'll have to manually set your WSDL location
112
94
  wsdl "https://webservices.na2.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl"
113
95
 
114
96
  # or specify the wsdl_domain if you want to specify the datacenter and let the configuration
115
97
  # construct the full wsdl location - e.g. "https://#{wsdl_domain}/wsdl/v#{api_version}_0/netsuite.wsdl"
116
98
  wsdl_domain "webservices.na2.netsuite.com"
117
99
 
118
- # often the netsuite servers will hang which would cause a timeout exception to be raised
100
+ # often the NetSuite servers will hang which would cause a timeout exception to be raised
119
101
  # if you don't mind waiting (e.g. processing NS via a background worker), increasing the timeout should fix the issue
120
102
  read_timeout 100_000
121
103
 
@@ -129,7 +111,7 @@ NetSuite.configure do
129
111
  # log_level :debug
130
112
 
131
113
  # password-based login information
132
- # in most cases you should use token based authentication instead
114
+ # in most cases you should use Token Based Authentication instead
133
115
  email 'email@domain.com'
134
116
  password 'password'
135
117
  account '12345'
@@ -142,7 +124,7 @@ NetSuite.configure do
142
124
  end
143
125
  ```
144
126
 
145
- If are using username + password authentication (which you shouldn't be!) *and* you'd like to use a API endpoints greater than 2015_1 you'll need to specify an application ID:
127
+ If you are using username + password authentication (which you shouldn't be!) *and* you'd like to use an API endpoint greater than 2015_1, you'll need to specify an application ID:
146
128
 
147
129
  ```ruby
148
130
  NetSuite::Configuration.soap_header = {
@@ -152,9 +134,9 @@ NetSuite::Configuration.soap_header = {
152
134
  }
153
135
  ```
154
136
 
155
- ### Token based Authentication
137
+ ### Token Based Authentication
156
138
 
157
- OAuth credentials are supported and the recommended authentication approach. [Learn more about how to set up token based authentication here](http://mikebian.co/using-netsuites-token-based-authentication-with-suitetalk/).
139
+ OAuth credentials are supported and the recommended authentication approach. [Learn more about how to set up Token Based Authentication here](http://mikebian.co/using-netsuites-token-based-authentication-with-suitetalk/).
158
140
 
159
141
  ```ruby
160
142
  NetSuite.configure do
@@ -170,7 +152,7 @@ NetSuite.configure do
170
152
  # oauth does not work with API versions less than 2015_2
171
153
  api_version '2016_2'
172
154
 
173
- # the endpoint indicated in the > 2018_2 wsdl is invalid
155
+ # the endpoint indicated in the > 2018_2 WSDL is invalid
174
156
  # you must set the endpoint directly
175
157
  # https://github.com/NetSweet/netsuite/pull/473
176
158
  endpoint "https://#{wsdl_domain}/services/NetSuitePort_#{api_version}"
@@ -238,6 +220,19 @@ options = NetSuite::Records::BaseRefList.get_select_value(
238
220
  options.base_refs.map(&:name)
239
221
  ```
240
222
 
223
+ ## Uploading/Attaching Files
224
+
225
+ ```ruby
226
+ file = NetSuite::Records::File.new(
227
+ content: Base64.encode64(File.read('/path/to/file')),
228
+ name: 'Invoice.pdf',
229
+ )
230
+ file.add
231
+
232
+ invoice = NetSuite::Records::Invoice.get(internal_id: 1)
233
+ invoice.attach_file(NetSuite::Records::RecordRef.new(internal_id: file.internal_id))
234
+ ```
235
+
241
236
  ## Custom Records & Fields
242
237
 
243
238
  ```ruby
@@ -289,6 +284,20 @@ NetSuite::Records::BaseRefList.get_select_value(
289
284
  )
290
285
  ```
291
286
 
287
+ ## Null Fields
288
+
289
+ ```ruby
290
+ # updating a field on a record to be null
291
+ invoice = NetSuite::Records::Invoice.get(12345)
292
+ invoice.update(null_field_list: 'shipMethod')
293
+
294
+ # updating multiple fields on a record to be null
295
+ invoice.update(null_field_list: ['shipAddressList', 'shipMethod'])
296
+
297
+ # updating a custom fields on a record to be null, using custom field ID
298
+ invoice.update(null_field_list: 'custBody9')
299
+ ```
300
+
292
301
  ## Searching
293
302
 
294
303
  ```ruby
@@ -305,7 +314,7 @@ search = NetSuite::Records::Customer.search({
305
314
 
306
315
  `open https://system.netsuite.com/app/common/entity/custjob.nl?id=#{search.results.first.internal_id}`
307
316
 
308
- # find the avalara tax item. Some records don't support search.
317
+ # find the Avalara tax item. Some records don't support search.
309
318
  all_sales_taxes = NetSuite::Utilities.backoff { NetSuite::Records::SalesTaxItem.get_all }
310
319
  ns_tax_code = all_sales_taxes.detect { |st| st.item_id == 'AVATAX' }
311
320
 
@@ -416,13 +425,13 @@ NetSuite::Records::SalesOrder.search({
416
425
  ]
417
426
  },
418
427
 
419
- # the column syntax is a WIP. This will change in the future
428
+ # the column syntax is a WIP. This will change in the future.
420
429
  columns: {
421
430
  'tranSales:basic' => [
422
431
  'platformCommon:internalId/' => {},
423
432
  'platformCommon:email/' => {},
424
433
  'platformCommon:tranDate/' => {},
425
- # If you include columns that are only part of the *SearchRowBasic (ie. TransactionSearchRowBasic),
434
+ # If you include columns that are only part of the *SearchRowBasic (ie. TransactionSearchRowBasic),
426
435
  # they'll be readable on the resulting record just like regular fields (my_record.close_date).
427
436
  'platformCommon:closeDate/' => {}
428
437
  ],
@@ -598,6 +607,53 @@ deposit.payment = 20
598
607
  deposit.add
599
608
  ```
600
609
 
610
+ ## Getting Deleted Records
611
+
612
+ ```ruby
613
+ response = NetSuite::Records::LotNumberedInventoryItem.get_deleted({
614
+ criteria: [
615
+ {
616
+ # If you don't specify a type criteria, you'll get all deleted records,
617
+ # regardless of the type of record you called this on.
618
+ field: 'type',
619
+ operator: 'anyOf',
620
+ value: 'lotNumberedInventoryItem',
621
+ }
622
+ ],
623
+ })
624
+
625
+ Array(response.body.fetch(:deleted_record_list)).first
626
+ # => {
627
+ # :deleted_date => Wed, 16 Feb 2022 17:43:45 -0800,
628
+ # :record => {
629
+ # :name => "My Item",
630
+ # :@internal_id => "12485",
631
+ # :@type => "lotNumberedInventoryItem",
632
+ # :"@xsi:type" => "platformCore:RecordRef"
633
+ # }
634
+ # }
635
+
636
+ # deleted_record_list could be:
637
+ # nil - No records matching criteria were deleted
638
+ # Hash - A single record matching criteria was deleted
639
+ # Array - Multiple records matching criteria were deleted
640
+
641
+ # Simple pagination
642
+ page = 1
643
+ begin
644
+ response = NetSuite::Records::LotNumberedInventoryItem.get_deleted({
645
+ criteria: [
646
+ # your criteria
647
+ ],
648
+ page: page,
649
+ })
650
+
651
+ # Do your thing with response.body.fetch(:deleted_record_list)
652
+
653
+ page += 1
654
+ end until page > Integer(response.fetch(:total_pages))
655
+ ```
656
+
601
657
  ## Non-standard Operations
602
658
 
603
659
  ```ruby
@@ -49,7 +49,11 @@ module NetSuite
49
49
  end
50
50
 
51
51
  def response_body
52
- @response_body ||= response_hash[:base_ref]
52
+ @response_body ||= if response_hash[:base_ref].is_a?(Nori::StringIOFile)
53
+ { :@internal_id => Nokogiri::XML(@response.to_s).remove_namespaces!.at_xpath('//baseRef')[:internalId] }
54
+ else
55
+ response_hash[:base_ref]
56
+ end
53
57
  end
54
58
 
55
59
  def response_errors
@@ -66,8 +70,9 @@ module NetSuite
66
70
  error_obj = response_hash[:status][:status_detail]
67
71
  error_obj = [error_obj] if error_obj.class == Hash
68
72
  error_obj.map do |error|
73
+ next if error.keys == [:after_submit_failed]
69
74
  NetSuite::Error.new(error)
70
- end
75
+ end.compact
71
76
  end
72
77
 
73
78
  module Support
@@ -0,0 +1,87 @@
1
+ module NetSuite
2
+ module Actions
3
+ class AttachFile
4
+ include Support::Requests
5
+
6
+ def initialize(object, file)
7
+ @object = object
8
+ @file = file
9
+ end
10
+
11
+ private
12
+
13
+ def request(credentials = {})
14
+ NetSuite::Configuration.connection({}, credentials).call(:attach, :message => request_body)
15
+ end
16
+
17
+ # <soap:Body>
18
+ # <platformMsgs:attach>
19
+ # <platformCore:attachReference xsi:type="platformCore:AttachContactReference">
20
+ # <platformCore::attachTo internalId="176" type="customer" xsi:type="platformCore::RecordRef">
21
+ # </platformCore:attachTo>
22
+ # <platformCore:attachRecord internalId="1467" type="file" xsi:type="platformCore:RecordRef"/>
23
+ # </platformCore:attachReference>
24
+ # </platformMsgs:attach>
25
+ # </soap:Body>
26
+
27
+ def request_body
28
+ {
29
+ 'platformCore:attachReference' => {
30
+ '@xsi:type' => 'platformCore:AttachBasicReference',
31
+ 'platformCore:attachTo' => {
32
+ '@internalId' => @object.internal_id,
33
+ '@type' => @object.netsuite_type,
34
+ '@xsi:type' => 'platformCore:RecordRef'
35
+ },
36
+ 'platformCore:attachedRecord' => {
37
+ '@internalId' => @file.internal_id,
38
+ '@type' => 'file',
39
+ '@xsi:type' => 'platformCore:RecordRef'
40
+ }
41
+ }
42
+ }
43
+ end
44
+
45
+ def success?
46
+ @success ||= response_hash[:status][:@is_success] == 'true'
47
+ end
48
+
49
+ def response_body
50
+ @response_body ||= response_hash[:base_ref]
51
+ end
52
+
53
+ def response_errors
54
+ if response_hash[:status] && response_hash[:status][:status_detail]
55
+ @response_errors ||= errors
56
+ end
57
+ end
58
+
59
+ def response_hash
60
+ @response_hash ||= @response.to_hash[:attach_response][:write_response]
61
+ end
62
+
63
+ def errors
64
+ error_obj = response_hash[:status][:status_detail]
65
+ error_obj = [error_obj] if error_obj.class == Hash
66
+ error_obj.map do |error|
67
+ NetSuite::Error.new(error)
68
+ end
69
+ end
70
+
71
+ module Support
72
+ def attach_file(file, credentials = {})
73
+ response = NetSuite::Actions::AttachFile.call([self, file], credentials)
74
+
75
+ @errors = response.errors
76
+
77
+ if response.success?
78
+ @internal_id = response.body[:@internal_id]
79
+ true
80
+ else
81
+ false
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -21,7 +21,7 @@ module NetSuite
21
21
  end
22
22
 
23
23
  def soap_type
24
- @object.class.to_s.split('::').last.lower_camelcase
24
+ NetSuite::Support::Records.netsuite_type(@object)
25
25
  end
26
26
 
27
27
  # <soap:Body>
@@ -65,6 +65,20 @@ module NetSuite
65
65
  @response_body ||= response_hash[:base_ref]
66
66
  end
67
67
 
68
+ def response_errors
69
+ if response_hash[:status] && response_hash[:status][:status_detail]
70
+ @response_errors ||= errors
71
+ end
72
+ end
73
+
74
+ def errors
75
+ error_obj = response_hash[:status][:status_detail]
76
+ error_obj = [error_obj] if error_obj.class == Hash
77
+ error_obj.map do |error|
78
+ NetSuite::Error.new(error)
79
+ end
80
+ end
81
+
68
82
  module Support
69
83
  def delete(options = {}, credentials={})
70
84
  response = if options.empty?
@@ -72,6 +86,9 @@ module NetSuite
72
86
  else
73
87
  NetSuite::Actions::Delete.call([self, options], credentials)
74
88
  end
89
+
90
+ @errors = response.errors
91
+
75
92
  response.success?
76
93
  end
77
94
  end
@@ -40,7 +40,7 @@ module NetSuite
40
40
  }
41
41
  end
42
42
  else
43
- type = @klass.to_s.split('::').last.lower_camelcase
43
+ type = NetSuite::Support::Records.netsuite_type(@klass)
44
44
  record_type = 'platformCore:RecordRef'
45
45
 
46
46
  list.map do |internal_id|
@@ -21,7 +21,7 @@ module NetSuite
21
21
  end
22
22
 
23
23
  def soap_type
24
- @klass.to_s.split('::').last.lower_camelcase
24
+ NetSuite::Support::Records.netsuite_type(@klass)
25
25
  end
26
26
 
27
27
  # <soap:Body>
@@ -20,7 +20,7 @@ module NetSuite
20
20
  end
21
21
 
22
22
  def soap_type
23
- @object.class.to_s.split('::').last.lower_camelcase
23
+ NetSuite::Support::Records.netsuite_type(@object)
24
24
  end
25
25
 
26
26
  # <soap:Body>
@@ -33,7 +33,7 @@ module NetSuite
33
33
  }
34
34
  end
35
35
  else
36
- type = @klass.to_s.split('::').last.lower_camelcase
36
+ type = NetSuite::Support::Records.netsuite_type(@klass)
37
37
  record_type = 'platformCore:RecordRef'
38
38
 
39
39
  list.map do |internal_id|
@@ -28,12 +28,12 @@ module NetSuite
28
28
  def request_body
29
29
  {
30
30
  'platformMsgs:initializeRecord' => {
31
- 'platformCore:type' => @klass.to_s.split('::').last.lower_camelcase,
31
+ 'platformCore:type' => NetSuite::Support::Records.netsuite_type(@klass),
32
32
  'platformCore:reference' => {},
33
33
  :attributes! => {
34
34
  'platformCore:reference' => {
35
35
  'internalId' => @object.internal_id,
36
- :type => @object.class.to_s.split('::').last.lower_camelcase
36
+ :type => NetSuite::Support::Records.netsuite_type(@object)
37
37
  }
38
38
  }
39
39
  }