netsuite 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +0 -2
  2. data/README.md +154 -0
  3. data/lib/netsuite.rb +7 -0
  4. data/lib/netsuite/actions/add.rb +6 -21
  5. data/lib/netsuite/actions/delete.rb +7 -10
  6. data/lib/netsuite/actions/get.rb +7 -10
  7. data/lib/netsuite/actions/get_list.rb +77 -0
  8. data/lib/netsuite/actions/get_select_value.rb +71 -0
  9. data/lib/netsuite/actions/search.rb +151 -36
  10. data/lib/netsuite/actions/search_more_with_id.rb +3 -2
  11. data/lib/netsuite/actions/update.rb +16 -16
  12. data/lib/netsuite/configuration.rb +29 -15
  13. data/lib/netsuite/namespaces/list_support.rb +11 -0
  14. data/lib/netsuite/records/account.rb +1 -0
  15. data/lib/netsuite/records/base_ref_list.rb +33 -0
  16. data/lib/netsuite/records/cash_sale.rb +7 -0
  17. data/lib/netsuite/records/custom_field.rb +2 -1
  18. data/lib/netsuite/records/custom_field_list.rb +62 -12
  19. data/lib/netsuite/records/custom_record.rb +1 -7
  20. data/lib/netsuite/records/custom_record_ref.rb +7 -0
  21. data/lib/netsuite/records/customer.rb +4 -3
  22. data/lib/netsuite/records/phone_call.rb +29 -0
  23. data/lib/netsuite/records/record_ref.rb +1 -9
  24. data/lib/netsuite/records/sales_order.rb +2 -1
  25. data/lib/netsuite/records/support_case.rb +32 -0
  26. data/lib/netsuite/records/task.rb +4 -3
  27. data/lib/netsuite/records/transaction.rb +4 -21
  28. data/lib/netsuite/support/actions.rb +4 -0
  29. data/lib/netsuite/support/records.rb +3 -0
  30. data/lib/netsuite/support/search_result.rb +53 -29
  31. data/lib/netsuite/version.rb +1 -1
  32. data/netsuite.gemspec +1 -1
  33. data/spec/netsuite/actions/add_spec.rb +9 -13
  34. data/spec/netsuite/actions/delete_spec.rb +5 -8
  35. data/spec/netsuite/actions/get_spec.rb +39 -24
  36. data/spec/netsuite/actions/search_spec.rb +149 -0
  37. data/spec/netsuite/actions/update_spec.rb +9 -13
  38. data/spec/netsuite/configuration_spec.rb +3 -9
  39. data/spec/netsuite/records/custom_field_list_spec.rb +19 -5
  40. data/spec/netsuite/records/customer_spec.rb +2 -2
  41. data/spec/netsuite/records/phone_call_spec.rb +23 -0
  42. data/spec/netsuite/records/support_case_spec.rb +157 -0
  43. data/spec/support/fixtures/search/saved_search_customer.xml +54 -0
  44. data/spec/support/fixtures/search/saved_search_joined_custom_customer.xml +87 -0
  45. metadata +21 -4
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
1
  source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in netsuite.gemspec
4
2
  gemspec
data/README.md CHANGED
@@ -100,6 +100,160 @@ search = NetSuite::Records::Customer.search({
100
100
  })
101
101
 
102
102
  `open https://system.netsuite.com/app/common/entity/custjob.nl?id=#{search.results.first.internal_id}`
103
+
104
+ # advanced search building on saved search
105
+ search = NetSuite::Records::Customer.search({
106
+ saved: 500, # your saved search internalId
107
+ basic: [
108
+ {
109
+ field: 'entityId',
110
+ operator: 'hasKeywords',
111
+ value: 'Assumption',
112
+ },
113
+ {
114
+ field: 'stage',
115
+ operator: 'anyOf',
116
+ type: 'SearchMultiSelectCustomField',
117
+ value: [
118
+ '_lead', '_customer'
119
+ ]
120
+ },
121
+ {
122
+ field: 'customFieldList',
123
+ value: [
124
+ {
125
+ field: 'custentity_acustomfield',
126
+ operator: 'anyOf',
127
+ # type is needed for multiselect fields
128
+ type: 'SearchMultiSelectCustomField',
129
+ value: [
130
+ NetSuite::Records::CustomRecordRef.new(:internal_id => 1),
131
+ NetSuite::Records::CustomRecordRef.new(:internal_id => 2),
132
+ ]
133
+ }
134
+ ]
135
+ }
136
+ ]
137
+ })
138
+
139
+ # advanced search from stratch
140
+ search = NetSuite::Records::Transaction.search({
141
+ criteria: {
142
+ basic: [
143
+ {
144
+ field: 'type',
145
+ operator: 'anyOf',
146
+ type: 'SearchEnumMultiSelectField',
147
+ value: [ "_invoice", "_salesOrder" ]
148
+ },
149
+ {
150
+ field: 'tranDate',
151
+ operator: 'within',
152
+ # this is needed for date range search requests, for date requests with a single param type is not needed
153
+ type: 'SearchDateField',
154
+ value: [
155
+ # the following format is equivilent to ISO 8601
156
+ # Date.parse("1/1/2012").strftime("%Y-%m-%dT%H:%M:%S%z"),
157
+ # Date.parse("30/07/2013").strftime("%Y-%m-%dT%H:%M:%S%z")
158
+
159
+ # need to require the time library for this to work
160
+ Time.parse("01/01/2012").iso8601,
161
+ Time.parse("30/07/2013").iso8601,
162
+
163
+ # or you can use a string. Note that the format below is different from the format of the above code
164
+ # but it matches exactly what NS returns
165
+ # "2012-01-01T22:00:00.000-07:00",
166
+ # "2013-07-30T22:00:00.000-07:00"
167
+ ]
168
+ }
169
+ ],
170
+
171
+
172
+ # equivilent to the 'Account' label in the GUI
173
+ accountJoin: [
174
+ {
175
+ field: 'internalId',
176
+ operator: 'noneOf',
177
+ value: [ NetSuite::Records::Account.new(:internal_id => 215) ]
178
+ }
179
+ ],
180
+
181
+ itemJoin: [
182
+ {
183
+ field: 'customFieldList',
184
+ value: [
185
+ {
186
+ field: 'custitem_apcategoryforsales',
187
+ operator: 'anyOf',
188
+ type: 'SearchMultiSelectCustomField',
189
+ value: [
190
+ NetSuite::Records::Customer.new(:internal_id => 1),
191
+ NetSuite::Records::Customer.new(:internal_id => 2),
192
+ ]
193
+ }
194
+ ]
195
+ }
196
+ ]
197
+ },
198
+
199
+ # the column syntax is a WIP. This will change in the future
200
+ columns: {
201
+ 'tranSales:basic' => [
202
+ 'platformCommon:internalId/' => {},
203
+ 'platformCommon:email/' => {},
204
+ 'platformCommon:tranDate/' => {}
205
+ ],
206
+ 'tranSales:accountJoin' => [
207
+ 'platformCommon:internalId/' => {}
208
+ ],
209
+ 'tranSales:contactPrimaryJoin' => [
210
+ 'platformCommon:internalId/' => {}
211
+ ],
212
+ 'tranSales:customerJoin' => [
213
+ 'platformCommon:internalId/' => {}
214
+ ],
215
+ 'tranSales:itemJoin' => [
216
+ 'platformCommon:customFieldList' => [
217
+ 'platformCore:customField/' => {
218
+ '@internalId' => 'custitem_apcategoryforsales',
219
+ '@xsi:type' => "platformCore:SearchColumnSelectCustomField"
220
+ }
221
+ ]
222
+ ]
223
+ },
224
+
225
+ preferences: {
226
+ page_size: 10
227
+ }
228
+ })
229
+
230
+ # basic search with pagination / SearchMorewithId
231
+ search = NetSuite::Records::Customer.search(
232
+ criteria: {
233
+ basic: [
234
+ {
235
+ # no operator for booleans
236
+ field: 'isInactive',
237
+ value: false,
238
+ },
239
+ ]
240
+ },
241
+
242
+ preferences: {
243
+ page_size: 10,
244
+ }
245
+ )
246
+
247
+ search.results_in_batches do |batch|
248
+ puts batch.map(&:internal_id)
249
+ end
250
+
251
+ # making a call that hasn't been implemented yet
252
+ NetSuite::Configuration.connection.call :get_customization_id, message: {
253
+ 'platformMsgs:customizationType' => { '@getCustomizationType' => 'customRecordType'},
254
+ 'platformMsgs:includeInactives' => 'false'
255
+ }
256
+
103
257
  ```
104
258
 
105
259
 
@@ -13,6 +13,7 @@ module NetSuite
13
13
  autoload :ActSched, 'netsuite/namespaces/act_sched'
14
14
  autoload :ListAcct, 'netsuite/namespaces/list_acct'
15
15
  autoload :ListRel, 'netsuite/namespaces/list_rel'
16
+ autoload :ListSupport, 'netsuite/namespaces/list_support'
16
17
  autoload :PlatformCommon, 'netsuite/namespaces/platform_common'
17
18
  autoload :PlatformCore, 'netsuite/namespaces/platform_core'
18
19
  autoload :TranCust, 'netsuite/namespaces/tran_cust'
@@ -35,6 +36,8 @@ module NetSuite
35
36
  autoload :Add, 'netsuite/actions/add'
36
37
  autoload :Delete, 'netsuite/actions/delete'
37
38
  autoload :Get, 'netsuite/actions/get'
39
+ autoload :GetList, 'netsuite/actions/get_list'
40
+ autoload :GetSelectValue, 'netsuite/actions/get_select_value'
38
41
  autoload :Initialize, 'netsuite/actions/initialize'
39
42
  autoload :Update, 'netsuite/actions/update'
40
43
  autoload :Search, 'netsuite/actions/search'
@@ -45,8 +48,10 @@ module NetSuite
45
48
  autoload :AssemblyItem, 'netsuite/records/assembly_item'
46
49
  autoload :Account, 'netsuite/records/account'
47
50
  autoload :AccountingPeriod, 'netsuite/records/accounting_period'
51
+ autoload :BaseRefList, 'netsuite/records/base_ref_list'
48
52
  autoload :BillAddress, 'netsuite/records/bill_address'
49
53
  autoload :BinNumberList, 'netsuite/records/bin_number_list'
54
+ autoload :CashSale, 'netsuite/records/cash_sale'
50
55
  autoload :Classification, 'netsuite/records/classification'
51
56
  autoload :CreditMemo, 'netsuite/records/credit_memo'
52
57
  autoload :CreditMemoApply, 'netsuite/records/credit_memo_apply'
@@ -88,6 +93,7 @@ module NetSuite
88
93
  autoload :Location, 'netsuite/records/location'
89
94
  autoload :NonInventorySaleItem, 'netsuite/records/non_inventory_sale_item'
90
95
  autoload :PaymentMethod, 'netsuite/records/payment_method'
96
+ autoload :PhoneCall, 'netsuite/records/phone_call'
91
97
  autoload :PricingMatrix, 'netsuite/records/pricing_matrix'
92
98
  autoload :RecordRef, 'netsuite/records/record_ref'
93
99
  autoload :RevRecTemplate, 'netsuite/records/rev_rec_template'
@@ -95,6 +101,7 @@ module NetSuite
95
101
  autoload :SalesOrderItem, 'netsuite/records/sales_order_item'
96
102
  autoload :SalesOrderItemList, 'netsuite/records/sales_order_item_list'
97
103
  autoload :ShipAddress, 'netsuite/records/ship_address'
104
+ autoload :SupportCase, 'netsuite/records/support_case'
98
105
  autoload :Task, 'netsuite/records/task'
99
106
  autoload :Term, 'netsuite/records/term'
100
107
  autoload :Transaction, 'netsuite/records/transaction'
@@ -10,20 +10,7 @@ module NetSuite
10
10
  private
11
11
 
12
12
  def request
13
- NetSuite::Configuration.connection(
14
- namespaces: {
15
- 'xmlns:platformMsgs' => "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
16
- 'xmlns:platformCore' => "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
17
- 'xmlns:listRel' => "urn:relationships_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com",
18
- 'xmlns:tranSales' => "urn:sales_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com",
19
- 'xmlns:platformCommon' => "urn:common_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
20
- 'xmlns:listAcct' => "urn:accounting_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com",
21
- 'xmlns:actSched' => "urn:scheduling_#{NetSuite::Configuration.api_version}.activities.webservices.netsuite.com",
22
- 'xmlns:tranCust' => "urn:customers_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com",
23
- 'xmlns:setupCustom' => "urn:customization_#{NetSuite::Configuration.api_version}.setup.webservices.netsuite.com",
24
- 'xmlns:tranGeneral' => "urn:general_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com",
25
- },
26
- ).call :add, :message => request_body
13
+ NetSuite::Configuration.connection.call :add, :message => request_body
27
14
  end
28
15
 
29
16
  # <soap:Body>
@@ -37,20 +24,18 @@ module NetSuite
37
24
 
38
25
  def request_body
39
26
  hash = {
40
- 'platformMsgs:record' => @object.to_record,
41
- :attributes! => {
42
- 'platformMsgs:record' => {
43
- 'xsi:type' => @object.record_type
44
- }
27
+ 'platformMsgs:record' => {
28
+ :content! => @object.to_record,
29
+ '@xsi:type' => @object.record_type
45
30
  }
46
31
  }
47
32
 
48
33
  if @object.respond_to?(:internal_id) && @object.internal_id
49
- hash[:attributes!]['platformMsgs:record']['platformMsgs:internalId'] = @object.internal_id
34
+ hash['platformMsgs:record']['@platformMsgs:internalId'] = @object.internal_id
50
35
  end
51
36
 
52
37
  if @object.respond_to?(:external_id) && @object.external_id
53
- hash[:attributes!]['platformMsgs:record']['platformMsgs:externalId'] = @object.external_id
38
+ hash['platformMsgs:record']['@platformMsgs:externalId'] = @object.external_id
54
39
  end
55
40
 
56
41
  hash
@@ -30,27 +30,24 @@ module NetSuite
30
30
  # </soap:Body>
31
31
  def request_body
32
32
  body = {
33
- 'platformMsgs:baseRef' => {},
34
- :attributes! => {
35
- 'platformMsgs:baseRef' => {
36
- 'xsi:type' => (@options[:custom] ? 'platformCore:CustomRecordRef' : 'platformCore:RecordRef')
37
- }
38
- }
33
+ 'platformMsgs:baseRef' => {
34
+ '@xsi:type' => (@options[:custom] ? 'platformCore:CustomRecordRef' : 'platformCore:RecordRef')
35
+ },
39
36
  }
40
37
 
41
38
  if @object.respond_to?(:external_id) && @object.external_id
42
- body[:attributes!]['platformMsgs:baseRef']['externalId'] = @object.external_id
39
+ body['platformMsgs:baseRef']['@externalId'] = @object.external_id
43
40
  end
44
41
 
45
42
  if @object.respond_to?(:internal_id) && @object.internal_id
46
- body[:attributes!]['platformMsgs:baseRef']['internalId'] = @object.internal_id
43
+ body['platformMsgs:baseRef']['@internalId'] = @object.internal_id
47
44
  end
48
45
 
49
46
  if @object.class.respond_to?(:type_id) && @object.class.type_id
50
- body[:attributes!]['platformMsgs:baseRef']['typeId'] = @object.class.type_id
47
+ body['platformMsgs:baseRef']['@typeId'] = @object.class.type_id
51
48
  end
52
49
 
53
- body[:attributes!]['platformMsgs:baseRef']['type'] = soap_type unless @options[:custom]
50
+ body['platformMsgs:baseRef']['@type'] = soap_type unless @options[:custom]
54
51
 
55
52
  body
56
53
  end
@@ -16,7 +16,7 @@ module NetSuite
16
16
  'xmlns:platformMsgs' => "urn:messages_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
17
17
  'xmlns:platformCore' => "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
18
18
  },
19
- ).call :get, message: request_body#, message_tag: :platformMsgs
19
+ ).call :get, message: request_body
20
20
  end
21
21
 
22
22
  def soap_type
@@ -32,17 +32,14 @@ module NetSuite
32
32
  # </soap:Body>
33
33
  def request_body
34
34
  body = {
35
- 'platformMsgs:baseRef' => {},
36
- :attributes! => {
37
- 'platformMsgs:baseRef' => {
38
- 'xsi:type' => (@options[:custom] ? 'platformCore:CustomRecordRef' : 'platformCore:RecordRef')
39
- }
35
+ 'platformMsgs:baseRef' => {
36
+ '@xsi:type' => (@options[:custom] ? 'platformCore:CustomRecordRef' : 'platformCore:RecordRef')
40
37
  }
41
38
  }
42
- body[:attributes!]['platformMsgs:baseRef']['externalId'] = @options[:external_id] if @options[:external_id]
43
- body[:attributes!]['platformMsgs:baseRef']['internalId'] = @options[:internal_id] if @options[:internal_id]
44
- body[:attributes!]['platformMsgs:baseRef']['typeId'] = @options[:type_id] if @options[:type_id]
45
- body[:attributes!]['platformMsgs:baseRef']['type'] = soap_type unless @options[:custom]
39
+ body['platformMsgs:baseRef']['@externalId'] = @options[:external_id] if @options[:external_id]
40
+ body['platformMsgs:baseRef']['@internalId'] = @options[:internal_id] if @options[:internal_id]
41
+ body['platformMsgs:baseRef']['@typeId'] = @options[:type_id] if @options[:type_id]
42
+ body['platformMsgs:baseRef']['@type'] = soap_type unless @options[:custom]
46
43
  body
47
44
  end
48
45
 
@@ -0,0 +1,77 @@
1
+ module NetSuite
2
+ module Actions
3
+ class GetList
4
+ include Support::Requests
5
+
6
+ def initialize(klass, options = { })
7
+ @klass = klass
8
+ @options = options
9
+ end
10
+
11
+ private
12
+
13
+ def request
14
+ NetSuite::Configuration.connection.call :get_list, :message => request_body
15
+ end
16
+
17
+ def request_body
18
+ if @options[:type_id]
19
+ type = @options[:type_id]
20
+ record_type = 'platformCore:CustomRecordRef'
21
+ else
22
+ type = @klass.to_s.split('::').last.lower_camelcase
23
+ record_type = 'platformCore:RecordRef'
24
+ end
25
+
26
+ list = @options.is_a?(Hash) ? @options[:list] : @options
27
+
28
+ {
29
+ baseRef: list.map do |internal_id|
30
+ {
31
+ '@internalId' => internal_id,
32
+ '@typeId' => type,
33
+ '@xsi:type' => record_type
34
+ }
35
+ end
36
+ }
37
+ end
38
+
39
+ def response_header
40
+ @response_header ||= response_header_hash
41
+ end
42
+
43
+ def response_header_hash
44
+ @response_header_hash = @response.header[:document_info]
45
+ end
46
+
47
+ def response_body
48
+ @response_body ||= @response.body[:get_list_response][:read_response_list][:read_response]
49
+ end
50
+
51
+ def success?
52
+ # each returned record has its own status; for now if one fails, the entire operation has failed
53
+ @success ||= response_body.detect { |r| r[:status][:@is_success] != 'true' }.nil?
54
+ end
55
+
56
+ module Support
57
+ def self.included(base)
58
+ base.extend(ClassMethods)
59
+ end
60
+
61
+ module ClassMethods
62
+ def get_list(options = { })
63
+ response = NetSuite::Actions::GetList.call(self, options)
64
+
65
+ if response.success?
66
+ response.body.map do |record|
67
+ new(record[:record])
68
+ end
69
+ else
70
+ false
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,71 @@
1
+ module NetSuite
2
+ module Actions
3
+ class GetSelectValue
4
+ include Support::Requests
5
+
6
+ def initialize(klass, options = {})
7
+ @klass = klass
8
+ @options = options
9
+ end
10
+
11
+ private
12
+
13
+ def request
14
+ NetSuite::Configuration.connection(
15
+ namespaces: {
16
+ 'xmlns:platformMsgs' => "urn:messages_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
17
+ 'xmlns:platformCore' => "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
18
+ },
19
+ ).call :get_select_value, :message => @options
20
+ end
21
+
22
+ def success?
23
+ @success ||= response_hash[:status][:@is_success] == 'true'
24
+ end
25
+
26
+ def response_body
27
+ @response_body ||= response_hash[:base_ref_list]
28
+ end
29
+
30
+ def response_hash
31
+ @response_hash = @response.body[:get_select_value_response][:get_select_value_result]
32
+ end
33
+
34
+ module Support
35
+
36
+ def self.included(base)
37
+ base.extend(ClassMethods)
38
+ end
39
+
40
+ module ClassMethods
41
+
42
+ def get_select_value(options = {})
43
+ message = {
44
+ pageIndex: (options.delete(:pageIndex) || 1),
45
+ fieldDescription: field_description(options)
46
+ }
47
+
48
+ response = NetSuite::Actions::GetSelectValue.call(self, message)
49
+
50
+ if response.success?
51
+ new(response.body)
52
+ else
53
+ raise RecordNotFound, "#{self} with OPTIONS=#{options.inspect} could not be found"
54
+ end
55
+ end
56
+
57
+ private
58
+ # TODO this goes against the design of the rest of the gem; should be removed in the future
59
+ def field_description(options)
60
+ options.inject({}) do |h, (k, v)|
61
+ h["platformCore:#{k}"] = v
62
+ h
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end