netsuite_rails 0.1.0 → 0.2.0
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.
- data/.gitignore +3 -0
- data/.travis.yml +3 -0
- data/Gemfile +17 -7
- data/README.md +9 -4
- data/lib/netsuite_rails/configuration.rb +3 -1
- data/lib/netsuite_rails/list_sync/poll_manager.rb +30 -0
- data/lib/netsuite_rails/list_sync.rb +2 -2
- data/lib/netsuite_rails/netsuite_rails.rb +13 -3
- data/lib/netsuite_rails/poll_timestamp.rb +5 -0
- data/lib/netsuite_rails/{poll_manager.rb → poll_trigger.rb} +18 -3
- data/lib/netsuite_rails/record_sync/poll_manager.rb +186 -0
- data/lib/netsuite_rails/record_sync/pull_manager.rb +10 -137
- data/lib/netsuite_rails/record_sync/push_manager.rb +59 -21
- data/lib/netsuite_rails/record_sync.rb +142 -140
- data/lib/netsuite_rails/spec/spec_helper.rb +34 -6
- data/lib/netsuite_rails/sync_trigger.rb +80 -75
- data/lib/netsuite_rails/tasks/netsuite.rb +26 -30
- data/lib/netsuite_rails/url_helper.rb +12 -3
- data/netsuite_rails.gemspec +3 -3
- data/spec/models/poll_manager_spec.rb +45 -0
- data/spec/models/poll_trigger_spec.rb +26 -0
- data/spec/models/record_sync/push_manager_spec.rb +0 -0
- data/spec/models/spec_helper_spec.rb +29 -0
- data/spec/models/sync_trigger_spec.rb +62 -0
- data/spec/models/url_helper_spec.rb +23 -0
- data/spec/spec_helper.rb +17 -1
- data/spec/support/config/database.yml +11 -0
- data/spec/support/dynamic_models/class_builder.rb +48 -0
- data/spec/support/dynamic_models/model_builder.rb +83 -0
- data/spec/support/example_models.rb +31 -0
- data/spec/support/netsuite_rails.rb +1 -0
- data/spec/support/test_application.rb +55 -0
- metadata +36 -10
- data/lib/netsuite_rails/list_sync/pull_manager.rb +0 -33
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -2,10 +2,20 @@ source 'https://rubygems.org'
|
|
2
2
|
gemspec
|
3
3
|
|
4
4
|
# TODO would like to support easy NetSuite VCR recording in the future
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
|
11
|
-
|
5
|
+
group :test do
|
6
|
+
# gem 'vcr'
|
7
|
+
# gem 'rspec', '~> 2.14'
|
8
|
+
# gem 'rack-test'
|
9
|
+
# gem 'webmock'
|
10
|
+
|
11
|
+
gem 'faker'
|
12
|
+
gem 'shoulda-matchers'
|
13
|
+
gem 'rails', '3.2.16'
|
14
|
+
gem 'sqlite3', :platform => :ruby
|
15
|
+
|
16
|
+
gem 'rspec-rails', '~> 3.1'
|
17
|
+
gem 'pry-nav'
|
18
|
+
|
19
|
+
gem 'rerun'
|
20
|
+
gem 'rb-fsevent'
|
21
|
+
end
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
[](https://travis-ci.org/NetSweet/netsuite_rails)
|
2
|
+
|
1
3
|
# NetSuite Rails
|
2
4
|
|
3
5
|
**Note:** Documentation is horrible... look at the code for details.
|
4
6
|
|
5
|
-
Build custom
|
7
|
+
Build custom Ruby on Rails applications that sync to NetSuite.
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -19,9 +21,8 @@ rails g netsuite_rails:install
|
|
19
21
|
### Date & Time
|
20
22
|
|
21
23
|
```ruby
|
22
|
-
|
23
|
-
|
24
|
-
end
|
24
|
+
# set your timezone offset
|
25
|
+
NetSuiteRails::Configuration.netsuite_instance_time_zone_offset(-6)
|
25
26
|
```
|
26
27
|
|
27
28
|
## Usage
|
@@ -32,6 +33,10 @@ When using a proc in a NS mapping, you are responsible for setting local and rem
|
|
32
33
|
|
33
34
|
for pushing tasks to DJ https://github.com/collectiveidea/delayed_job/wiki/Rake-Task-as-a-Delayed-Job
|
34
35
|
|
36
|
+
`:if` for controlling when syncing occurs
|
37
|
+
|
38
|
+
TODO hooks for before/after push/pull
|
39
|
+
|
35
40
|
### Syncing
|
36
41
|
|
37
42
|
```bash
|
@@ -2,6 +2,8 @@ module NetSuiteRails
|
|
2
2
|
module Configuration
|
3
3
|
extend self
|
4
4
|
|
5
|
+
NETSUITE_MAX_PAGE_SIZE = 1000
|
6
|
+
|
5
7
|
def reset!
|
6
8
|
attributes.clear
|
7
9
|
end
|
@@ -44,7 +46,7 @@ module NetSuiteRails
|
|
44
46
|
|
45
47
|
def polling_page_size(size = nil)
|
46
48
|
if size.nil?
|
47
|
-
attributes[:size] ||=
|
49
|
+
attributes[:size] ||= NETSUITE_MAX_PAGE_SIZE
|
48
50
|
else
|
49
51
|
attributes[:size] = size
|
50
52
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module NetSuiteRails
|
2
|
+
module ListSync
|
3
|
+
module PollManager
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def poll(klass, opts = {})
|
7
|
+
custom_list = NetSuite::Records::CustomList.get(klass.netsuite_list_id)
|
8
|
+
|
9
|
+
process_results(klass, opts, custom_list.custom_value_list.custom_value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def process_results(klass, opts, list)
|
13
|
+
list.each do |custom_value|
|
14
|
+
local_record = klass.where(netsuite_id: custom_value.attributes[:value_id]).first_or_initialize
|
15
|
+
|
16
|
+
if local_record.respond_to?(:value=)
|
17
|
+
local_record.value = custom_value.attributes[:value]
|
18
|
+
end
|
19
|
+
|
20
|
+
if local_record.respond_to?(:inactive=)
|
21
|
+
local_record.inactive = custom_value.attributes[:is_inactive]
|
22
|
+
end
|
23
|
+
|
24
|
+
local_record.save!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -4,7 +4,7 @@ module NetSuiteRails
|
|
4
4
|
def self.included(klass)
|
5
5
|
klass.send(:extend, ClassMethods)
|
6
6
|
|
7
|
-
|
7
|
+
PollTrigger.attach(klass)
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
@@ -17,7 +17,7 @@ module NetSuiteRails
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def netsuite_poll(opts = {})
|
20
|
-
NetSuiteRails::ListSync::
|
20
|
+
NetSuiteRails::ListSync::PollManager.poll(self, opts)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -1,18 +1,28 @@
|
|
1
|
+
require 'netsuite'
|
2
|
+
|
1
3
|
require 'netsuite_rails/configuration'
|
2
4
|
require 'netsuite_rails/poll_timestamp'
|
3
5
|
require 'netsuite_rails/transformations'
|
4
|
-
require 'netsuite_rails/
|
6
|
+
require 'netsuite_rails/url_helper'
|
7
|
+
|
8
|
+
require 'netsuite_rails/poll_trigger'
|
5
9
|
require 'netsuite_rails/sync_trigger'
|
6
10
|
require 'netsuite_rails/sub_list_sync'
|
11
|
+
|
7
12
|
require 'netsuite_rails/record_sync'
|
13
|
+
require 'netsuite_rails/record_sync/poll_manager'
|
8
14
|
require 'netsuite_rails/record_sync/pull_manager'
|
9
15
|
require 'netsuite_rails/record_sync/push_manager'
|
16
|
+
|
10
17
|
require 'netsuite_rails/list_sync'
|
11
|
-
require 'netsuite_rails/list_sync/
|
12
|
-
require 'netsuite_rails/url_helper'
|
18
|
+
require 'netsuite_rails/list_sync/poll_manager'
|
13
19
|
|
14
20
|
module NetSuiteRails
|
15
21
|
|
22
|
+
def self.rails4?
|
23
|
+
Rails::VERSION::MAJOR >= 4
|
24
|
+
end
|
25
|
+
|
16
26
|
class Railtie < ::Rails::Railtie
|
17
27
|
rake_tasks do
|
18
28
|
load 'netsuite_rails/tasks/netsuite.rb'
|
@@ -4,8 +4,13 @@ module NetSuiteRails
|
|
4
4
|
|
5
5
|
validates :key, presence: true, uniqueness: true
|
6
6
|
|
7
|
+
def self.for_class(klass)
|
8
|
+
self.where(key: "netsuite_poll_#{klass.to_s.downcase}timestamp").first_or_initialize
|
9
|
+
end
|
10
|
+
|
7
11
|
def self.table_name_prefix
|
8
12
|
'netsuite_'
|
9
13
|
end
|
14
|
+
|
10
15
|
end
|
11
16
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module NetSuiteRails
|
2
|
-
class
|
2
|
+
class PollTrigger
|
3
3
|
|
4
4
|
class << self
|
5
5
|
|
@@ -33,7 +33,7 @@ module NetSuiteRails
|
|
33
33
|
|
34
34
|
Rails.logger.info "NetSuite: Syncing #{klass.to_s}"
|
35
35
|
|
36
|
-
preference = PollTimestamp.
|
36
|
+
preference = PollTimestamp.for_class(klass)
|
37
37
|
|
38
38
|
# check if we've never synced before
|
39
39
|
if preference.new_record?
|
@@ -43,7 +43,7 @@ module NetSuiteRails
|
|
43
43
|
last_poll_date = preference.value
|
44
44
|
last_poll_date = DateTime.parse(last_poll_date) unless last_poll_date.is_a?(DateTime)
|
45
45
|
|
46
|
-
if DateTime.now - last_poll_date > sync_frequency
|
46
|
+
if DateTime.now.to_i - last_poll_date.to_i > sync_frequency
|
47
47
|
Rails.logger.info "NetSuite: Syncing #{klass} modified since #{last_poll_date}"
|
48
48
|
klass.netsuite_poll({ last_poll: last_poll_date }.merge(opts))
|
49
49
|
else
|
@@ -55,6 +55,21 @@ module NetSuiteRails
|
|
55
55
|
preference.save!
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
def update_local_records(opts = {})
|
60
|
+
record_models = opts[:record_models] || @record_models
|
61
|
+
list_models = opts[:list_models] || @list_models
|
62
|
+
|
63
|
+
# TODO only records are supported right now
|
64
|
+
# list_models.each do |klass|
|
65
|
+
# Rails.logger.info "NetSuite: Syncing #{klass}"
|
66
|
+
# klass.netsuite_poll
|
67
|
+
# end
|
68
|
+
|
69
|
+
record_models.each do |klass|
|
70
|
+
NetSuiteRails::RecordSync::PollManager.update_local_records(klass, opts)
|
71
|
+
end
|
72
|
+
end
|
58
73
|
|
59
74
|
end
|
60
75
|
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module NetSuiteRails
|
2
|
+
module RecordSync
|
3
|
+
module PollManager
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def update_local_records(klass, opts = {})
|
7
|
+
klass.select([:netsuite_id, :id]).find_in_batches(batch_size: NetSuiteRails::Configuration.polling_page_size) do |local_batch|
|
8
|
+
netsuite_batch = if klass.netsuite_custom_record?
|
9
|
+
NetSuite::Records::CustomRecord.get_list(
|
10
|
+
list: local_batch.map(&:netsuite_id),
|
11
|
+
type_id: klass.netsuite_custom_record_type_id,
|
12
|
+
allow_incomplete: true
|
13
|
+
)
|
14
|
+
else
|
15
|
+
klass.netsuite_record_class.get_list(
|
16
|
+
list: local_batch.map(&:netsuite_id),
|
17
|
+
allow_incomplete: true
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
unless netsuite_batch
|
22
|
+
binding.pry
|
23
|
+
end
|
24
|
+
|
25
|
+
netsuite_batch.each do |netsuite_record|
|
26
|
+
self.process_search_result_item(klass, opts, netsuite_record)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def poll(klass, opts = {})
|
32
|
+
opts = {
|
33
|
+
import_all: false,
|
34
|
+
page_size: NetSuiteRails::Configuration.polling_page_size,
|
35
|
+
}.merge(opts)
|
36
|
+
|
37
|
+
opts[:netsuite_record_class] ||= klass.netsuite_record_class
|
38
|
+
opts[:saved_search_id] ||= klass.netsuite_sync_options[:saved_search_id]
|
39
|
+
|
40
|
+
search = opts[:netsuite_record_class].search(
|
41
|
+
poll_criteria(klass, opts).merge({
|
42
|
+
preferences: {
|
43
|
+
body_fields_only: false,
|
44
|
+
page_size: opts[:page_size]
|
45
|
+
}
|
46
|
+
})
|
47
|
+
)
|
48
|
+
|
49
|
+
# TODO more robust error reporting
|
50
|
+
unless search
|
51
|
+
raise 'error running netsuite sync'
|
52
|
+
end
|
53
|
+
|
54
|
+
process_search_results(klass, opts, search)
|
55
|
+
end
|
56
|
+
|
57
|
+
def poll_criteria(klass, opts)
|
58
|
+
search_criteria = {
|
59
|
+
criteria: {
|
60
|
+
basic: poll_basic_criteria(klass, opts)
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
if opts[:saved_search_id]
|
65
|
+
search_criteria[:criteria][:saved] = opts[:saved_search_id]
|
66
|
+
end
|
67
|
+
|
68
|
+
if needs_get_list?(opts)
|
69
|
+
search_criteria[:columns] = {
|
70
|
+
'listRel:basic' => [
|
71
|
+
'platformCommon:internalId/' => {},
|
72
|
+
],
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
search_criteria
|
77
|
+
end
|
78
|
+
|
79
|
+
def poll_basic_criteria(klass, opts)
|
80
|
+
opts = {
|
81
|
+
criteria: [],
|
82
|
+
# last_poll: DateTime
|
83
|
+
}.merge(opts)
|
84
|
+
|
85
|
+
# allow custom criteria to be passed directly to the sync call
|
86
|
+
criteria = opts[:criteria] || []
|
87
|
+
|
88
|
+
# allow custom criteria from the model level
|
89
|
+
criteria += klass.netsuite_sync_options[:criteria] || []
|
90
|
+
|
91
|
+
if opts[:netsuite_record_class] == NetSuite::Records::CustomRecord
|
92
|
+
opts[:netsuite_custom_record_type_id] ||= klass.netsuite_custom_record_type_id
|
93
|
+
|
94
|
+
criteria << {
|
95
|
+
field: 'recType',
|
96
|
+
operator: 'is',
|
97
|
+
value: NetSuite::Records::CustomRecordRef.new(internal_id: opts[:netsuite_custom_record_type_id])
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
unless opts[:import_all]
|
102
|
+
criteria << {
|
103
|
+
# CustomRecordSearchBasic uses lastModified instead of the standard lastModifiedDate
|
104
|
+
field: (klass.netsuite_custom_record?) ? 'lastModified' : 'lastModifiedDate',
|
105
|
+
operator: 'after',
|
106
|
+
value: opts[:last_poll]
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
criteria
|
111
|
+
end
|
112
|
+
|
113
|
+
def process_search_results(klass, opts, search)
|
114
|
+
opts = {
|
115
|
+
skip_existing: false,
|
116
|
+
full_record_data: -1,
|
117
|
+
}.merge(opts)
|
118
|
+
|
119
|
+
Rails.logger.info "NetSuite: Processing #{search.total_records} over #{search.total_pages} pages"
|
120
|
+
|
121
|
+
# TODO need to improve the conditional here to match the get_list call conditional belo
|
122
|
+
if opts[:import_all] && opts[:skip_existing]
|
123
|
+
synced_netsuite_list = klass.pluck(:netsuite_id)
|
124
|
+
end
|
125
|
+
|
126
|
+
search.results_in_batches do |batch|
|
127
|
+
# a saved search is processed as a advanced search; advanced search often does not allow you to retrieve
|
128
|
+
# all of the fields (ex: addressbooklist on customer) that a normal search does
|
129
|
+
# the only way to get those fields is to pull down the full record again using getAll
|
130
|
+
|
131
|
+
if needs_get_list?(opts)
|
132
|
+
filtered_netsuite_id_list = batch.map(&:internal_id).map(&:to_i)
|
133
|
+
|
134
|
+
if opts[:skip_existing] == true
|
135
|
+
filtered_netsuite_id_list.reject! { |netsuite_id| synced_netsuite_list.include?(netsuite_id) }
|
136
|
+
end
|
137
|
+
|
138
|
+
if filtered_netsuite_id_list.present?
|
139
|
+
opts[:netsuite_record_class].get_list(list: filtered_netsuite_id_list)
|
140
|
+
else
|
141
|
+
[]
|
142
|
+
end
|
143
|
+
else
|
144
|
+
batch
|
145
|
+
end.each do |netsuite_record|
|
146
|
+
self.process_search_result_item(klass, opts, netsuite_record)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def process_search_result_item(klass, opts, netsuite_record)
|
152
|
+
local_record = klass.where(netsuite_id: netsuite_record.internal_id).first_or_initialize
|
153
|
+
|
154
|
+
# when importing lots of records during an import_all skipping imported records is important
|
155
|
+
return if opts[:skip_existing] == true && !local_record.new_record?
|
156
|
+
|
157
|
+
local_record.netsuite_extract_from_record(netsuite_record)
|
158
|
+
|
159
|
+
# TODO optionally throw fatal errors; we want to skip fatal errors on intial import
|
160
|
+
|
161
|
+
unless local_record.save
|
162
|
+
Rails.logger.error "NetSuite: Error pulling record #{klass} NS ID #{netsuite_record.internal_id} #{local_record.errors.full_messages}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def needs_get_list?(opts)
|
167
|
+
(opts[:saved_search_id].present? && opts[:full_record_data] != false) || opts[:full_record_data] == true
|
168
|
+
end
|
169
|
+
|
170
|
+
# TODO this should remain in the pull manager
|
171
|
+
|
172
|
+
def extract_custom_field_value(custom_field_value)
|
173
|
+
if custom_field_value.present? && custom_field_value.is_a?(Hash) && custom_field_value.has_key?(:name)
|
174
|
+
custom_field_value = custom_field_value[:name]
|
175
|
+
end
|
176
|
+
|
177
|
+
if custom_field_value.present? && custom_field_value.is_a?(NetSuite::Records::CustomRecordRef)
|
178
|
+
custom_field_value = custom_field_value.attributes[:name]
|
179
|
+
end
|
180
|
+
|
181
|
+
custom_field_value
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -1,147 +1,20 @@
|
|
1
1
|
module NetSuiteRails
|
2
2
|
module RecordSync
|
3
|
+
module PullManager
|
4
|
+
extend self
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def poll(klass, opts = {})
|
8
|
-
opts = {
|
9
|
-
import_all: false,
|
10
|
-
page_size: NetSuiteRails::Configuration.polling_page_size,
|
11
|
-
}.merge(opts)
|
12
|
-
|
13
|
-
opts[:netsuite_record_class] ||= klass.netsuite_record_class
|
14
|
-
|
15
|
-
search = opts[:netsuite_record_class].search(
|
16
|
-
poll_criteria(klass, opts).merge({
|
17
|
-
preferences: {
|
18
|
-
body_fields_only: false,
|
19
|
-
page_size: opts[:page_size]
|
20
|
-
}
|
21
|
-
})
|
22
|
-
)
|
23
|
-
|
24
|
-
# TODO more robust error reporting
|
25
|
-
unless search
|
26
|
-
raise 'error running netsuite sync'
|
27
|
-
end
|
28
|
-
|
29
|
-
process_search_results(klass, opts, search)
|
6
|
+
def extract_custom_field_value(custom_field_value)
|
7
|
+
if custom_field_value.present? && custom_field_value.is_a?(Hash) && custom_field_value.has_key?(:name)
|
8
|
+
custom_field_value = custom_field_value[:name]
|
30
9
|
end
|
31
10
|
|
32
|
-
|
33
|
-
|
34
|
-
criteria: {
|
35
|
-
basic: poll_basic_criteria(klass, opts)
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
39
|
-
saved_search_id = opts[:saved_search_id] || klass.netsuite_sync_options[:saved_search_id]
|
40
|
-
|
41
|
-
if saved_search_id
|
42
|
-
search_criteria[:criteria][:saved] = saved_search_id
|
43
|
-
end
|
44
|
-
|
45
|
-
if needs_get_list?(opts)
|
46
|
-
search_criteria[:columns] = {
|
47
|
-
'listRel:basic' => [
|
48
|
-
'platformCommon:internalId/' => {},
|
49
|
-
],
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
|
-
search_criteria
|
54
|
-
end
|
55
|
-
|
56
|
-
def poll_basic_criteria(klass, opts)
|
57
|
-
opts = {
|
58
|
-
criteria: [],
|
59
|
-
# last_poll: DateTime
|
60
|
-
}.merge(opts)
|
61
|
-
|
62
|
-
# allow custom criteria to be passed directly to the sync call
|
63
|
-
criteria = opts[:criteria] || []
|
64
|
-
|
65
|
-
# allow custom criteria from the model level
|
66
|
-
criteria += klass.netsuite_sync_options[:criteria] || []
|
67
|
-
|
68
|
-
if opts[:netsuite_record_class] == NetSuite::Records::CustomRecord
|
69
|
-
opts[:netsuite_custom_record_type_id] ||= klass.netsuite_custom_record_type_id
|
70
|
-
|
71
|
-
criteria << {
|
72
|
-
field: 'recType',
|
73
|
-
operator: 'is',
|
74
|
-
value: NetSuite::Records::CustomRecordRef.new(internal_id: opts[:netsuite_custom_record_type_id])
|
75
|
-
}
|
76
|
-
end
|
77
|
-
|
78
|
-
unless opts[:import_all]
|
79
|
-
criteria << {
|
80
|
-
# CustomRecordSearchBasic uses lastModified instead of the standard lastModifiedDate
|
81
|
-
field: (klass.netsuite_custom_record?) ? 'lastModified' : 'lastModifiedDate',
|
82
|
-
operator: 'after',
|
83
|
-
value: opts[:last_poll]
|
84
|
-
}
|
85
|
-
end
|
86
|
-
|
87
|
-
criteria
|
88
|
-
end
|
89
|
-
|
90
|
-
def process_search_results(klass, opts, search)
|
91
|
-
opts = {
|
92
|
-
skip_existing: false,
|
93
|
-
full_record_data: -1,
|
94
|
-
}.merge(opts)
|
95
|
-
|
96
|
-
Rails.logger.info "NetSuite: Processing #{search.total_records} over #{search.total_pages} pages"
|
97
|
-
|
98
|
-
# TODO need to improve the conditional here to match the get_list call conditional belo
|
99
|
-
if opts[:import_all] && opts[:skip_existing]
|
100
|
-
synced_netsuite_list = klass.pluck(:netsuite_id)
|
101
|
-
end
|
102
|
-
|
103
|
-
search.results_in_batches do |batch|
|
104
|
-
# a saved search is processed as a advanced search; advanced search often does not allow you to retrieve
|
105
|
-
# all of the fields (ex: addressbooklist on customer) that a normal search does
|
106
|
-
# the only way to get those fields is to pull down the full record again using getAll
|
107
|
-
|
108
|
-
if needs_get_list?(opts)
|
109
|
-
filtered_netsuite_id_list = batch.map(&:internal_id)
|
110
|
-
|
111
|
-
if opts[:skip_existing] == true
|
112
|
-
filtered_netsuite_id_list.reject! { |netsuite_id| synced_netsuite_list.include?(netsuite_id) }
|
113
|
-
end
|
114
|
-
|
115
|
-
opts[:netsuite_record_class].get_list(list: batch.map(&:internal_id))
|
116
|
-
else
|
117
|
-
batch
|
118
|
-
end.each do |netsuite_record|
|
119
|
-
self.process_search_result_item(klass, opts, netsuite_record)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def process_search_result_item(klass, opts, netsuite_record)
|
125
|
-
local_record = klass.where(netsuite_id: netsuite_record.internal_id).first_or_initialize
|
126
|
-
|
127
|
-
# when importing lots of records during an import_all skipping imported records is important
|
128
|
-
return if opts[:skip_existing] == true && !local_record.new_record?
|
129
|
-
|
130
|
-
local_record.netsuite_extract_from_record(netsuite_record)
|
131
|
-
|
132
|
-
# TODO optionally throw fatal errors; we want to skip fatal errors on intial import
|
133
|
-
|
134
|
-
unless local_record.save
|
135
|
-
Rails.logger.error "NetSuite: Error pulling record #{klass} NS ID #{netsuite_record.internal_id} #{local_record.errors.full_messages}"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def needs_get_list?(opts)
|
140
|
-
(opts[:saved_search_id].present? && opts[:full_record_data] != false) || opts[:full_record_data] == true
|
11
|
+
if custom_field_value.present? && custom_field_value.is_a?(NetSuite::Records::CustomRecordRef)
|
12
|
+
custom_field_value = custom_field_value.attributes[:name]
|
141
13
|
end
|
142
14
|
|
15
|
+
custom_field_value
|
143
16
|
end
|
144
|
-
end
|
145
17
|
|
18
|
+
end
|
146
19
|
end
|
147
|
-
end
|
20
|
+
end
|