netsuite_rails 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/NetSweet/netsuite_rails.svg?branch=master)](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
|