netsuite 0.8.3 → 0.8.4

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/README.md +3 -2
  4. data/circle.yml +3 -2
  5. data/lib/netsuite.rb +17 -0
  6. data/lib/netsuite/actions/login.rb +11 -0
  7. data/lib/netsuite/actions/update.rb +6 -2
  8. data/lib/netsuite/errors.rb +1 -0
  9. data/lib/netsuite/records/assembly_build.rb +4 -1
  10. data/lib/netsuite/records/assembly_item.rb +1 -0
  11. data/lib/netsuite/records/assembly_unbuild.rb +3 -0
  12. data/lib/netsuite/records/bin_number.rb +18 -0
  13. data/lib/netsuite/records/bin_number_list.rb +1 -20
  14. data/lib/netsuite/records/classification.rb +1 -1
  15. data/lib/netsuite/records/custom_field_list.rb +10 -2
  16. data/lib/netsuite/records/custom_record.rb +2 -2
  17. data/lib/netsuite/records/custom_record_ref.rb +1 -0
  18. data/lib/netsuite/records/customer.rb +3 -3
  19. data/lib/netsuite/records/customer_deposit.rb +5 -2
  20. data/lib/netsuite/records/customer_sales_team.rb +24 -0
  21. data/lib/netsuite/records/customer_sales_team_list.rb +9 -0
  22. data/lib/netsuite/records/customer_status.rb +29 -0
  23. data/lib/netsuite/records/customer_subscription.rb +18 -0
  24. data/lib/netsuite/records/customer_subscriptions_list.rb +10 -0
  25. data/lib/netsuite/records/entity_custom_field.rb +53 -0
  26. data/lib/netsuite/records/non_inventory_resale_item.rb +2 -2
  27. data/lib/netsuite/records/price.rb +17 -0
  28. data/lib/netsuite/records/price_list.rb +9 -0
  29. data/lib/netsuite/records/pricing.rb +20 -0
  30. data/lib/netsuite/records/pricing_matrix.rb +2 -2
  31. data/lib/netsuite/records/promotions.rb +26 -0
  32. data/lib/netsuite/records/promotions_list.rb +9 -0
  33. data/lib/netsuite/records/sales_order.rb +1 -0
  34. data/lib/netsuite/records/sales_role.rb +26 -0
  35. data/lib/netsuite/records/sales_tax_item.rb +3 -1
  36. data/lib/netsuite/records/serialized_assembly_item.rb +239 -0
  37. data/lib/netsuite/records/service_sale_item.rb +1 -1
  38. data/lib/netsuite/records/tax_group.rb +2 -2
  39. data/lib/netsuite/records/transaction_body_custom_field.rb +61 -0
  40. data/lib/netsuite/records/transaction_column_custom_field.rb +59 -0
  41. data/lib/netsuite/records/vendor_credit.rb +2 -0
  42. data/lib/netsuite/records/work_order.rb +8 -0
  43. data/lib/netsuite/support/country.rb +27 -15
  44. data/lib/netsuite/utilities.rb +45 -20
  45. data/lib/netsuite/version.rb +1 -1
  46. data/spec/netsuite/actions/login_spec.rb +23 -0
  47. data/spec/netsuite/actions/update_spec.rb +42 -0
  48. data/spec/netsuite/records/address_spec.rb +10 -0
  49. data/spec/netsuite/records/basic_record_spec.rb +6 -1
  50. data/spec/netsuite/records/bin_number_spec.rb +23 -0
  51. data/spec/netsuite/records/custom_field_list_spec.rb +33 -0
  52. data/spec/netsuite/records/customer_sales_team_list_spec.rb +41 -0
  53. data/spec/netsuite/records/customer_spec.rb +22 -1
  54. data/spec/netsuite/records/customer_subscription_spec.rb +41 -0
  55. data/spec/netsuite/records/customer_subscriptions_list_spec.rb +19 -0
  56. data/spec/netsuite/records/entity_custom_field_spec.rb +34 -0
  57. data/spec/netsuite/records/pricing_matrix_spec.rb +15 -13
  58. data/spec/netsuite/records/transaction_body_custom_field_spec.rb +32 -0
  59. data/spec/netsuite/records/transaction_column_custom_field_spec.rb +32 -0
  60. data/spec/netsuite/records/vendor_credit_spec.rb +29 -0
  61. data/spec/netsuite/utilities_spec.rb +15 -3
  62. data/spec/spec_helper.rb +5 -4
  63. metadata +34 -2
@@ -26,7 +26,7 @@ module NetSuite
26
26
  record_refs :billing_schedule, :cost_category, :custom_form, :deferred_revenue_account, :department, :income_account,
27
27
  :issue_product, :item_options_list, :klass, :location, :parent, :pricing_group, :purchase_tax_code,
28
28
  :quantity_pricing_schedule, :rev_rec_schedule, :sale_unit, :sales_tax_code, :store_display_image,
29
- :store_display_thumbnail, :store_item_template, :tax_schedule, :units_type
29
+ :store_display_thumbnail, :store_item_template, :tax_schedule, :units_type, :revenue_recognition_rule
30
30
 
31
31
  field :pricing_matrix, PricingMatrix
32
32
  field :custom_field_list, CustomFieldList
@@ -7,8 +7,8 @@ module NetSuite
7
7
  include Support::Actions
8
8
  include Namespaces::ListAcct
9
9
 
10
- # NOTE search not available on TaxGroup!
11
-
10
+ # NOTE `get_all` is not available in recent API versions ~2017_2
11
+ # `search` is only available in recent API versions
12
12
  actions :get, :get_list, :add, :initialize, :delete, :update, :upsert, :search, :get_all
13
13
 
14
14
  fields :city, :county, :description, :include_children, :is_default, :is_inactive,
@@ -0,0 +1,61 @@
1
+ module NetSuite
2
+ module Records
3
+ class TransactionBodyCustomField
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::SetupCustom
9
+
10
+ actions :get, :get_list, :add, :delete, :update, :upsert, :upsert_list
11
+
12
+ # http://www.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2017_1/schema/record/transactionbodycustomfield.html
13
+ fields(
14
+ :label,
15
+ :store_value,
16
+ :display_type,
17
+ :is_mandatory,
18
+ :default_checked,
19
+ :is_formula,
20
+ :body_assembly_build,
21
+ :body_bom,
22
+ :body_b_tegata,
23
+ :body_customer_payment,
24
+ :body_deposit,
25
+ :body_expense_report,
26
+ :body_inventory_adjustment,
27
+ :body_item_fulfillment,
28
+ :body_item_fulfillment_order,
29
+ :body_item__receipt,
30
+ :body_item__receipt_order,
31
+ :body_journal,
32
+ :body_opportunity,
33
+ :body_other_transaction,
34
+ :body_picking_ticket,
35
+ :body_print_flag,
36
+ :body_print_packing_slip,
37
+ :body_print_statement,
38
+ :body_purchase,
39
+ :body_sale,
40
+ :body_store,
41
+ :body_transfer_order,
42
+ :body_vendor_payment,
43
+ :access_level,
44
+ :search_level,
45
+ :field_type,
46
+ :script_id
47
+ )
48
+
49
+ record_refs :owner
50
+
51
+ attr_reader :internal_id
52
+ attr_accessor :external_id
53
+
54
+ def initialize(attributes = {})
55
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
56
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
57
+ initialize_from_attributes_hash(attributes)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,59 @@
1
+ module NetSuite
2
+ module Records
3
+ class TransactionColumnCustomField
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::SetupCustom
9
+
10
+ actions :get, :get_list, :add, :delete, :update, :upsert, :upsert_list
11
+
12
+ # http://www.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2017_1/schema/record/transactioncolumncustomfield.html
13
+ fields(
14
+ :label,
15
+ :store_value,
16
+ :display_type,
17
+ :is_mandatory,
18
+ :default_checked,
19
+ :is_formula,
20
+ :col_expense,
21
+ :col_purchase,
22
+ :col_sale,
23
+ :col_opportunity,
24
+ :col_store,
25
+ :col_store_hidden,
26
+ :col_journal,
27
+ :col_expense_report,
28
+ :col_time,
29
+ :col_transfer_order,
30
+ :col_item_receipt,
31
+ :col_item_receipt_order,
32
+ :col_item_fulfillment,
33
+ :col_item_fulfillment_order,
34
+ :col_print_flag,
35
+ :col_picking_ticket,
36
+ :col_packing_slip,
37
+ :col_return_form,
38
+ :col_store_with_groups,
39
+ :col_group_on_invoices,
40
+ :col_kit_item,
41
+ :access_level,
42
+ :search_level,
43
+ :field_type,
44
+ :script_id
45
+ )
46
+
47
+ record_refs :owner
48
+
49
+ attr_reader :internal_id
50
+ attr_accessor :external_id
51
+
52
+ def initialize(attributes = {})
53
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
54
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
55
+ initialize_from_attributes_hash(attributes)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -33,9 +33,11 @@ module NetSuite
33
33
  field :custom_field_list, CustomFieldList
34
34
 
35
35
  attr_reader :internal_id
36
+ attr_accessor :external_id
36
37
 
37
38
  def initialize(attributes = {})
38
39
  @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
40
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
39
41
  initialize_from_attributes_hash(attributes)
40
42
  end
41
43
 
@@ -33,6 +33,14 @@ module NetSuite
33
33
  @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
34
34
  initialize_from_attributes_hash(attributes)
35
35
  end
36
+
37
+ def self.search_class_name
38
+ "Transaction"
39
+ end
40
+
41
+ def self.search_class_namespace
42
+ 'tranSales'
43
+ end
36
44
  end
37
45
  end
38
46
  end
@@ -4,6 +4,7 @@ module NetSuite
4
4
 
5
5
  ISO_TO_NETSUITE = {
6
6
  'AF' => '_afghanistan',
7
+ 'AX' => '_alandIslands',
7
8
  'AL' => '_albania',
8
9
  'DZ' => '_algeria',
9
10
  'AS' => '_americanSamoa',
@@ -29,6 +30,7 @@ module NetSuite
29
30
  'BM' => '_bermuda',
30
31
  'BT' => '_bhutan',
31
32
  'BO' => '_bolivia',
33
+ 'BQ' => '_bonaireSaintEustatiusAndSaba',
32
34
  'BA' => '_bosniaAndHerzegovina',
33
35
  'BW' => '_botswana',
34
36
  'BV' => '_bouvetIsland',
@@ -41,9 +43,11 @@ module NetSuite
41
43
  'KH' => '_cambodia',
42
44
  'CM' => '_cameroon',
43
45
  'CA' => '_canada',
44
- 'CV' => '_capVerde',
46
+ 'IC' => '_canaryIslands',
47
+ 'CV' => '_capeVerde',
45
48
  'KY' => '_caymanIslands',
46
49
  'CF' => '_centralAfricanRepublic',
50
+ 'EA' => '_ceutaAndMelilla',
47
51
  'TD' => '_chad',
48
52
  'CL' => '_chile',
49
53
  'CN' => '_china',
@@ -65,7 +69,7 @@ module NetSuite
65
69
  'DJ' => '_djibouti',
66
70
  'DM' => '_dominica',
67
71
  'DO' => '_dominicanRepublic',
68
- 'TP' => '_eastTimor',
72
+ 'TL' => '_eastTimor',
69
73
  'EC' => '_ecuador',
70
74
  'EG' => '_egypt',
71
75
  'SV' => '_elSalvador',
@@ -73,7 +77,7 @@ module NetSuite
73
77
  'ER' => '_eritrea',
74
78
  'EE' => '_estonia',
75
79
  'ET' => '_ethiopia',
76
- 'FK' => '_falklandIslandsMalvina',
80
+ 'FK' => '_falklandIslands',
77
81
  'FO' => '_faroeIslands',
78
82
  'FJ' => '_fiji',
79
83
  'FI' => '_finland',
@@ -121,6 +125,7 @@ module NetSuite
121
125
  'KI' => '_kiribati',
122
126
  'KP' => '_koreaDemocraticPeoplesRepublic',
123
127
  'KR' => '_koreaRepublicOf',
128
+ 'XK' => '_kosovo',
124
129
  'KW' => '_kuwait',
125
130
  'KG' => '_kyrgyzstan',
126
131
  'LA' => '_laoPeoplesDemocraticRepublic',
@@ -128,7 +133,7 @@ module NetSuite
128
133
  'LB' => '_lebanon',
129
134
  'LS' => '_lesotho',
130
135
  'LR' => '_liberia',
131
- 'LY' => '_libyanArabJamahiriya',
136
+ 'LY' => '_libya',
132
137
  'LI' => '_liechtenstein',
133
138
  'LT' => '_lithuania',
134
139
  'LU' => '_luxembourg',
@@ -159,7 +164,6 @@ module NetSuite
159
164
  'NR' => '_nauru',
160
165
  'NP' => '_nepal',
161
166
  'NL' => '_netherlands',
162
- 'AN' => '_netherlandsAntilles',
163
167
  'NC' => '_newCaledonia',
164
168
  'NZ' => '_newZealand',
165
169
  'NI' => '_nicaragua',
@@ -172,7 +176,7 @@ module NetSuite
172
176
  'OM' => '_oman',
173
177
  'PK' => '_pakistan',
174
178
  'PW' => '_palau',
175
- 'PS' => '_palestinianTerritories',
179
+ 'PS' => '_stateOfPalestine',
176
180
  'PA' => '_panama',
177
181
  'PG' => '_papuaNewGuinea',
178
182
  'PY' => '_paraguay',
@@ -185,18 +189,19 @@ module NetSuite
185
189
  'QA' => '_qatar',
186
190
  'RE' => '_reunionIsland',
187
191
  'RO' => '_romania',
188
- 'RS' => '_serbia',
189
192
  'RU' => '_russianFederation',
190
193
  'RW' => '_rwanda',
191
194
  'BL' => '_saintBarthelemy',
195
+ 'SH' => '_saintHelena',
192
196
  'KN' => '_saintKittsAndNevis',
193
197
  'LC' => '_saintLucia',
198
+ 'MF' => '_saintMartin',
194
199
  'VC' => '_saintVincentAndTheGrenadines',
195
200
  'SM' => '_sanMarino',
196
201
  'ST' => '_saoTomeAndPrincipe',
197
202
  'SA' => '_saudiArabia',
198
203
  'SN' => '_senegal',
199
- 'CS' => '_serbia',
204
+ 'RS' => '_serbia',
200
205
  'SC' => '_seychelles',
201
206
  'SL' => '_sierraLeone',
202
207
  'SG' => '_singapore',
@@ -207,9 +212,9 @@ module NetSuite
207
212
  'SO' => '_somalia',
208
213
  'ZA' => '_southAfrica',
209
214
  'GS' => '_southGeorgia',
215
+ 'SS' => '_southSudan',
210
216
  'ES' => '_spain',
211
217
  'LK' => '_sriLanka',
212
- 'SH' => '_stHelena',
213
218
  'PM' => '_stPierreAndMiquelon',
214
219
  'SD' => '_sudan',
215
220
  'SR' => '_suriname',
@@ -234,8 +239,7 @@ module NetSuite
234
239
  'UG' => '_uganda',
235
240
  'UA' => '_ukraine',
236
241
  'AE' => '_unitedArabEmirates',
237
- # NOTE GB country code changed on 2016_1
238
- 'GB' => NetSuite::Configuration.api_version <= "2015_2" ? '_unitedKingdomGB' : '_unitedKingdom',
242
+ 'GB' => '_unitedKingdom',
239
243
  'US' => '_unitedStates',
240
244
  'UY' => '_uruguay',
241
245
  'UM' => '_uSMinorOutlyingIslands',
@@ -247,16 +251,15 @@ module NetSuite
247
251
  'VI' => '_virginIslandsUSA',
248
252
  'WF' => '_wallisAndFutunaIslands',
249
253
  'EH' => '_westernSahara',
250
- 'WS' => '_westernSamoa',
254
+ 'WS' => '_samoa',
251
255
  'YE' => '_yemen',
252
- 'YU' => '_yugoslavia',
253
256
  'ZM' => '_zambia',
254
257
  'ZW' => '_zimbabwe'
255
258
  }
256
259
 
257
260
  def initialize(iso_or_name = '')
258
261
  if iso_or_name =~ /^[A-Z]{2}/
259
- @id = ISO_TO_NETSUITE.fetch(iso_or_name)
262
+ @id = iso_to_netsuite.fetch(iso_or_name)
260
263
  else
261
264
  @id = iso_or_name
262
265
  end
@@ -269,13 +272,22 @@ module NetSuite
269
272
  alias :eql? :==
270
273
 
271
274
  def to_iso
272
- ISO_TO_NETSUITE.key(@id)
275
+ iso_to_netsuite.key(@id)
273
276
  end
274
277
 
275
278
  def to_record
276
279
  @id
277
280
  end
278
281
 
282
+ def iso_to_netsuite
283
+ # NOTE GB country code changed on 2016_1
284
+ if NetSuite::Configuration.api_version <= "2015_2"
285
+ ISO_TO_NETSUITE.merge({ 'GB' => '_unitedKingdomGB' })
286
+ else
287
+ ISO_TO_NETSUITE
288
+ end
289
+ end
290
+
279
291
  end
280
292
  end
281
293
  end
@@ -1,3 +1,5 @@
1
+ require 'date'
2
+
1
3
  module NetSuite
2
4
  module Utilities
3
5
  extend self
@@ -40,6 +42,9 @@ module NetSuite
40
42
  def netsuite_data_center_urls(account_id)
41
43
  data_center_call_response = NetSuite::Configuration.connection({
42
44
  # NOTE force a production WSDL so the sandbox settings are ignored
45
+ # as of 1/20/18 NS will start using the account ID to determine
46
+ # if a account is sandbox (123_SB1) as opposed to using a sandbox domain
47
+
43
48
  wsdl: 'https://webservices.netsuite.com/wsdl/v2017_2_0/netsuite.wsdl',
44
49
 
45
50
  # NOTE don't inherit default namespace settings, it includes the API version
@@ -59,6 +64,11 @@ module NetSuite
59
64
  end
60
65
  end
61
66
 
67
+ # TODO consider what to dop with this duplicate data center implementation
68
+ def data_center_url(*args)
69
+ DataCenter.get(*args)
70
+ end
71
+
62
72
  def backoff(options = {})
63
73
  # TODO the default backoff attempts should be customizable the global config
64
74
  options[:attempts] ||= 8
@@ -105,6 +115,8 @@ module NetSuite
105
115
  # https://github.com/stripe/stripe-netsuite/issues/815
106
116
  if !e.message.include?("Only one request may be made against a session at a time") &&
107
117
  !e.message.include?('java.util.ConcurrentModificationException') &&
118
+ !e.message.include?('java.lang.IllegalStateException') &&
119
+ !e.message.include?('java.lang.reflect.InvocationTargetException') &&
108
120
  !e.message.include?('com.netledger.common.exceptions.NLDatabaseOfflineException') &&
109
121
  !e.message.include?('com.netledger.database.NLConnectionUtil$NoCompanyDbsOnlineException') &&
110
122
  !e.message.include?('com.netledger.cache.CacheUnavailableException') &&
@@ -112,6 +124,7 @@ module NetSuite
112
124
  !e.message.include?('An unexpected error occurred.') &&
113
125
  !e.message.include?('An unexpected error has occurred. Technical Support has been alerted to this problem.') &&
114
126
  !e.message.include?('Session invalidation is in progress with different thread') &&
127
+ !e.message.include?('[missing resource APP:ERRORMESSAGE:WS_AN_UNEXPECTED_ERROR_OCCURRED] [missing resource APP:ERRORMESSAGE:ERROR_ID_1]') &&
115
128
  !e.message.include?('SuiteTalk concurrent request limit exceeded. Request blocked.') &&
116
129
  # maintenance is the new outage: this message is being used for intermittent errors
117
130
  !e.message.include?('The account you are trying to access is currently unavailable while we undergo our regularly scheduled maintenance.') &&
@@ -135,15 +148,16 @@ module NetSuite
135
148
 
136
149
  def request_failed?(ns_object)
137
150
  return false if ns_object.errors.nil? || ns_object.errors.empty?
151
+ ns_object.errors.any? { |x| x.type == "ERROR" }
152
+ end
138
153
 
139
- warnings = ns_object.errors.select { |x| x.type == "WARN" }
140
- errors = ns_object.errors.select { |x| x.type == "ERROR" }
141
-
142
- # warnings.each do |warn|
143
- # log.warn(warn.message, code: warn.code)
144
- # end
154
+ def get_field_options(recordType, fieldName)
155
+ options = NetSuite::Records::BaseRefList.get_select_value(
156
+ field: fieldName,
157
+ recordType: recordType
158
+ )
145
159
 
146
- return errors.size > 0
160
+ options.base_refs
147
161
  end
148
162
 
149
163
  def get_item(ns_item_internal_id, opts = {})
@@ -222,7 +236,11 @@ module NetSuite
222
236
  field_name = 'email'
223
237
  end
224
238
 
225
- field_name ||= 'name'
239
+ field_name ||= if record.to_s.end_with?('Item')
240
+ 'displayName'
241
+ else
242
+ 'name'
243
+ end
226
244
 
227
245
  # TODO remove backoff when it's built-in to search
228
246
  search = backoff { record.search({
@@ -243,25 +261,32 @@ module NetSuite
243
261
  nil
244
262
  end
245
263
 
246
- def data_center_url(*args)
247
- DataCenter.get(*args)
248
- end
249
-
250
264
  # http://mikebian.co/notes-on-dates-timezones-with-netsuites-suitetalk-api/
265
+ # https://wyeworks.com/blog/2016/6/22/behavior-changes-in-ruby-2.4
266
+ # https://github.com/rails/rails/commit/c9c5788a527b70d7f983e2b4b47e3afd863d9f48
267
+
251
268
  # assumes UTC0 unix timestamp
252
269
  def normalize_time_to_netsuite_date(unix_timestamp)
253
270
  # convert to date to eliminate hr/min/sec
254
- time = Time.at(unix_timestamp).utc.to_date.to_datetime
255
-
256
- offset = 8
257
- time = time.new_offset("-08:00")
258
-
259
- if time.to_time.dst?
260
- offset = 7
271
+ time = Time.at(unix_timestamp).
272
+ utc.
273
+ to_date.
274
+ to_datetime
275
+
276
+ # tzinfo allows us to determine the dst status of the time being passed in
277
+ # NetSuite requires that the time be passed to the API with the PDT TZ offset
278
+ # of the time passed in (i.e. not the current TZ offset of PDT)
279
+
280
+ offset = Rational(-7, 24)
281
+
282
+ if defined?(TZInfo)
283
+ time = TZInfo::Timezone.get("America/Los_Angeles").utc_to_local(time)
284
+ offset = time.offset
285
+ else
261
286
  time = time.new_offset("-07:00")
262
287
  end
263
288
 
264
- (time + Rational(offset, 24)).iso8601
289
+ (time + (offset * -1)).iso8601
265
290
  end
266
291
 
267
292
  end