netsuite 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.
Files changed (45) hide show
  1. data/Gemfile +0 -2
  2. data/README.md +154 -0
  3. data/lib/netsuite.rb +7 -0
  4. data/lib/netsuite/actions/add.rb +6 -21
  5. data/lib/netsuite/actions/delete.rb +7 -10
  6. data/lib/netsuite/actions/get.rb +7 -10
  7. data/lib/netsuite/actions/get_list.rb +77 -0
  8. data/lib/netsuite/actions/get_select_value.rb +71 -0
  9. data/lib/netsuite/actions/search.rb +151 -36
  10. data/lib/netsuite/actions/search_more_with_id.rb +3 -2
  11. data/lib/netsuite/actions/update.rb +16 -16
  12. data/lib/netsuite/configuration.rb +29 -15
  13. data/lib/netsuite/namespaces/list_support.rb +11 -0
  14. data/lib/netsuite/records/account.rb +1 -0
  15. data/lib/netsuite/records/base_ref_list.rb +33 -0
  16. data/lib/netsuite/records/cash_sale.rb +7 -0
  17. data/lib/netsuite/records/custom_field.rb +2 -1
  18. data/lib/netsuite/records/custom_field_list.rb +62 -12
  19. data/lib/netsuite/records/custom_record.rb +1 -7
  20. data/lib/netsuite/records/custom_record_ref.rb +7 -0
  21. data/lib/netsuite/records/customer.rb +4 -3
  22. data/lib/netsuite/records/phone_call.rb +29 -0
  23. data/lib/netsuite/records/record_ref.rb +1 -9
  24. data/lib/netsuite/records/sales_order.rb +2 -1
  25. data/lib/netsuite/records/support_case.rb +32 -0
  26. data/lib/netsuite/records/task.rb +4 -3
  27. data/lib/netsuite/records/transaction.rb +4 -21
  28. data/lib/netsuite/support/actions.rb +4 -0
  29. data/lib/netsuite/support/records.rb +3 -0
  30. data/lib/netsuite/support/search_result.rb +53 -29
  31. data/lib/netsuite/version.rb +1 -1
  32. data/netsuite.gemspec +1 -1
  33. data/spec/netsuite/actions/add_spec.rb +9 -13
  34. data/spec/netsuite/actions/delete_spec.rb +5 -8
  35. data/spec/netsuite/actions/get_spec.rb +39 -24
  36. data/spec/netsuite/actions/search_spec.rb +149 -0
  37. data/spec/netsuite/actions/update_spec.rb +9 -13
  38. data/spec/netsuite/configuration_spec.rb +3 -9
  39. data/spec/netsuite/records/custom_field_list_spec.rb +19 -5
  40. data/spec/netsuite/records/customer_spec.rb +2 -2
  41. data/spec/netsuite/records/phone_call_spec.rb +23 -0
  42. data/spec/netsuite/records/support_case_spec.rb +157 -0
  43. data/spec/support/fixtures/search/saved_search_customer.xml +54 -0
  44. data/spec/support/fixtures/search/saved_search_joined_custom_customer.xml +87 -0
  45. metadata +21 -4
@@ -1,4 +1,3 @@
1
- # TODO: Tests
2
1
  module NetSuite
3
2
  module Actions
4
3
  class Search
@@ -12,23 +11,20 @@ module NetSuite
12
11
  private
13
12
 
14
13
  def request
15
- # https://system.netsuite.com/help/helpcenter/en_US/Output/Help/SuiteFlex/WebServices/STP_SettingSearchPreferences.html#N2028598271-3
14
+ # https://system.netsuite.com/help/helpcenter/en_US/Output/Help/SuiteCloudCustomizationScriptingWebServices/SuiteTalkWebServices/SettingSearchPreferences.html
15
+ # https://webservices.netsuite.com/xsd/platform/v2012_2_0/messages.xsd
16
+
16
17
  preferences = NetSuite::Configuration.auth_header
17
- preferences = preferences.merge((@options[:preferences] || {}).inject({}) do |h, (k, v)|
18
- h[k.to_s.lower_camelcase] = v
19
- h
20
- end)
21
-
22
- NetSuite::Configuration.connection(
23
- namespaces: {
24
- 'xmlns:platformMsgs' => "urn:messages_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
25
- 'xmlns:platformCore' => "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
26
- 'xmlns:platformCommon' => "urn:common_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
27
- 'xmlns:listRel' => "urn:relationships_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com",
28
- 'xmlns:tranSales' => "urn:sales_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com",
29
- },
30
- soap_header: preferences
31
- ).call :search, :message => request_body
18
+ preferences = preferences.merge(
19
+ (@options[:preferences] || {}).inject({'platformMsgs:SearchPreferences' => {}}) do |h, (k, v)|
20
+ h['platformMsgs:SearchPreferences'][k.to_s.lower_camelcase] = v
21
+ h
22
+ end
23
+ )
24
+
25
+ NetSuite::Configuration
26
+ .connection(soap_header: preferences)
27
+ .call (@options.has_key?(:search_id)? :search_more_with_id : :search), :message => request_body
32
28
  end
33
29
 
34
30
  # basic search XML
@@ -46,41 +42,151 @@ module NetSuite
46
42
  # </soap:Body>
47
43
 
48
44
  def request_body
45
+ if @options.has_key?(:search_id)
46
+ return {
47
+ 'pageIndex' => @options[:page_index],
48
+ 'searchId' => @options[:search_id],
49
+ }
50
+ end
51
+
52
+ # columns is only needed for advanced search results
53
+ columns = @options[:columns] || {}
49
54
  criteria = @options[:criteria] || @options
50
55
 
51
- # TODO wish there was a cleaner way to do this: we need the namespace of the record
56
+ # TODO find cleaner solution for pulling the namespace of the record, which is a instance method
52
57
  example_instance = @klass.new
53
58
  namespace = example_instance.record_namespace
54
59
 
55
- # extract the class name without the module
60
+ # extract the class name
56
61
  class_name = @klass.to_s.split("::").last
57
62
 
58
- search_record = {}
63
+ criteria_structure = {}
64
+ columns_structure = columns
65
+ saved_search_id = criteria.delete(:saved)
59
66
 
60
- criteria.each_pair do |condition_category, conditions|
61
- search_record["#{namespace}:#{condition_category}"] = conditions.inject({}) do |h, condition|
62
- h["platformCommon:#{condition[:field]}"] = {
63
- "platformCore:searchValue" => condition[:value]
64
- }
67
+ # TODO this whole thing needs to be refactored so we can apply some of the same logic to the
68
+ # column creation xml
65
69
 
66
- (h[:attributes!] ||= {}).merge!({
67
- "platformCommon:#{condition[:field]}" => {
68
- 'operator' => condition[:operator]
70
+ criteria.each_pair do |condition_category, conditions|
71
+ criteria_structure["#{namespace}:#{condition_category}"] = conditions.inject({}) do |h, condition|
72
+ element_name = "platformCommon:#{condition[:field]}"
73
+
74
+ case condition[:field]
75
+ when 'recType'
76
+ # TODO this seems a bit brittle, look into a way to handle this better
77
+ h[element_name] = {
78
+ :@internalId => condition[:value].internal_id
79
+ }
80
+ when 'customFieldList'
81
+ # === START CUSTOM FIELD
82
+
83
+ # there isn't a clean way to do lists of the same element
84
+ # Gyoku doesn't seem support the nice :@attribute and :content! syntax for lists of elements of the same name
85
+ # https://github.com/savonrb/gyoku/issues/18#issuecomment-17825848
86
+
87
+ # TODO with the latest version of savon we can easily improve the code here, should be rewritten with new attribute syntax
88
+
89
+ custom_field_list = condition[:value].map do |h|
90
+ if h[:value].is_a?(Array) && h[:value].first.respond_to?(:to_record)
91
+ {
92
+ "platformCore:searchValue" => h[:value].map(&:to_record),
93
+ :attributes! => {
94
+ 'platformCore:searchValue' => {
95
+ 'internalId' => h[:value].map(&:internal_id)
96
+ }
97
+ }
98
+ }
99
+ elsif h[:value].respond_to?(:to_record)
100
+ {
101
+ "platformCore:searchValue" => {
102
+ :content! => h[:value].to_record,
103
+ :@internalId => h[:value].internal_id
104
+ }
105
+ }
106
+ else
107
+ { "platformCore:searchValue" => h[:value] }
108
+ end
109
+ end
110
+
111
+ h[element_name] = {
112
+ 'platformCore:customField' => custom_field_list,
113
+ :attributes! => {
114
+ 'platformCore:customField' => {
115
+ 'internalId' => condition[:value].map { |h| h[:field] },
116
+ 'operator' => condition[:value].map { |h| h[:operator] },
117
+ 'xsi:type' => condition[:value].map { |h| "platformCore:#{h[:type]}" }
118
+ }
119
+ }
69
120
  }
70
- })
121
+
122
+ # === END CUSTOM FIELD
123
+ else
124
+ if condition[:value].is_a?(Array) && condition[:value].first.respond_to?(:to_record)
125
+ # TODO need to update to the latest savon so we don't need to duplicate the same workaround above again
126
+ # TODO it's possible that this might break, not sure if platformCore:SearchMultiSelectField is the right type in every situation
127
+
128
+ h[element_name] = {
129
+ '@operator' => condition[:operator],
130
+ '@xsi:type' => 'platformCore:SearchMultiSelectField',
131
+ "platformCore:searchValue" => {
132
+ :content! => condition[:value].map(&:to_record),
133
+ '@internalId' => condition[:value].map(&:internal_id),
134
+ '@xsi:type' => 'platformCore:RecordRef',
135
+ '@type' => 'account'
136
+ }
137
+ }
138
+ elsif condition[:value].is_a?(Array) && condition[:type] == 'SearchDateField'
139
+ # date ranges are handled via searchValue (start range) and searchValue2 (end range)
140
+
141
+ h[element_name] = {
142
+ '@operator' => condition[:operator],
143
+ "platformCore:searchValue" => condition[:value].first.to_s,
144
+ "platformCore:searchValue2" => condition[:value].last.to_s
145
+ }
146
+ else
147
+ h[element_name] = {
148
+ :content! => { "platformCore:searchValue" => condition[:value] },
149
+ }
150
+
151
+ h[element_name][:@operator] = condition[:operator] if condition[:operator]
152
+ end
153
+ end
71
154
 
72
155
  h
73
156
  end
74
157
  end
75
158
 
76
- {
77
- 'searchRecord' => search_record,
78
- :attributes! => {
159
+ # TODO this needs to be DRYed up a bit
160
+
161
+ if saved_search_id
162
+ {
163
+ 'searchRecord' => {
164
+ '@savedSearchId' => saved_search_id,
165
+ '@xsi:type' => "#{namespace}:#{class_name}SearchAdvanced",
166
+ :content! => {
167
+ "#{namespace}:criteria" => criteria_structure
168
+ # TODO need to optionally support columns here
169
+ }
170
+ }
171
+ }
172
+ elsif !columns_structure.empty?
173
+ {
174
+ 'searchRecord' => {
175
+ '@xsi:type' => "#{namespace}:#{class_name}SearchAdvanced",
176
+ :content! => {
177
+ "#{namespace}:criteria" => criteria_structure,
178
+ "#{namespace}:columns" => columns_structure
179
+ }
180
+ }
181
+ }
182
+ else
183
+ {
79
184
  'searchRecord' => {
80
- 'xsi:type' => "#{namespace}:#{class_name}Search"
81
- },
185
+ :content! => criteria_structure,
186
+ '@xsi:type' => "#{namespace}:#{class_name}Search"
187
+ }
82
188
  }
83
- }
189
+ end
84
190
  end
85
191
 
86
192
  def response_header
@@ -96,13 +202,22 @@ module NetSuite
96
202
  end
97
203
 
98
204
  def search_result
99
- @search_result = @response.body[:search_response][:search_result]
205
+ @search_result = if @response.body.has_key?(:search_more_with_id_response)
206
+ @response.body[:search_more_with_id_response]
207
+ else
208
+ @response.body[:search_response]
209
+ end[:search_result]
100
210
  end
101
211
 
102
212
  def success?
103
213
  @success ||= search_result[:status][:@is_success] == 'true'
104
214
  end
105
215
 
216
+ protected
217
+ def method_name
218
+
219
+ end
220
+
106
221
  module Support
107
222
  def self.included(base)
108
223
  base.extend(ClassMethods)
@@ -1,5 +1,6 @@
1
- # TODO: Tests
2
- # TODO: DBC
1
+ # TODO Tests
2
+ # TODO this is broken with the current search implementation
3
+
3
4
  module NetSuite
4
5
  module Actions
5
6
  class SearchMoreWithId
@@ -9,17 +9,19 @@ module NetSuite
9
9
  end
10
10
 
11
11
  def request
12
+ api_version = NetSuite::Configuration.api_version
13
+
12
14
  NetSuite::Configuration.connection(
13
15
  namespaces: {
14
- 'xmlns:platformMsgs' => "urn:messages_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
15
- 'xmlns:platformCore' => "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
16
- 'xmlns:listRel' => "urn:relationships_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com",
17
- 'xmlns:tranSales' => "urn:sales_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com",
18
- 'xmlns:platformCommon' => "urn:common_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com",
19
- 'xmlns:listAcct' => "urn:accounting_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com",
20
- 'xmlns:actSched' => "urn:scheduling_#{NetSuite::Configuration.api_version}.activities.webservices.netsuite.com",
21
- 'xmlns:tranCust' => "urn:customers_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com",
22
- 'xmlns:setupCustom' => "urn:customization_#{NetSuite::Configuration.api_version}.setup.webservices.netsuite.com",
16
+ 'xmlns:platformMsgs' => "urn:messages_#{api_version}.platform.webservices.netsuite.com",
17
+ 'xmlns:platformCore' => "urn:core_#{api_version}.platform.webservices.netsuite.com",
18
+ 'xmlns:listRel' => "urn:relationships_#{api_version}.lists.webservices.netsuite.com",
19
+ 'xmlns:tranSales' => "urn:sales_#{api_version}.transactions.webservices.netsuite.com",
20
+ 'xmlns:platformCommon' => "urn:common_#{api_version}.platform.webservices.netsuite.com",
21
+ 'xmlns:listAcct' => "urn:accounting_#{api_version}.lists.webservices.netsuite.com",
22
+ 'xmlns:actSched' => "urn:scheduling_#{api_version}.activities.webservices.netsuite.com",
23
+ 'xmlns:tranCust' => "urn:customers_#{api_version}.transactions.webservices.netsuite.com",
24
+ 'xmlns:setupCustom' => "urn:customization_#{api_version}.setup.webservices.netsuite.com",
23
25
  },
24
26
  ).call :update, :message => request_body
25
27
  end
@@ -31,20 +33,18 @@ module NetSuite
31
33
  # </platformMsgs:update>
32
34
  def request_body
33
35
  hash = {
34
- 'platformMsgs:record' => updated_record.to_record,
35
- :attributes! => {
36
- 'platformMsgs:record' => {
37
- 'xsi:type' => updated_record.record_type
38
- }
36
+ 'platformMsgs:record' => {
37
+ :content! => updated_record.to_record,
38
+ '@xsi:type' => updated_record.record_type
39
39
  }
40
40
  }
41
41
 
42
42
  if updated_record.respond_to?(:internal_id) && updated_record.internal_id
43
- hash[:attributes!]['platformMsgs:record']['platformMsgs:internalId'] = updated_record.internal_id
43
+ hash['platformMsgs:record']['@platformMsgs:internalId'] = updated_record.internal_id
44
44
  end
45
45
 
46
46
  if updated_record.respond_to?(:external_id) && updated_record.external_id
47
- hash[:attributes!]['platformMsgs:record']['platformMsgs:externalId'] = updated_record.external_id
47
+ hash['platformMsgs:record']['@platformMsgs:externalId'] = updated_record.external_id
48
48
  end
49
49
 
50
50
  hash
@@ -12,12 +12,13 @@ module NetSuite
12
12
 
13
13
  def connection(params = {})
14
14
  Savon.client({
15
- wsdl: wsdl,
16
- read_timeout: read_timeout,
17
- soap_header: auth_header,
18
- pretty_print_xml: true,
19
- logger: logger
20
- # open_timeout: ???
15
+ wsdl: wsdl,
16
+ read_timeout: read_timeout,
17
+ namespaces: namespaces,
18
+ soap_header: auth_header,
19
+ pretty_print_xml: true,
20
+ logger: logger
21
+ # open_timeout: ???
21
22
  }.merge(params))
22
23
  end
23
24
 
@@ -70,23 +71,36 @@ module NetSuite
70
71
  'platformCore:email' => email,
71
72
  'platformCore:password' => password,
72
73
  'platformCore:account' => account.to_s,
73
- 'platformCore:role' => role.to_record,
74
- :attributes! => {
75
- 'platformCore:role' => role.attributes!
76
- }
74
+ 'platformCore:role' => { :'@type' => 'role', :@internalId => role }
77
75
  }
78
76
  }
79
77
  end
80
-
78
+
79
+ def namespaces
80
+ {
81
+ 'xmlns:platformMsgs' => "urn:messages_#{api_version}.platform.webservices.netsuite.com",
82
+ 'xmlns:platformCore' => "urn:core_#{api_version}.platform.webservices.netsuite.com",
83
+ 'xmlns:platformCommon' => "urn:common_#{api_version}.platform.webservices.netsuite.com",
84
+ 'xmlns:listRel' => "urn:relationships_#{api_version}.lists.webservices.netsuite.com",
85
+ 'xmlns:tranSales' => "urn:sales_#{api_version}.transactions.webservices.netsuite.com",
86
+ 'xmlns:actSched' => "urn:scheduling_#{api_version}.activities.webservices.netsuite.com",
87
+ 'xmlns:setupCustom' => "urn:customization_#{api_version}.setup.webservices.netsuite.com",
88
+ 'xmlns:listAcct' => "urn:accounting_#{api_version}.lists.webservices.netsuite.com",
89
+ 'xmlns:tranCust' => "urn:customers_#{api_version}.transactions.webservices.netsuite.com",
90
+ 'xmlns:listSupport' => "urn:support_#{api_version}.lists.webservices.netsuite.com",
91
+ 'xmlns:tranGeneral' => "urn:general_#{api_version}.transactions.webservices.netsuite.com",
92
+ }
93
+ end
94
+
81
95
  def role=(role)
82
- attributes[:role] = NetSuite::Records::RecordRef.new(:internal_id => role, :type => 'role')
96
+ attributes[:role] = role
83
97
  end
84
-
98
+
85
99
  def role(role = nil)
86
100
  if role
87
101
  self.role = role
88
- else
89
- attributes[:role] ||= NetSuite::Records::RecordRef.new(:internal_id => '3', :type => 'role')
102
+ else
103
+ attributes[:role] ||= '3'
90
104
  end
91
105
  end
92
106
 
@@ -0,0 +1,11 @@
1
+ module NetSuite
2
+ module Namespaces
3
+ module ListSupport
4
+
5
+ def record_namespace
6
+ 'listSupport'
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -3,6 +3,7 @@ module NetSuite
3
3
  class Account
4
4
  include Support::Fields
5
5
  include Support::RecordRefs
6
+ include Support::Records
6
7
  include Support::Actions
7
8
 
8
9
  actions :get, :add, :delete
@@ -0,0 +1,33 @@
1
+ # TODO needs spec
2
+
3
+ module NetSuite
4
+ module Records
5
+ class BaseRefList
6
+ include Support::Fields
7
+ include Support::Actions
8
+ include Namespaces::PlatformCore
9
+
10
+ actions :get_select_value
11
+
12
+ fields :base_ref
13
+
14
+ def initialize(attrs = {})
15
+ initialize_from_attributes_hash(attrs)
16
+ end
17
+
18
+ def base_ref=(refs)
19
+ case refs
20
+ when Hash
21
+ self.base_ref << RecordRef.new(refs)
22
+ when Array
23
+ refs.each { |ref| self.base_ref << RecordRef.new(ref) }
24
+ end
25
+ end
26
+
27
+ def base_ref
28
+ @base_ref ||= []
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module NetSuite
2
+ module Records
3
+ class CashSale < SalesOrder
4
+
5
+ end
6
+ end
7
+ end
@@ -4,7 +4,8 @@ module NetSuite
4
4
  include Support::Fields
5
5
  include Support::Records
6
6
 
7
- attr_reader :internal_id, :type
7
+ attr_reader :internal_id
8
+ attr_accessor :type
8
9
 
9
10
  def initialize(attributes = {})
10
11
  @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
@@ -20,39 +20,89 @@ module NetSuite
20
20
  end
21
21
 
22
22
  def method_missing(sym, *args, &block)
23
- return @custom_fields_assoc[sym] if @custom_fields_assoc.include? sym
23
+ # read custom field if already set
24
+ if @custom_fields_assoc.include?(sym)
25
+ return @custom_fields_assoc[sym]
26
+ end
27
+
28
+ # write custom field
29
+ if sym.to_s.end_with?('=')
30
+ return create_custom_field(sym.to_s[0..-2], args.first)
31
+ end
32
+
24
33
  super(sym, *args, &block)
25
34
  end
26
35
 
27
36
  def respond_to?(sym, include_private = false)
28
- return true if @custom_fields_assoc.include? sym
37
+ return true if @custom_fields_assoc.include?(sym)
29
38
  super
30
39
  end
31
40
 
32
41
  def to_record
33
- # TODO this is the best way I could find to handle this, there *has* to be a better way
34
- # http://stackoverflow.com/questions/7001957/savon-array-of-xml-tags
35
-
36
42
  {
37
- "#{record_namespace}:customField" => custom_fields.map(&:to_record),
38
- :attributes! => {
39
- "#{record_namespace}:customField" => {
40
- 'internalId' => custom_fields.map(&:internal_id),
41
- 'xsi:type' => custom_fields.map(&:type)
43
+ "#{record_namespace}:customField" => custom_fields.map do |custom_field|
44
+ if custom_field.value.respond_to?(:to_record)
45
+ custom_field_value = custom_field.value.to_record
46
+ else
47
+ custom_field_value = custom_field.value.to_s
48
+ end
49
+
50
+ {
51
+ "platformCore:value" => custom_field_value,
52
+ '@internalId' => custom_field.internal_id,
53
+ '@xsi:type' => custom_field.type
42
54
  }
43
- }
55
+ end
44
56
  }
45
57
  end
46
58
 
47
59
  private
48
60
  def extract_custom_field(custom_field_data)
49
- # TODO this needs to be cleaned up & tested; very messy
61
+ # TODO this seems brittle, but might sufficient, watch out for this if something breaks
50
62
  if custom_field_data[:"@xsi:type"] == "platformCore:SelectCustomFieldRef"
51
63
  custom_field_data[:value] = CustomRecordRef.new(custom_field_data.delete(:value))
52
64
  end
53
65
 
54
66
  custom_fields << CustomField.new(custom_field_data)
55
67
  end
68
+
69
+ def create_custom_field(internal_id, field_value)
70
+ # all custom fields need types; infer type based on class sniffing
71
+ field_type = case
72
+ when field_value.is_a?(Hash)
73
+ 'SelectCustomFieldRef'
74
+ when field_value.is_a?(DateTime),
75
+ field_value.is_a?(Time),
76
+ field_value.is_a?(Date)
77
+ 'DateCustomFieldRef'
78
+ when field_value.is_a?(FalseClass),
79
+ field_value.is_a?(TrueClass)
80
+ 'BooleanCustomFieldRef'
81
+ else
82
+ 'StringCustomFieldRef'
83
+ end
84
+
85
+ # TODO seems like DateTime doesn't need the iso8601 call
86
+ # not sure if this is specific to my env though
87
+
88
+ custom_field_value = case
89
+ when field_value.is_a?(Hash)
90
+ CustomRecordRef.new(field_value)
91
+ when field_value.is_a?(Time)
92
+ field_value.iso8601
93
+ else
94
+ field_value
95
+ end
96
+
97
+ custom_field = CustomField.new(
98
+ internal_id: internal_id,
99
+ value: custom_field_value,
100
+ type: "#{record_namespace}:#{field_type}"
101
+ )
102
+
103
+ custom_fields << custom_field
104
+ @custom_fields_assoc[internal_id.to_sym] = custom_field
105
+ end
56
106
  end
57
107
  end
58
108
  end