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.
- checksums.yaml +15 -0
- data/Gemfile +4 -2
- data/README.md +186 -10
- data/circle.yml +3 -0
- data/lib/netsuite_rails/configuration.rb +7 -5
- data/lib/netsuite_rails/netsuite_rails.rb +24 -4
- data/lib/netsuite_rails/poll_trigger.rb +10 -9
- data/lib/netsuite_rails/record_sync/poll_manager.rb +23 -11
- data/lib/netsuite_rails/record_sync/pull_manager.rb +2 -0
- data/lib/netsuite_rails/record_sync/push_manager.rb +76 -38
- data/lib/netsuite_rails/record_sync.rb +28 -11
- data/lib/netsuite_rails/routines/company_contact_match.rb +98 -0
- data/lib/netsuite_rails/spec/disabler.rb +27 -0
- data/lib/netsuite_rails/spec/query_helpers.rb +93 -0
- data/lib/netsuite_rails/spec/spec_helper.rb +2 -79
- data/lib/netsuite_rails/sync_trigger.rb +40 -17
- data/lib/netsuite_rails/tasks/netsuite.rb +33 -4
- data/lib/netsuite_rails/transformations.rb +59 -19
- data/lib/netsuite_rails/url_helper.rb +45 -12
- data/netsuite_rails.gemspec +2 -2
- data/spec/models/configuration_spec.rb +11 -0
- data/spec/models/poll_manager_spec.rb +11 -2
- data/spec/models/poll_trigger_spec.rb +31 -11
- data/spec/models/record_sync/push_manager_spec.rb +51 -0
- data/spec/models/record_sync_spec.rb +16 -0
- data/spec/models/spec_helper_spec.rb +1 -2
- data/spec/models/transformations_spec.rb +62 -0
- data/spec/models/url_helper_spec.rb +20 -9
- data/spec/spec_helper.rb +19 -0
- data/spec/support/example_models.rb +33 -1
- metadata +19 -25
- data/.travis.yml +0 -3
- data/lib/netsuite_rails/netsuite_configure.rb +0 -14
- 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
|
-
|
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
|
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
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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 =
|
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
|
-
|
209
|
-
|
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, :
|
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
|
-
|
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
|
-
#
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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
|