netsuite_rails 0.2.2 → 0.3.1

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 (34) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +4 -2
  3. data/README.md +186 -10
  4. data/circle.yml +3 -0
  5. data/lib/netsuite_rails/configuration.rb +7 -5
  6. data/lib/netsuite_rails/netsuite_rails.rb +24 -4
  7. data/lib/netsuite_rails/poll_trigger.rb +10 -9
  8. data/lib/netsuite_rails/record_sync/poll_manager.rb +23 -11
  9. data/lib/netsuite_rails/record_sync/pull_manager.rb +2 -0
  10. data/lib/netsuite_rails/record_sync/push_manager.rb +76 -38
  11. data/lib/netsuite_rails/record_sync.rb +28 -11
  12. data/lib/netsuite_rails/routines/company_contact_match.rb +98 -0
  13. data/lib/netsuite_rails/spec/disabler.rb +27 -0
  14. data/lib/netsuite_rails/spec/query_helpers.rb +93 -0
  15. data/lib/netsuite_rails/spec/spec_helper.rb +2 -79
  16. data/lib/netsuite_rails/sync_trigger.rb +40 -17
  17. data/lib/netsuite_rails/tasks/netsuite.rb +33 -4
  18. data/lib/netsuite_rails/transformations.rb +59 -19
  19. data/lib/netsuite_rails/url_helper.rb +45 -12
  20. data/netsuite_rails.gemspec +2 -2
  21. data/spec/models/configuration_spec.rb +11 -0
  22. data/spec/models/poll_manager_spec.rb +11 -2
  23. data/spec/models/poll_trigger_spec.rb +31 -11
  24. data/spec/models/record_sync/push_manager_spec.rb +51 -0
  25. data/spec/models/record_sync_spec.rb +16 -0
  26. data/spec/models/spec_helper_spec.rb +1 -2
  27. data/spec/models/transformations_spec.rb +62 -0
  28. data/spec/models/url_helper_spec.rb +20 -9
  29. data/spec/spec_helper.rb +19 -0
  30. data/spec/support/example_models.rb +33 -1
  31. metadata +19 -25
  32. data/.travis.yml +0 -3
  33. data/lib/netsuite_rails/netsuite_configure.rb +0 -14
  34. data/spec/support/netsuite_rails.rb +0 -1
@@ -8,7 +8,10 @@ module NetSuiteRails
8
8
  # TODO check to see if anything is changed before moving forward
9
9
  # if changes_keys.blank? && local_record.netsuite_manual_fields
10
10
 
11
- if opts[:modified_fields]
11
+ # always include the full netsuite field mapping, regardless of which
12
+ # fields were modfified locally, when initially creating the netsuite record
13
+
14
+ if opts[:modified_fields] && !local_record.new_netsuite_record?
12
15
  # if Array, we need to convert info fields hash based on the record definition
13
16
  if opts[:modified_fields].is_a?(Array)
14
17
  opts[:modified_fields] = all_netsuite_fields(local_record).select { |k,v| opts[:modified_fields].include?(k) }
@@ -21,17 +24,10 @@ module NetSuiteRails
21
24
 
22
25
  local_record.netsuite_execute_callbacks(local_record.class.before_netsuite_push, netsuite_record)
23
26
 
24
- if !local_record.new_netsuite_record?
25
- push_update(local_record, netsuite_record, opts)
26
- else
27
+ if opts[:push_method] == :upsert || local_record.new_netsuite_record?
27
28
  push_add(local_record, netsuite_record, opts)
28
- end
29
-
30
- # :aggressive is for custom fields which are based on input – need pull updated values after
31
- # the push to netsuite to retrieve the calculated values
32
-
33
- if local_record.netsuite_sync == :aggressive
34
- local_record.netsuite_pull
29
+ else
30
+ push_update(local_record, netsuite_record, opts)
35
31
  end
36
32
 
37
33
  local_record.netsuite_execute_callbacks(local_record.class.after_netsuite_push, netsuite_record)
@@ -40,12 +36,19 @@ module NetSuiteRails
40
36
  end
41
37
 
42
38
  def push_add(local_record, netsuite_record, opts = {})
43
- if netsuite_record.add
44
- # update_column to avoid triggering another save
45
- local_record.update_column(:netsuite_id, netsuite_record.internal_id)
39
+ # push_method is either :add or :upsert
40
+ if netsuite_record.send(opts[:push_method] || :add)
41
+ Rails.logger.info "NetSuite: action=#{opts[:push_method]}, local_record=#{local_record.class}[#{local_record.id}]" +
42
+ "netsuite_record_type=#{netsuite_record.class}, netsuite_record_id=#{netsuite_record.internal_id}"
43
+
44
+ if is_active_record_model?(local_record)
45
+ # update_column to avoid triggering another save
46
+ local_record.update_column(:netsuite_id, netsuite_record.internal_id)
47
+ else
48
+ netsuite_record.internal_id
49
+ end
46
50
  else
47
- # TODO use NS error class
48
- raise "NetSuite: error creating record #{netsuite_record.errors}"
51
+ raise "NetSuite: error. action=#{opts[:push_method]}, netsuite_record_type=#{netsuite_record.class}, errors=#{netsuite_record.errors}"
49
52
  end
50
53
  end
51
54
 
@@ -54,8 +57,17 @@ module NetSuiteRails
54
57
  # NS could have logic which could change field functionality depending on
55
58
  # input data; it's safest to limit the number of field changes pushed to NS
56
59
 
60
+ # exclude fields that map to procs: they don't indicate which netsuite field
61
+ # the local rails field maps to, so the user must specify this manually in `netsuite_manual_fields`
62
+
63
+ # TODO add option for model to mark `custom_field_list = true` if custom field mapping to a
64
+ # proc is detected. This is helpful for users mapping a local field to a custom field
65
+
57
66
  custom_field_list = local_record.netsuite_field_map[:custom_field_list] || {}
67
+ custom_field_list = custom_field_list.select { |local_field, netsuite_field| !netsuite_field.is_a?(Proc) }
68
+
58
69
  modified_fields_list = opts[:modified_fields]
70
+ modified_fields_list = modified_fields_list.select { |local_field, netsuite_field| !netsuite_field.is_a?(Proc) }
59
71
 
60
72
  update_list = {}
61
73
 
@@ -87,7 +99,10 @@ module NetSuiteRails
87
99
  update_list[:rec_type] = netsuite_record.rec_type
88
100
  end
89
101
 
90
- # TODO consider using upsert here
102
+ Rails.logger.info "NetSuite: Update #{netsuite_record.class} #{netsuite_record.internal_id}, list #{update_list.keys}"
103
+
104
+ # don't update if list is empty
105
+ return if update_list.empty?
91
106
 
92
107
  if netsuite_record.update(update_list)
93
108
  true
@@ -97,19 +112,13 @@ module NetSuiteRails
97
112
  end
98
113
 
99
114
  def build_netsuite_record(local_record, opts = {})
100
- netsuite_record = build_netsuite_record_reference(local_record)
101
-
102
- # TODO need to normalize datetime fields
115
+ netsuite_record = build_netsuite_record_reference(local_record, opts)
103
116
 
104
117
  all_field_list = opts[:modified_fields]
105
118
  custom_field_list = local_record.netsuite_field_map[:custom_field_list] || {}
106
119
  field_hints = local_record.netsuite_field_hints
107
120
 
108
- reflections = if NetSuiteRails.rails4?
109
- local_record.class.reflections
110
- else
111
- local_record.reflections
112
- end
121
+ reflections = relationship_attributes_list(local_record)
113
122
 
114
123
  all_field_list.each do |local_field, netsuite_field|
115
124
  # allow Procs as field mapping in the record definition for custom mapping
@@ -118,7 +127,7 @@ module NetSuiteRails
118
127
  next
119
128
  end
120
129
 
121
- # TODO pretty sure this will break if we are dealing with has_many
130
+ # TODO pretty sure this will break if we are dealing with has_many
122
131
 
123
132
  netsuite_field_value = if reflections.has_key?(local_field)
124
133
  if (remote_internal_id = local_record.send(local_field).try(:netsuite_id)).present?
@@ -131,7 +140,7 @@ module NetSuiteRails
131
140
  end
132
141
 
133
142
  if field_hints.has_key?(local_field) && netsuite_field_value.present?
134
- netsuite_field_value = NetSuiteRails::Transformations.transform(field_hints[local_field], netsuite_field_value)
143
+ netsuite_field_value = NetSuiteRails::Transformations.transform(field_hints[local_field], netsuite_field_value, :push)
135
144
  end
136
145
 
137
146
  # TODO should we skip setting nil values completely? What if we want to nil out fields on update?
@@ -148,10 +157,16 @@ module NetSuiteRails
148
157
  netsuite_record
149
158
  end
150
159
 
151
- def build_netsuite_record_reference(local_record)
160
+ def build_netsuite_record_reference(local_record, opts = {})
152
161
  # must set internal_id for records on new; will be set to nil if new record
153
162
 
154
- netsuite_record = local_record.netsuite_record_class.new(internal_id: local_record.netsuite_id)
163
+ init_hash = if opts[:use_external_id]
164
+ { external_id: local_record.netsuite_external_id }
165
+ else
166
+ { internal_id: local_record.netsuite_id }
167
+ end
168
+
169
+ netsuite_record = local_record.netsuite_record_class.new(init_hash)
155
170
 
156
171
  if local_record.netsuite_custom_record?
157
172
  netsuite_record.rec_type = NetSuite::Records::CustomRecord.new(internal_id: local_record.class.netsuite_custom_record_type_id)
@@ -163,7 +178,11 @@ module NetSuiteRails
163
178
  def modified_local_fields(local_record)
164
179
  synced_netsuite_fields = all_netsuite_fields(local_record)
165
180
 
166
- changed_keys = changed_attributes(local_record)
181
+ changed_keys = if is_active_record_model?(local_record)
182
+ changed_attributes(local_record)
183
+ else
184
+ local_record.changed_attributes
185
+ end
167
186
 
168
187
  # filter out unchanged keys when updating record
169
188
  unless local_record.new_netsuite_record?
@@ -186,11 +205,7 @@ module NetSuiteRails
186
205
 
187
206
  # TODO think about has_many / join table changes
188
207
 
189
- reflections = if NetSuiteRails.rails4?
190
- local_record.class.reflections
191
- else
192
- local_record.reflections
193
- end
208
+ reflections = relationship_attributes_list(local_record)
194
209
 
195
210
  association_field_key_mapping = reflections.values.reject(&:collection?).inject({}) do |h, a|
196
211
  begin
@@ -205,8 +220,16 @@ module NetSuiteRails
205
220
 
206
221
  changed_attributes_keys = local_record.changed_attributes.keys
207
222
 
208
- # TODO documentation about serialized values
209
- changed_attributes_keys += local_record.serialized_attributes.keys.map do |k|
223
+ serialized_attrs = if NetSuiteRails.rails4?
224
+ local_record.class.serialized_attributes
225
+ else
226
+ local_record.serialized_attributes
227
+ end
228
+
229
+ # changes_attributes does not track serialized attributes, although it does track the storage key
230
+ # if a serialized attribute storage key is dirty assume that all keys in the hash are dirty as well
231
+
232
+ changed_attributes_keys += serialized_attrs.keys.map do |k|
210
233
  local_record.send(k.to_sym).keys.map(&:to_s)
211
234
  end.flatten
212
235
 
@@ -216,9 +239,24 @@ module NetSuiteRails
216
239
  end
217
240
  end
218
241
 
242
+ def relationship_attributes_list(local_record)
243
+ if is_active_record_model?(local_record)
244
+ if NetSuiteRails.rails4?
245
+ local_record.class.reflections
246
+ else
247
+ local_record.reflections
248
+ end
249
+ else
250
+ local_record.respond_to?(:reflections) ? local_record.reflections : {}
251
+ end
252
+ end
253
+
254
+ def is_active_record_model?(local_record)
255
+ local_record.class.ancestors.include?(ActiveRecord::Base)
256
+ end
219
257
 
220
258
  end
221
259
  end
222
260
 
223
261
  end
224
- end
262
+ end
@@ -84,7 +84,7 @@ module NetSuiteRails
84
84
  self.netsuite_record_class == NetSuite::Records::CustomRecord
85
85
  end
86
86
 
87
- # :read, :aggressive (push & update on save), :write_only, :read_write
87
+ # :read, :write_only, :read_write
88
88
  def netsuite_sync(flag = nil, opts = {})
89
89
  if !flag.nil?
90
90
  self.netsuite_sync_options = opts
@@ -96,7 +96,11 @@ module NetSuiteRails
96
96
  end
97
97
 
98
98
  module InstanceMethods
99
- attr_accessor :netsuite_manual_fields
99
+ attr_writer :netsuite_manual_fields
100
+
101
+ def netsuite_manual_fields
102
+ @netsuite_manual_fields ||= []
103
+ end
100
104
 
101
105
  # these methods are here for easy model override
102
106
 
@@ -130,12 +134,23 @@ module NetSuiteRails
130
134
  @netsuite_pulled ||= false
131
135
  end
132
136
 
137
+ def netsuite_async_jobs?
138
+ self.netsuite_sync_options[:sync_mode] == :async || (self.netsuite_sync_options[:sync_mode].blank? && NetSuiteRails::Configuration.netsuite_sync_mode == :async)
139
+ end
140
+
141
+ # TODO need to support the opts hash
133
142
  def netsuite_pull(opts = {})
134
- # TODO need to support the opts hash
135
143
  netsuite_extract_from_record(netsuite_pull_record)
144
+
145
+ if self.netsuite_async_jobs?
146
+ # without callbacks?
147
+ self.save
148
+ end
136
149
  end
137
150
 
138
151
  def netsuite_pull_record
152
+ # TODO support use_external_id / netsuite_external_id
153
+
139
154
  if netsuite_custom_record?
140
155
  NetSuite::Records::CustomRecord.get(
141
156
  internal_id: self.netsuite_id,
@@ -152,6 +167,8 @@ module NetSuiteRails
152
167
 
153
168
  # TODO move this login into separate service object
154
169
  def netsuite_extract_from_record(netsuite_record)
170
+ Rails.logger.info "NetSuite: Pull #{netsuite_record.class} #{netsuite_record.internal_id}"
171
+
155
172
  @netsuite_pulling = true
156
173
 
157
174
  field_hints = self.netsuite_field_hints
@@ -161,10 +178,11 @@ module NetSuiteRails
161
178
  all_field_list = self.netsuite_field_map.except(:custom_field_list) || {}
162
179
  all_field_list.merge!(custom_field_list)
163
180
 
164
- # self.netsuite_normalize_datetimes(:pull)
181
+ # TODO should have a helper module for common push/pull methods
182
+ reflection_attributes = NetSuiteRails::RecordSync::PushManager.relationship_attributes_list(self)
165
183
 
166
184
  # handle non-collection associations
167
- association_keys = self.reflections.values.reject(&:collection?).map(&:name)
185
+ association_keys = reflection_attributes.values.reject(&:collection?).map(&:name)
168
186
 
169
187
  all_field_list.each do |local_field, netsuite_field|
170
188
  is_custom_field = custom_field_list.keys.include?(local_field)
@@ -186,7 +204,10 @@ module NetSuiteRails
186
204
  end
187
205
 
188
206
  if association_keys.include?(local_field)
189
- field_value = self.reflections[local_field].klass.where(netsuite_id: field_value.internal_id).first_or_initialize
207
+ field_value = reflection_attributes[local_field].
208
+ klass.
209
+ where(netsuite_id: field_value.internal_id).
210
+ first_or_initialize
190
211
  elsif is_custom_field
191
212
  field_value = NetSuiteRails::RecordSync::PullManager.extract_custom_field_value(field_value)
192
213
  else
@@ -195,12 +216,8 @@ module NetSuiteRails
195
216
 
196
217
  # TODO should we just check for nil? vs present?
197
218
 
198
- # TODO should be moved to Transformations with a direction flag
199
219
  if field_hints.has_key?(local_field) && field_value.present?
200
- case field_hints[local_field]
201
- when :datetime
202
- field_value = field_value.change(offset: "00:00") - (Time.zone.utc_offset / 3600).hours + (8 + NetSuiteRails::Configuration.netsuite_instance_time_zone_offset).hours
203
- end
220
+ field_value = NetSuiteRails::Transformations.transform(field_hints[local_field], field_value, :pull)
204
221
  end
205
222
 
206
223
  self.send(:"#{local_field}=", field_value)
@@ -0,0 +1,98 @@
1
+ module NetSuiteRails
2
+ module Routines
3
+ module CompanyContactMatch
4
+ extend self
5
+
6
+ def match(company_customer, contact_data)
7
+ # TODO set page size to a large number for this search
8
+ search = NetSuite::Records::Contact.search({
9
+ customerJoin: [
10
+ {
11
+ field: 'internalId',
12
+ operator: 'anyOf',
13
+ value: [
14
+ NetSuite::Records::Customer.new(internal_id: company_customer.internal_id)
15
+ ]
16
+ }
17
+ ]
18
+ })
19
+
20
+ match_data = {
21
+ email: contact_data[:email].dup,
22
+ first_name: contact_data[:first_name].dup,
23
+ last_name: contact_data[:last_name].dup
24
+ }
25
+
26
+ match_data.
27
+ values.
28
+ each(&:strip!).
29
+ each(&:downcase!)
30
+
31
+ # TODO search error checking
32
+
33
+ # try name match first; NS will throw an error if a contact is created or updated if the name already exists
34
+ search.results.each do |contact|
35
+ contact_first_name = contact.first_name.downcase.strip rescue ''
36
+ contact_last_name = contact.last_name.downcase.strip rescue ''
37
+
38
+ # if no email match & name data is present try fuzzy matching
39
+ if match_data[:first_name] && match_data[:last_name] && !contact_first_name.empty? && !contact_last_name.empty?
40
+
41
+ # TODO consider `self.fuzzy_name_matches?(contact_first_name, contact_last_name, match_data[:first_name], match_data[:last_name])`
42
+ if contact_first_name == match_data[:first_name] && contact_last_name == match_data[:last_name]
43
+ return contact
44
+ end
45
+ end
46
+ end
47
+
48
+ # try email match second
49
+ # search.results.each do |contact|
50
+ # contact_first_name = contact.first_name.downcase.strip rescue ''
51
+ # contact_last_name = contact.last_name.downcase.strip rescue ''
52
+
53
+ # # match on email
54
+ # if match_data[:email] && contact.email && contact.email.downcase.strip == match_data[:email]
55
+ # if match_data[:first_name] != contact_first_name || match_data[:last_name] != contact_last_name
56
+ # # first name and/or last name did not match the input, update contact information
57
+
58
+ # result = contact.update(
59
+ # # use the first & last name from the payload; the match_data versions have been transformed
60
+ # first_name: order_payload[:shipping_address][:firstname],
61
+ # last_name: order_payload[:shipping_address][:lastname]
62
+ # )
63
+
64
+ # unless result
65
+ # raise 'error updating name on contact placing order'
66
+ # end
67
+ # end
68
+
69
+ # return contact
70
+ # end
71
+ # end
72
+
73
+ nil
74
+ end
75
+
76
+ # TODO consider optionally using fuzzy name matches in the future
77
+ # def fuzzy_name_matches?(first_name_1, last_name_1, first_name_2, last_name_2)
78
+ # @fuzzy_comparison ||= FuzzyStringMatch::JaroWinkler.create
79
+
80
+ # # Jarow-Winkler returns 1 for exact match
81
+ # if @fuzzy_comparison.getDistance(last_name_1, last_name_2) > 0.90
82
+ # # check for a match on the first name
83
+ # if @fuzzy_comparison.getDistance(first_name_1, first_name_2) > 0.90
84
+ # return true
85
+ # end
86
+
87
+ # # if fuzzy on first name failed; try to see if there are any nickname equivilents
88
+ # if Monikers.equivalents?(first_name_1, first_name_2)
89
+ # return true
90
+ # end
91
+ # end
92
+
93
+ # false
94
+ # end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,27 @@
1
+ module NetSuiteRails
2
+ module Spec
3
+ module TestDisabler
4
+
5
+ def disable_netsuite_communication
6
+ before do
7
+ @_push_disabled = NetSuiteRails::Configuration.netsuite_push_disabled
8
+ @_pull_disabled = NetSuiteRails::Configuration.netsuite_pull_disabled
9
+
10
+ NetSuiteRails::Configuration.netsuite_push_disabled true
11
+ NetSuiteRails::Configuration.netsuite_pull_disabled true
12
+ end
13
+
14
+ after do
15
+ NetSuiteRails::Configuration.netsuite_push_disabled @_push_disabled
16
+ NetSuiteRails::Configuration.netsuite_pull_disabled @_pull_disabled
17
+ end
18
+ end
19
+
20
+
21
+ end
22
+ end
23
+ end
24
+
25
+ RSpec.configure do |config|
26
+ config.extend NetSuiteRails::Spec::TestDisabler
27
+ end
@@ -0,0 +1,93 @@
1
+ module NetSuiteRails
2
+ module Spec
3
+ module QueryHelpers
4
+
5
+ def self.included(base)
6
+ base.before { netsuite_timestamp(DateTime.now) }
7
+ end
8
+
9
+ def netsuite_timestamp(stamp = nil)
10
+ if stamp.nil?
11
+ @netsuite_timestamp ||= (Time.now - (60 * 2)).to_datetime
12
+ else
13
+ @netsuite_timestamp = stamp
14
+ end
15
+ end
16
+
17
+ def get_last_netsuite_object(record)
18
+ # TODO support passing custom record ref
19
+
20
+ if record.is_a?(Class)
21
+ record_class = record
22
+ is_custom_record = false
23
+ else
24
+ record_class = record.netsuite_record_class
25
+ is_custom_record = record.netsuite_custom_record?
26
+ end
27
+
28
+ search = record_class.search({
29
+ criteria: {
30
+ basic:
31
+ (
32
+ if is_custom_record
33
+ [
34
+ {
35
+ field: 'recType',
36
+ operator: 'is',
37
+ value: NetSuite::Records::CustomRecordRef.new(internal_id: record.class.netsuite_custom_record_type_id)
38
+ },
39
+ {
40
+ field: 'lastModified',
41
+ operator: 'after',
42
+ value: netsuite_timestamp
43
+ }
44
+ ]
45
+ else
46
+ [
47
+ {
48
+ field: 'lastModifiedDate',
49
+ operator: 'after',
50
+ value: netsuite_timestamp
51
+ }
52
+ ]
53
+ end +
54
+
55
+ if [ NetSuite::Records::SalesOrder, NetSuite::Records::ItemFulfillment, NetSuite::Records::Invoice ].include?(record_class)
56
+ [
57
+ {
58
+ field: 'type',
59
+ operator: 'anyOf',
60
+ value: [ '_' + record_class.name.demodulize.lower_camelcase ]
61
+ }
62
+ ]
63
+ else
64
+ []
65
+ end
66
+ )
67
+ }
68
+ })
69
+
70
+ return nil if search.results.blank?
71
+
72
+ if is_custom_record
73
+ NetSuite::Records::CustomRecord.get(
74
+ internal_id: search.results.first.internal_id.to_i,
75
+ type_id: record.class.netsuite_custom_record_type_id
76
+ )
77
+ else
78
+ record_class.get(search.results.first.internal_id.to_i)
79
+ end
80
+ end
81
+
82
+ # convenience method for inspecting objects in a live IRB session
83
+ def netsuite_url(object)
84
+ `open "#{NetSuiteRails::UrlHelper.netsuite_url(object)}"`
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+
91
+ RSpec.configure do |config|
92
+ config.include NetSuiteRails::Spec::QueryHelpers
93
+ end
@@ -1,86 +1,9 @@
1
1
  require 'netsuite'
2
2
  require 'savon/mock/spec_helper'
3
3
 
4
- module NetSuiteRails::TestHelpers
5
-
6
- def self.included(base)
7
- base.before { netsuite_timestamp(DateTime.now) }
8
- end
9
-
10
- def netsuite_timestamp(stamp = nil)
11
- if stamp.nil?
12
- @netsuite_timestamp ||= (Time.now - (60 * 2)).to_datetime
13
- else
14
- @netsuite_timestamp = stamp
15
- end
16
- end
17
-
18
- def get_last_netsuite_object(record)
19
- # TODO support passing custom record ref
20
-
21
- if record.is_a?(Class)
22
- record_class = record
23
- is_custom_record = false
24
- else
25
- record_class = record.netsuite_record_class
26
- is_custom_record = record.netsuite_custom_record?
27
- end
28
-
29
- search = record_class.search({
30
- criteria: {
31
- basic:
32
- (
33
- if is_custom_record
34
- [
35
- {
36
- field: 'recType',
37
- operator: 'is',
38
- value: NetSuite::Records::CustomRecordRef.new(internal_id: record.class.netsuite_custom_record_type_id)
39
- },
40
- {
41
- field: 'lastModified',
42
- operator: 'after',
43
- value: netsuite_timestamp
44
- }
45
- ]
46
- else
47
- [
48
- {
49
- field: 'lastModifiedDate',
50
- operator: 'after',
51
- value: netsuite_timestamp
52
- }
53
- ]
54
- end +
55
-
56
- if record_class == NetSuite::Records::SalesOrder
57
- [
58
- {
59
- field: 'type',
60
- operator: 'anyOf',
61
- value: [ '_salesOrder' ]
62
- }
63
- ]
64
- else
65
- []
66
- end
67
- )
68
- }
69
- })
70
-
71
- if is_custom_record
72
- NetSuite::Records::CustomRecord.get(
73
- internal_id: search.results.first.internal_id.to_i,
74
- type_id: record.class.netsuite_custom_record_type_id
75
- )
76
- else
77
- record_class.get(search.results.first.internal_id.to_i)
78
- end
79
- end
80
-
81
- end
4
+ require 'netsuite_rails/spec/query_helpers'
5
+ require 'netsuite_rails/spec/disabler'
82
6
 
83
7
  RSpec.configure do |config|
84
- config.include NetSuiteRails::TestHelpers, type: :feature
85
8
  config.include Savon::SpecHelper
86
9
  end