netsuite_rails 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|