netsuite 0.0.49 → 0.0.50

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/README.md +47 -103
  2. data/lib/netsuite.rb +16 -6
  3. data/lib/netsuite/actions/add.rb +3 -0
  4. data/lib/netsuite/actions/search.rb +126 -0
  5. data/lib/netsuite/actions/search_more_with_id.rb +106 -0
  6. data/lib/netsuite/actions/update.rb +1 -0
  7. data/lib/netsuite/configuration.rb +52 -3
  8. data/lib/netsuite/namespaces/act_sched.rb +11 -0
  9. data/lib/netsuite/records/assembly_item.rb +2 -0
  10. data/lib/netsuite/records/bin_number_list.rb +48 -0
  11. data/lib/netsuite/records/contact.rb +44 -0
  12. data/lib/netsuite/records/contact_list.rb +52 -0
  13. data/lib/netsuite/records/custom_field.rb +4 -0
  14. data/lib/netsuite/records/custom_field_list.rb +11 -2
  15. data/lib/netsuite/records/custom_record_ref.rb +2 -0
  16. data/lib/netsuite/records/customer.rb +1 -1
  17. data/lib/netsuite/records/inventory_item.rb +3 -1
  18. data/lib/netsuite/records/invoice.rb +5 -4
  19. data/lib/netsuite/records/invoice_item.rb +3 -1
  20. data/lib/netsuite/records/kit_item.rb +7 -0
  21. data/lib/netsuite/records/non_inventory_sale_item.rb +1 -0
  22. data/lib/netsuite/records/sales_order.rb +1 -1
  23. data/lib/netsuite/records/task.rb +29 -0
  24. data/lib/netsuite/records/transaction.rb +35 -0
  25. data/lib/netsuite/response.rb +2 -1
  26. data/lib/netsuite/support/actions.rb +4 -0
  27. data/lib/netsuite/support/fields.rb +11 -1
  28. data/lib/netsuite/support/requests.rb +6 -1
  29. data/lib/netsuite/support/search_result.rb +67 -0
  30. data/lib/netsuite/version.rb +1 -1
  31. data/spec/netsuite/configuration_spec.rb +8 -0
  32. data/spec/netsuite/records/non_inventory_sale_item_spec.rb +2 -1
  33. data/spec/netsuite/support/record_refs_spec.rb +10 -1
  34. metadata +12 -2
data/README.md CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  * This gem will act as a wrapper around the NetSuite SuiteTalk WebServices API. Wow, that is a mouthful.
4
4
  * The gem does not cover the entire API, only the subset that we have found useful to cover so far.
5
- * Extending the wrapper is pretty simple. See below for an example.
5
+ * Extending the wrapper is pretty simple. Check out the [contribution help doc](https://github.com/RevolutionPrep/netsuite/wiki/Contributing-to-the-Supported-NetSuite-API)
6
+ * NetSuite development is overall a pretty poor experience. We have a list of [NetSuite Development Resources](https://github.com/RevolutionPrep/netsuite/wiki/NetSuite-Development-Resources) that might make things a bit less painful.
6
7
 
7
8
  ## Installation
8
9
 
@@ -24,7 +25,7 @@ This gem is built for ruby 1.9.x, checkout the [1-8-stable](https://github.com/R
24
25
  Before contributing a patch make sure all existing tests pass.
25
26
 
26
27
  ```
27
- git clone git://github.com/iloveitaly/netsuite.git
28
+ git clone git://github.com/RevolutionPrep/netsuite.git
28
29
  cd netsuite
29
30
  bundle
30
31
  bundle exec rspec
@@ -36,114 +37,57 @@ bundle exec rspec
36
37
  NetSuite.configure do
37
38
  reset!
38
39
 
40
+ # optional, defaults to 2011_2
39
41
  api_version '2012_1'
40
42
 
41
- # specify full wsdl URL for sandbox / production switching
43
+ # optionally specify full wsdl URL (to switch to sandbox, for example)
42
44
  wsdl "https://webservices.sandbox.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl"
43
45
 
46
+ # or specify the sandbox flag if you don't want to deal with specifying a full URL
47
+ sandbox true
48
+
49
+ # often the netsuite servers will hang which would cause a timeout exception to be raised
50
+ # if you don't mind waiting (e.g. processing NS via DJ), increasing the timeout should fix the issue
51
+ read_timeout 100000
52
+
53
+ # you can specify a file or file descriptor to send the log output to (defaults to STDOUT)
54
+ log File.join(Rails.root, 'log/netsuite.log')
55
+
44
56
  # login information
45
- email 'email@domain.com'
46
- password 'password'
57
+ email 'email@domain.com'
58
+ password 'password'
47
59
  account '12345'
48
60
  role 1111
49
61
  end
50
62
  ```
51
63
 
52
- ### Customer
53
-
54
- * Initializing a customer can be done using a hash of attributes.
55
-
56
- ### Get
57
-
58
- * Retrieves the customer by internalId.
59
-
60
- ```Ruby
61
- customer = NetSuite::Records::Customer.get(:internal_id => 4)
62
- # => #<NetSuite::Records::Customer:0x1042f59b8>
63
- customer.is_person
64
- # => true
65
-
66
- # or
67
- NetSuite::Records::Customer.get(4).is_person
68
- ```
69
-
70
- ## Additions
71
-
72
- * Please submit a pull request for any models or actions that you would like to be included. The API is quite large and so we will necessarily not cover all of it.
73
- * Records should go into the `lib/netsuite/records/` directory.
74
- * Actions should be placed in their respective subdirectory under `lib/netsuite/actions`.
75
- * Example:
76
-
77
- ```Ruby
78
- # lib/netsuite/actions/customer/add.rb
79
-
80
- module NetSuite
81
- module Actions
82
- module Customer
83
- class Add
84
-
85
- def initialize(attributes = {})
86
- @attributes = attributes
87
- end
88
-
89
- def self.call(attributes)
90
- new(attributes).call
91
- end
92
-
93
- def call
94
- response = NetSuite::Configuration.connection.request :add do
95
- soap.header = NetSuite::Configuration.auth_header
96
- soap.body = {
97
- :entityId => @attributes[:entity_id],
98
- :companyName => @attributes[:company_name],
99
- :unsubscribe => @attributes[:unsubscribe]
100
- }
101
- end
102
- success = response.to_hash[:add_response][:write_response][:status][:@is_success] == 'true'
103
- body = response.to_hash[:add_response][:write_response][:base_ref]
104
- NetSuite::Response.new(:success => success, :body => body)
105
- end
106
-
107
- end
108
- end
109
- end
110
- end
111
-
112
- response = NetSuite::Actions::Customer::Add.call(
113
- :entity_id => 'Shutter Fly',
114
- :company_name => 'Shutter Fly, Inc.',
115
- :unsubscribe => false
116
- ) # => #<NetSuite::Response:0x1041f64b5>
117
- response.success? # => true
118
- response.body # => { :internal_id => '979', :type => 'customer' }
119
- ```
120
-
121
- ## Gotchas
122
-
123
- * The Initialize Action duck-punches the .initialize method on any class that includes it.
124
- This has not proven to be a issue yet, but should be taken into account when analyzing any
125
- strange issues with the gem.
126
- * Some records define a 'class' field. Defining a 'class' field on a record overrides the
127
- #class and #class= methods for this class. This is very obviously a problem. You can,
128
- instead, define a 'klass' field that will be turned into 'class' before being submitted
129
- to the API. The Invoice record has an example of this.
130
-
131
- ## Contributing
132
-
133
- 1. Fork it
134
- 2. Create your feature branch (`git checkout -b my-new-feature`)
135
- 3. Write new test and ensure that `bundle exec rspec` doesn't fail
136
- 4. Commit your changes (`git commit -am 'Added some feature'`)
137
- 5. Push to the branch (`git push origin my-new-feature`)
138
- 6. Create new Pull Request
139
-
140
- ## Fields and RecordRefs
141
-
142
- Note that some record attributes are specified as Fields while others are specified as RecordRefs. In some cases
143
- attributes are actually RecordRefs in the schema, but we indicate them as Fields. Our experience has shown
144
- this works as long as the attribute is only read from and is not written to. Writing a value for an attribute
145
- that has been wrongly specified will result in an error. Be careful when initializing objects from other objects --
146
- they may carry attributes that write to the new object.
147
-
148
- As we build up this gem we will replace these inconsistent Fields with RecordRefs. Feel free to contribute new Record
149
- definitions to help us along.
64
+ ### Examples
65
+
66
+ ```ruby
67
+ # retrieve a customer
68
+ customer = NetSuite::Records::Customer.get(:internal_id => 4)
69
+ customer.is_person
70
+
71
+ # or
72
+ NetSuite::Records::Customer.get(4).is_person
73
+
74
+ # randomly assign a task
75
+ customer_support_reps = [12345, 12346]
76
+
77
+ task = NetSuite::Records::Task.new(
78
+ :title => 'Take Care of a Customer',
79
+ :assigned => NetSuite::Records::RecordRef.new(customer_support_reps.sample),
80
+ :due_date => DateTime.now + 1,
81
+ :message => "Take care of this"
82
+ )
83
+
84
+ task.add
85
+
86
+ # this will only work on OS X, open a browser to the record that was just created
87
+ `open https://system.sandbox.netsuite.com/app/crm/calendar/task.nl?id=#{invoice.internal_id}`
88
+
89
+ task.update :message => 'New Message'
90
+ ```
91
+
92
+
93
+
data/lib/netsuite.rb CHANGED
@@ -11,6 +11,7 @@ module NetSuite
11
11
  autoload :Response, 'netsuite/response'
12
12
 
13
13
  module Namespaces
14
+ autoload :ActSched, 'netsuite/namespaces/act_sched'
14
15
  autoload :ListAcct, 'netsuite/namespaces/list_acct'
15
16
  autoload :ListRel, 'netsuite/namespaces/list_rel'
16
17
  autoload :PlatformCommon, 'netsuite/namespaces/platform_common'
@@ -28,14 +29,17 @@ module NetSuite
28
29
  autoload :RecordRefs, 'netsuite/support/record_refs'
29
30
  autoload :Records, 'netsuite/support/records'
30
31
  autoload :Requests, 'netsuite/support/requests'
32
+ autoload :SearchResult, 'netsuite/support/search_result'
31
33
  end
32
34
 
33
35
  module Actions
34
- autoload :Add, 'netsuite/actions/add'
35
- autoload :Delete, 'netsuite/actions/delete'
36
- autoload :Get, 'netsuite/actions/get'
37
- autoload :Initialize, 'netsuite/actions/initialize'
38
- autoload :Update, 'netsuite/actions/update'
36
+ autoload :Add, 'netsuite/actions/add'
37
+ autoload :Delete, 'netsuite/actions/delete'
38
+ autoload :Get, 'netsuite/actions/get'
39
+ autoload :Initialize, 'netsuite/actions/initialize'
40
+ autoload :Update, 'netsuite/actions/update'
41
+ autoload :Search, 'netsuite/actions/search'
42
+ autoload :SearchMoreWithId, 'netsuite/actions/search_more_with_id'
39
43
  end
40
44
 
41
45
  module Records
@@ -43,6 +47,7 @@ module NetSuite
43
47
  autoload :Account, 'netsuite/records/account'
44
48
  autoload :AccountingPeriod, 'netsuite/records/accounting_period'
45
49
  autoload :BillAddress, 'netsuite/records/bill_address'
50
+ autoload :BinNumberList, 'netsuite/records/bin_number_list'
46
51
  autoload :Classification, 'netsuite/records/classification'
47
52
  autoload :CreditMemo, 'netsuite/records/credit_memo'
48
53
  autoload :CreditMemoApply, 'netsuite/records/credit_memo_apply'
@@ -63,6 +68,8 @@ module NetSuite
63
68
  autoload :CustomerRefundApplyList, 'netsuite/records/customer_refund_apply_list'
64
69
  autoload :CustomerRefundDeposit, 'netsuite/records/customer_refund_deposit'
65
70
  autoload :CustomerRefundDepositList, 'netsuite/records/customer_refund_deposit_list'
71
+ autoload :ContactList, 'netsuite/records/contact_list'
72
+ autoload :Contact, 'netsuite/records/contact'
66
73
  autoload :Department, 'netsuite/records/department'
67
74
  autoload :Duration, 'netsuite/records/duration'
68
75
  autoload :InventoryItem, 'netsuite/records/inventory_item'
@@ -73,6 +80,7 @@ module NetSuite
73
80
  autoload :JournalEntry, 'netsuite/records/journal_entry'
74
81
  autoload :JournalEntryLine, 'netsuite/records/journal_entry_line'
75
82
  autoload :JournalEntryLineList, 'netsuite/records/journal_entry_line_list'
83
+ autoload :KitItem, 'netsuite/records/kit_item'
76
84
  autoload :Location, 'netsuite/records/location'
77
85
  autoload :NonInventorySaleItem, 'netsuite/records/non_inventory_sale_item'
78
86
  autoload :PaymentMethod, 'netsuite/records/payment_method'
@@ -83,13 +91,15 @@ module NetSuite
83
91
  autoload :SalesOrderItem, 'netsuite/records/sales_order_item'
84
92
  autoload :SalesOrderItemList, 'netsuite/records/sales_order_item_list'
85
93
  autoload :ShipAddress, 'netsuite/records/ship_address'
94
+ autoload :Task, 'netsuite/records/task'
86
95
  autoload :Term, 'netsuite/records/term'
96
+ autoload :Transaction, 'netsuite/records/transaction'
87
97
  end
88
98
 
89
99
  def self.configure(&block)
90
100
  NetSuite::Configuration.instance_eval(&block)
91
101
  Savon.configure do |config|
92
- config.logger = NetSuite::XmlLogger.new(STDOUT)
102
+ config.logger = NetSuite::Configuration.logger
93
103
  end
94
104
  end
95
105
 
@@ -17,6 +17,7 @@ module NetSuite
17
17
  soap.namespaces['xmlns:tranSales'] = "urn:sales_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com"
18
18
  soap.namespaces['xmlns:platformCommon'] = "urn:common_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
19
19
  soap.namespaces['xmlns:listAcct'] = "urn:accounting_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com"
20
+ soap.namespaces['xmlns:actSched'] = "urn:scheduling_#{NetSuite::Configuration.api_version}.activities.webservices.netsuite.com"
20
21
  soap.namespaces['xmlns:tranCust'] = "urn:customers_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com"
21
22
  soap.namespaces['xmlns:setupCustom'] = "urn:customization_#{NetSuite::Configuration.api_version}.setup.webservices.netsuite.com"
22
23
  soap.namespaces['xmlns:tranGeneral'] = "urn:general_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com"
@@ -69,6 +70,8 @@ module NetSuite
69
70
  if response.success?
70
71
  @internal_id = response.body[:@internal_id]
71
72
  true
73
+ else
74
+ false
72
75
  end
73
76
  end
74
77
  end
@@ -0,0 +1,126 @@
1
+ # TODO: Tests
2
+ module NetSuite
3
+ module Actions
4
+ class Search
5
+ include Support::Requests
6
+
7
+ def initialize(klass, options = { })
8
+ @klass = klass
9
+ @options = options
10
+ end
11
+
12
+ private
13
+
14
+ def request
15
+ # need to do this outside the block below because of variable scoping
16
+ preferences = (@options[:preferences] || {}).inject({}) do |h, (k, v)|
17
+ h[k.to_s.lower_camelcase] = v
18
+ h
19
+ end
20
+
21
+ connection.request(:search) do
22
+ soap.namespaces['xmlns:platformMsgs'] = "urn:messages_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
23
+ soap.namespaces['xmlns:platformCore'] = "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
24
+ soap.namespaces['xmlns:platformCommon'] = "urn:common_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
25
+ soap.namespaces['xmlns:listRel'] = "urn:relationships_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com"
26
+ soap.namespaces['xmlns:tranSales'] = "urn:sales_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com"
27
+
28
+ # https://system.netsuite.com/help/helpcenter/en_US/Output/Help/SuiteFlex/WebServices/STP_SettingSearchPreferences.html#N2028598271-3
29
+ soap.header = auth_header.merge(preferences.empty? ? {} : { 'platformMsgs:searchPreferences' => preferences })
30
+
31
+ soap.body = request_body
32
+ end
33
+ end
34
+
35
+ # basic search XML
36
+
37
+ # <soap:Body>
38
+ # <platformMsgs:search>
39
+ # <searchRecord xsi:type="ContactSearch">
40
+ # <customerJoin xsi:type="CustomerSearchBasic">
41
+ # <email operator="contains" xsi:type="platformCore:SearchStringField">
42
+ # <platformCore:searchValue>shutterfly.com</platformCore:searchValue>
43
+ # <email>
44
+ # <customerJoin>
45
+ # </searchRecord>
46
+ # </search>
47
+ # </soap:Body>
48
+
49
+ def request_body
50
+ criteria = @options[:criteria] || @options
51
+
52
+ # TODO wish there was a cleaner way to do this: we need the namespace of the record
53
+ example_instance = @klass.new
54
+ namespace = example_instance.record_namespace
55
+
56
+ # extract the class name without the module
57
+ class_name = @klass.to_s.split("::").last
58
+
59
+ search_record = {}
60
+
61
+ criteria.each_pair do |condition_category, conditions|
62
+ search_record["#{namespace}:#{condition_category}"] = conditions.inject({}) do |h, condition|
63
+ h["platformCommon:#{condition[:field]}"] = {
64
+ "platformCore:searchValue" => condition[:value]
65
+ }
66
+
67
+ (h[:attributes!] ||= {}).merge!({
68
+ "platformCommon:#{condition[:field]}" => {
69
+ 'operator' => condition[:operator]
70
+ }
71
+ })
72
+
73
+ h
74
+ end
75
+ end
76
+
77
+ {
78
+ 'searchRecord' => search_record,
79
+ :attributes! => {
80
+ 'searchRecord' => {
81
+ 'xsi:type' => "#{namespace}:#{class_name}Search"
82
+ },
83
+ }
84
+ }
85
+ end
86
+
87
+ def response_header
88
+ @response_header ||= response_header_hash
89
+ end
90
+
91
+ def response_header_hash
92
+ @response_header_hash = @response.header[:document_info]
93
+ end
94
+
95
+ def response_body
96
+ @response_body ||= search_result
97
+ end
98
+
99
+ def search_result
100
+ @search_result = @response[:search_response][:search_result]
101
+ end
102
+
103
+ def success?
104
+ @success ||= search_result[:status][:@is_success] == 'true'
105
+ end
106
+
107
+ module Support
108
+ def self.included(base)
109
+ base.extend(ClassMethods)
110
+ end
111
+
112
+ module ClassMethods
113
+ def search(options = { })
114
+ response = NetSuite::Actions::Search.call(self, options)
115
+
116
+ if response.success?
117
+ NetSuite::Support::SearchResult.new(response, self)
118
+ else
119
+ false
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,106 @@
1
+ # TODO: Tests
2
+ # TODO: DBC
3
+ module NetSuite
4
+ module Actions
5
+ class SearchMoreWithId
6
+ include Support::Requests
7
+
8
+ def initialize(klass, options = { })
9
+ @klass = klass
10
+
11
+ @options = options
12
+ end
13
+
14
+ private
15
+
16
+ def request
17
+ connection.request(:search_more_with_id) do
18
+ soap.namespaces['xmlns:platformMsgs'] = "urn:messages_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
19
+ soap.namespaces['xmlns:platformCore'] = "urn:core_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
20
+ soap.namespaces['xmlns:platformCommon'] = "urn:common_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
21
+ soap.namespaces['xmlns:listRel'] = "urn:relationships_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com"
22
+ soap.namespaces['xmlns:tranSales'] = "urn:sales_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com"
23
+
24
+ soap.header = auth_header
25
+
26
+ soap.body = request_body
27
+ end
28
+ end
29
+
30
+ # TODO: Consistent use of namespace qualifying
31
+ def request_body
32
+ buffer = ''
33
+
34
+ xml = Builder::XmlMarkup.new(target: buffer)
35
+
36
+ xml.platformMsgs(:searchId, @options[:search_id])
37
+ xml.platformMsgs(:pageIndex, @options[:page].present? ? @options[:page] : 2)
38
+
39
+ buffer
40
+ end
41
+
42
+ def response_body
43
+ @response_body ||= response_body_hash
44
+ end
45
+
46
+ def response_body_hash
47
+ @response_body_hash = @response[:search_more_with_id_response][:search_result]
48
+ end
49
+
50
+ def success?
51
+ @success ||= response_body_hash[:status][:@is_success] == 'true'
52
+ end
53
+
54
+ # TODO: Refactor
55
+ def more?
56
+ @more ||= response_body_hash[:page_index] < response_body_hash[:total_pages]
57
+ end
58
+
59
+ module Support
60
+ def self.included(base)
61
+ base.extend(ClassMethods)
62
+ end
63
+
64
+ # TODO: Rename page_index to page
65
+ module ClassMethods
66
+ # Preconditions
67
+ # => options[:search_id] is a string
68
+ # => options[:page], optional, is an integer
69
+ # Postconditions
70
+ # => Hash with the following keys:
71
+ # * page_index which is an integer
72
+ # * total_pages which is an integer
73
+ # * search_results containing array of SearchResult's
74
+ def search_more_with_id(options = { })
75
+ response = NetSuite::Actions::SearchMoreWithId.call(self, options)
76
+
77
+ response_hash = { }
78
+
79
+ if response.success?
80
+ search_results = []
81
+
82
+ if response.body[:search_row_list].present?
83
+ response.body[:search_row_list][:search_row].each do |record|
84
+ search_result = NetSuite::Support::SearchResult.new(record)
85
+
86
+ search_results << search_result
87
+ end
88
+ end
89
+
90
+ page_index = response.body[:page_index]
91
+ total_pages = response.body[:total_pages]
92
+
93
+ response_hash[:page_index] = page_index
94
+ response_hash[:total_pages] = total_pages
95
+ response_hash[:search_results] = search_results
96
+
97
+ response_hash
98
+ else
99
+ raise ArgumentError
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -16,6 +16,7 @@ module NetSuite
16
16
  soap.namespaces['xmlns:tranSales'] = "urn:sales_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com"
17
17
  soap.namespaces['xmlns:platformCommon'] = "urn:common_#{NetSuite::Configuration.api_version}.platform.webservices.netsuite.com"
18
18
  soap.namespaces['xmlns:listAcct'] = "urn:accounting_#{NetSuite::Configuration.api_version}.lists.webservices.netsuite.com"
19
+ soap.namespaces['xmlns:actSched'] = "urn:scheduling_#{NetSuite::Configuration.api_version}.activities.webservices.netsuite.com"
19
20
  soap.namespaces['xmlns:tranCust'] = "urn:customers_#{NetSuite::Configuration.api_version}.transactions.webservices.netsuite.com"
20
21
  soap.namespaces['xmlns:setupCustom'] = "urn:customization_#{NetSuite::Configuration.api_version}.setup.webservices.netsuite.com"
21
22
  soap.header = auth_header
@@ -11,9 +11,14 @@ module NetSuite
11
11
  end
12
12
 
13
13
  def connection
14
- attributes[:connection] ||= Savon::Client.new(self.wsdl)
14
+ unless attributes[:connection]
15
+ attributes[:connection] = Savon::Client.new(self.wsdl)
16
+ attributes[:connection].http.read_timeout = read_timeout
17
+ end
18
+
19
+ attributes[:connection]
15
20
  end
16
-
21
+
17
22
  def api_version(version = nil)
18
23
  if version
19
24
  self.api_version = version
@@ -26,6 +31,18 @@ module NetSuite
26
31
  attributes[:api_version] = version
27
32
  end
28
33
 
34
+ def sandbox=(flag)
35
+ attributes[:flag] = flag
36
+ end
37
+
38
+ def sandbox(flag = nil)
39
+ if flag.nil?
40
+ attributes[:flag] ||= false
41
+ else
42
+ self.sandbox = flag
43
+ end
44
+ end
45
+
29
46
  def wsdl=(wsdl)
30
47
  attributes[:wsdl] = wsdl
31
48
  end
@@ -34,7 +51,14 @@ module NetSuite
34
51
  if wsdl
35
52
  self.wsdl = wsdl
36
53
  else
37
- attributes[:wsdl] ||= File.expand_path("../../../wsdl/#{api_version}.wsdl", __FILE__)
54
+ if sandbox
55
+ wsdl_path = "https://webservices.sandbox.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl"
56
+ else
57
+ wsdl_path = File.expand_path("../../../wsdl/#{api_version}.wsdl", __FILE__)
58
+ wsdl_path = "https://webservices.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl" unless File.exists? wsdl_path
59
+ end
60
+
61
+ attributes[:wsdl] ||= wsdl_path
38
62
  end
39
63
  end
40
64
 
@@ -106,5 +130,30 @@ module NetSuite
106
130
  end
107
131
  end
108
132
 
133
+ def read_timeout=(timeout)
134
+ attributes[:read_timeout] = timeout
135
+ end
136
+
137
+ def read_timeout(timeout = nil)
138
+ if timeout
139
+ self.read_timeout = timeout
140
+ else
141
+ attributes[:read_timeout] ||= 60
142
+ end
143
+ end
144
+
145
+ def log=(path)
146
+ attributes[:log] = path
147
+ end
148
+
149
+ def log(path = nil)
150
+ self.log = path if path
151
+ attributes[:log]
152
+ end
153
+
154
+ def logger
155
+ attributes[:logger] ||= NetSuite::XmlLogger.new (log && !log.empty?) ? log : STDOUT
156
+ end
157
+
109
158
  end
110
159
  end
@@ -0,0 +1,11 @@
1
+ module NetSuite
2
+ module Namespaces
3
+ module ActSched
4
+
5
+ def record_namespace
6
+ 'actSched'
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -41,6 +41,8 @@ module NetSuite
41
41
  :supply_replenishment_method, :supply_type, :tax_schedule, :units_type, :vendor
42
42
 
43
43
  field :custom_field_list, CustomFieldList
44
+ field :bin_number_list, BinNumberList
45
+ field :pricing_matrix, PricingMatrix
44
46
 
45
47
  attr_reader :internal_id
46
48
  attr_accessor :external_id
@@ -0,0 +1,48 @@
1
+ module NetSuite
2
+ module Records
3
+ # TODO this is fairly messy: shouldn't mix multiple classes in one file
4
+ # might be possible to trash the GenericField as well
5
+
6
+ class GenericField
7
+ include Support::Attributes
8
+ include Support::Fields
9
+
10
+ def initialize(attributes = {})
11
+ self.attributes = attributes
12
+ end
13
+ end
14
+
15
+ class BinNumber < GenericField
16
+
17
+ end
18
+
19
+ class BinNumberList
20
+ include Support::Fields
21
+ include Namespaces::PlatformCore
22
+ # include Namespaces::ListAcct
23
+
24
+ fields :bin_number
25
+
26
+ def initialize(attributes = {})
27
+ initialize_from_attributes_hash(attributes)
28
+ end
29
+
30
+ def bin_number=(items)
31
+ case items
32
+ when Hash
33
+ self.bin_numbers << BinNumber.new(items)
34
+ when Array
35
+ items.each { |item| self.bin_numbers << BinNumber.new(item) }
36
+ end
37
+ end
38
+
39
+ def bin_numbers
40
+ @bin_numbers ||= []
41
+ end
42
+
43
+ def to_record
44
+ { "#{record_namespace}:item" => bin_numbers.map(&:to_record) }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ module NetSuite
2
+ module Records
3
+ class Contact
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::ListRel
9
+
10
+ actions :get, :add, :delete, :search
11
+
12
+ fields :salutation, :first_name, :middle_name, :last_name, :title, :phone, :fax, :email, :default_address,
13
+ :entity_id, :phonetic_name, :alt_email, :office_phone, :home_phone, :mobile_phone, :supervisor_phone,
14
+ :assistant_phone, :comments, :bill_pay, :is_private, :is_inactive
15
+
16
+ field :addressbook_list, CustomerAddressbookList
17
+ field :custom_field_list, CustomFieldList
18
+ # field :subscriptions_list, SubscriptionsList
19
+ # field :category_list, CategoryList
20
+
21
+ read_only_fields :last_modified_date, :date_created
22
+
23
+ record_refs :custom_form, :company, :subsidiary, :supervisor, :assistant, :image, :global_subscription_status
24
+
25
+ attr_reader :internal_id
26
+ attr_accessor :external_id
27
+
28
+ def initialize(attributes = {})
29
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
30
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
31
+ initialize_from_attributes_hash(attributes)
32
+ end
33
+
34
+ def to_record
35
+ rec = super
36
+ if rec["#{record_namespace}:customFieldList"]
37
+ rec["#{record_namespace}:customFieldList!"] = rec.delete("#{record_namespace}:customFieldList")
38
+ end
39
+ rec
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,52 @@
1
+ module NetSuite
2
+ module Records
3
+ class ContactList
4
+ include Namespaces::ActSched
5
+
6
+ def initialize(attributes = {})
7
+ # - the contact list on the NS GUI doesn't show the assigned contact which shows up in the XML
8
+ # - you can't add an arbitary number of contacts through the NS GUI
9
+
10
+ # TODO the contact list doesn't really work because of the strange XML below (2 assigned companies, one contact)
11
+
12
+ # <actSched:contactList>
13
+ # <actSched:contact>
14
+ # <actSched:company xmlns:platformCore="urn:core_2012_1.platform.webservices.netsuite.com" internalId="12345">
15
+ # <platformCore:name>10001 Another Customer</platformCore:name>
16
+ # </actSched:company>
17
+ # </actSched:contact>
18
+ # <actSched:contact>
19
+ # <actSched:company xmlns:platformCore="urn:core_2012_1.platform.webservices.netsuite.com" internalId="12346">
20
+ # <platformCore:name>31500 A Customer</platformCore:name>
21
+ # </actSched:company>
22
+ # <actSched:contact xmlns:platformCore="urn:core_2012_1.platform.webservices.netsuite.com" internalId="12347">
23
+ # <platformCore:name>A Person</platformCore:name>
24
+ # </actSched:contact>
25
+ # </actSched:contact>
26
+ # </actSched:contactList>
27
+
28
+ case attributes[:contact]
29
+ when Hash
30
+ contacts << Contact.new(attributes[:contact])
31
+ when Array
32
+ attributes[:contact].each { |contact| contacts << Contact.new(contact) }
33
+ end
34
+ end
35
+
36
+ def <<(contact)
37
+ @contacts << contact
38
+ end
39
+
40
+ def contacts
41
+ @contacts ||= []
42
+ end
43
+
44
+ def to_record
45
+ contacts.map do |contact|
46
+ { "#{record_namespace}:contact" => contact.to_record }
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -12,6 +12,10 @@ module NetSuite
12
12
  self.attributes = attributes
13
13
  end
14
14
 
15
+ def value
16
+ attributes[:value]
17
+ end
18
+
15
19
  def to_record
16
20
  hash_rec = attributes.inject({}) do |hash, (k,v)|
17
21
  kname = "#{record_namespace}:#{k.to_s.lower_camelcase}"
@@ -6,9 +6,9 @@ module NetSuite
6
6
  def initialize(attributes = {})
7
7
  case attributes[:custom_field]
8
8
  when Hash
9
- custom_fields << CustomField.new(attributes[:custom_field])
9
+ extract_custom_field(attributes[:custom_field])
10
10
  when Array
11
- attributes[:custom_field].each { |custom_field| custom_fields << CustomField.new(custom_field) }
11
+ attributes[:custom_field].each { |custom_field| extract_custom_field(custom_field) }
12
12
  end
13
13
 
14
14
  @custom_fields_assoc = Hash.new
@@ -40,6 +40,15 @@ module NetSuite
40
40
  }.join
41
41
  end
42
42
 
43
+ private
44
+ def extract_custom_field(custom_field_data)
45
+ # TODO this needs to be cleaned up & tested; very messy
46
+ if custom_field_data[:"@xsi:type"] == "platformCore:SelectCustomFieldRef"
47
+ custom_field_data[:value] = CustomRecordRef.new(custom_field_data.delete(:value))
48
+ end
49
+
50
+ custom_fields << CustomField.new(custom_field_data)
51
+ end
43
52
  end
44
53
  end
45
54
  end
@@ -8,6 +8,8 @@ module NetSuite
8
8
  attr_reader :internal_id
9
9
  attr_accessor :external_id, :type_id
10
10
 
11
+ fields :name
12
+
11
13
  def initialize(attributes_or_record = {})
12
14
  case attributes_or_record
13
15
  when Hash
@@ -7,7 +7,7 @@ module NetSuite
7
7
  include Support::Actions
8
8
  include Namespaces::ListRel
9
9
 
10
- actions :get, :add, :delete
10
+ actions :get, :add, :delete, :search
11
11
 
12
12
  fields :access_role, :account_number, :aging, :alt_email, :alt_name, :alt_phone, :bill_pay,
13
13
  :buying_reason, :buying_time_frame, :campaign_category, :category, :click_stream, :comments, :company_name,
@@ -42,7 +42,9 @@ module NetSuite
42
42
 
43
43
  field :pricing_matrix, PricingMatrix
44
44
  field :custom_field_list, CustomFieldList
45
-
45
+ field :bin_number_list, BinNumberList
46
+ field :pricing_matrix, PricingMatrix
47
+
46
48
  attr_reader :internal_id
47
49
  attr_accessor :external_id
48
50
 
@@ -9,13 +9,13 @@ module NetSuite
9
9
 
10
10
  actions :get, :initialize, :add, :delete
11
11
 
12
- fields :alt_handling_cost, :alt_shipping_cost, :balance, :bill_address,
12
+ fields :alt_handling_cost, :balance, :bill_address,
13
13
  :billing_schedule, :contrib_pct, :created_date, :currency_name, :custom_field_list,
14
14
  :deferred_revenue, :discount_amount, :discount_date, :discount_item, :discount_rate,
15
15
  :due_date, :email, :end_date, :est_gross_profit, :est_gross_profit_percent, :exchange_rate,
16
16
  :exclude_commission, :exp_cost_disc_amount, :exp_cost_disc_print, :exp_cost_disc_rate, :exp_cost_disc_tax_1_amt,
17
17
  :exp_cost_disc_taxable, :exp_cost_discount, :exp_cost_list, :exp_cost_tax_code, :exp_cost_tax_rate_1,
18
- :exp_cost_tax_rate_2, :fax, :fob, :gift_cert_applied, :gift_cert_redemption_list, :handling_cost, :handling_tax_1_rate,
18
+ :exp_cost_tax_rate_2, :fax, :fob, :gift_cert_redemption_list, :handling_tax_1_rate,
19
19
  :handling_tax_2_rate, :handling_tax_code, :is_taxable, :item_cost_disc_amount, :item_cost_disc_print,
20
20
  :item_cost_disc_rate, :item_cost_disc_tax_1_amt, :item_cost_disc_taxable, :item_cost_discount, :item_cost_list,
21
21
  :item_cost_tax_code, :item_cost_tax_rate_1, :item_cost_tax_rate_2, :item_list, :job, :last_modified_date,
@@ -24,7 +24,7 @@ module NetSuite
24
24
  :rev_rec_on_rev_commitment, :rev_rec_schedule, :rev_rec_start_date, :revenue_status, :sales_effective_date,
25
25
  :sales_group, :sales_team_list, :ship_address, :ship_date, :ship_group_list,
26
26
  :shipping_cost, :shipping_tax_1_rate, :shipping_tax_2_rate, :shipping_tax_code, :source, :start_date,
27
- :status, :subsidiary, :sync_partner_teams, :sync_sales_teams, :tax_2_total, :tax_rate,
27
+ :status, :subsidiary, :sync_partner_teams, :sync_sales_teams, :tax_2_total,
28
28
  :tax_total, :time_disc_amount, :time_disc_print, :time_disc_rate, :time_disc_tax_1_amt, :time_disc_taxable,
29
29
  :time_discount, :time_list, :time_tax_code, :time_tax_rate_1, :time_tax_rate_2, :to_be_emailed, :to_be_faxed,
30
30
  :to_be_printed, :total_cost_estimate, :tracking_numbers, :tran_date, :tran_id, :tran_is_vsoe_bundle,
@@ -35,7 +35,8 @@ module NetSuite
35
35
  field :item_list, InvoiceItemList
36
36
  field :custom_field_list, CustomFieldList
37
37
 
38
- read_only_fields :sub_total, :discount_total, :total, :recognized_revenue, :amount_remaining, :amount_paid
38
+ read_only_fields :sub_total, :discount_total, :total, :recognized_revenue, :amount_remaining, :amount_paid,
39
+ :alt_shipping_cost, :gift_cert_applied, :tax_rate, :handling_cost
39
40
 
40
41
  record_refs :account, :bill_address_list, :custom_form, :department, :entity, :klass,
41
42
  :posting_period, :ship_address_list, :terms, :location, :sales_rep, :tax_item, :created_from,
@@ -10,12 +10,14 @@ module NetSuite
10
10
  :defer_rev_rec, :description, :gift_cert_from, :gift_cert_message, :gift_cert_number, :gift_cert_recipient_email,
11
11
  :gift_cert_recipient_name, :gross_amt, :inventory_detail, :is_taxable, :item_is_fulfilled, :license_code, :line,
12
12
  :klass, :options, :order_line, :percent_complete, :quantity, :quantity_available, :quantity_fulfilled,
13
- :quantity_on_hand, :quantity_ordered, :quantity_remaining, :rate, :rev_rec_end_date, :rev_rec_start_date,
13
+ :quantity_on_hand, :quantity_ordered, :rate, :rev_rec_end_date, :rev_rec_start_date,
14
14
  :serial_numbers, :ship_group, :tax1_amt, :tax_rate1, :tax_rate2, :vsoe_allocation, :vsoe_amount, :vsoe_deferral,
15
15
  :vsoe_delivered, :vsoe_permit_discount, :vsoe_price
16
16
 
17
17
  field :custom_field_list, CustomFieldList
18
18
 
19
+ read_only_fields :quantity_remaining
20
+
19
21
  record_refs :department, :item, :job, :location, :price, :rev_rec_schedule, :ship_address, :ship_method, :tax_code, :units, :klass
20
22
 
21
23
  def initialize(attributes_or_record = {})
@@ -0,0 +1,7 @@
1
+ module NetSuite
2
+ module Records
3
+ class KitItem < InventoryItem
4
+
5
+ end
6
+ end
7
+ end
@@ -32,6 +32,7 @@ module NetSuite
32
32
 
33
33
  field :pricing_matrix, PricingMatrix
34
34
  field :custom_field_list, CustomFieldList
35
+ field :pricing_matrix, PricingMatrix
35
36
 
36
37
  attr_reader :internal_id
37
38
  attr_accessor :external_id
@@ -7,7 +7,7 @@ module NetSuite
7
7
  include Support::Actions
8
8
  include Namespaces::TranSales
9
9
 
10
- actions :get, :add, :initialize, :delete, :update
10
+ actions :get, :add, :initialize, :delete, :update, :search
11
11
 
12
12
  fields :alt_handling_cost, :alt_shipping_cost, :amount_paid, :amount_remaining, :auto_apply, :balance,
13
13
  :bill_address, :contrib_pct, :created_date, :currency_name, :deferred_revenue, :discount_rate, :email, :end_date,
@@ -0,0 +1,29 @@
1
+ module NetSuite
2
+ module Records
3
+ class Task
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::ActSched
9
+
10
+ actions :get, :add, :delete, :update
11
+
12
+ fields :title, :send_email, :message, :status, :access_level, :reminder_type,
13
+ :reminder_minutes, :start_date, :end_date, :due_date, :timed_event
14
+
15
+ field :contact_list, ContactList
16
+
17
+ record_refs :assigned, :owner, :company, :contact
18
+
19
+ attr_reader :internal_id
20
+ attr_accessor :external_id
21
+
22
+ def initialize(attributes = {})
23
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
24
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
25
+ initialize_from_attributes_hash(attributes)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module NetSuite
2
+ module Records
3
+ class Transaction
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::TranSales
9
+
10
+ actions :get, :search, :search_more_with_id, :initialize, :add, :delete
11
+
12
+ attr_reader :internal_id
13
+ attr_accessor :external_id
14
+
15
+ def initialize(attributes = {})
16
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
17
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
18
+ initialize_from_attributes_hash(attributes)
19
+ end
20
+
21
+ def self.custom_soap_advanced_search_record_type
22
+ 'tranSales:TransactionSearchAdvanced'
23
+ end
24
+
25
+ def to_record
26
+ rec = super
27
+ if rec["#{record_namespace}:customFieldList"]
28
+ rec["#{record_namespace}:customFieldList!"] = rec.delete("#{record_namespace}:customFieldList")
29
+ end
30
+ rec
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -1,9 +1,10 @@
1
1
  module NetSuite
2
2
  class Response
3
- attr_accessor :body
3
+ attr_accessor :header, :body
4
4
 
5
5
  def initialize(attributes = {})
6
6
  @success = attributes[:success]
7
+ @header = attributes[:header]
7
8
  @body = attributes[:body]
8
9
  end
9
10
 
@@ -18,6 +18,10 @@ module NetSuite
18
18
  case name
19
19
  when :get
20
20
  self.send(:include, NetSuite::Actions::Get::Support)
21
+ when :search
22
+ self.send(:include, NetSuite::Actions::Search::Support)
23
+ when :search_more_with_id
24
+ self.send(:include, NetSuite::Actions::SearchMoreWithId::Support)
21
25
  when :add
22
26
  self.send(:include, NetSuite::Actions::Add::Support)
23
27
  when :delete
@@ -28,7 +28,11 @@ module NetSuite
28
28
  end
29
29
 
30
30
  define_method("#{name_sym}=") do |value|
31
- attributes[name_sym] = value.kind_of?(klass) ? value : klass.new(value)
31
+ if value.nil?
32
+ attributes.delete(name_sym)
33
+ else
34
+ attributes[name_sym] = value.kind_of?(klass) ? value : klass.new(value)
35
+ end
32
36
  end
33
37
  else
34
38
  define_method(name_sym) do
@@ -57,6 +61,12 @@ module NetSuite
57
61
  field name
58
62
  end
59
63
 
64
+ # a bit of trickery: this is for classes which inherit from other classes
65
+ # i.e. the AssemblyItem, KitItem, etc; this copies the superclass's fields over
66
+ def inherited(klass)
67
+ klass.instance_variable_set("@fields", self.fields)
68
+ klass.instance_variable_set("@read_only_fields", self.read_only_fields)
69
+ end
60
70
  end
61
71
 
62
72
  end
@@ -34,13 +34,18 @@ module NetSuite
34
34
  end
35
35
 
36
36
  def build_response
37
- Response.new(:success => success?, :body => response_body)
37
+ Response.new(:success => success?, header: response_header, :body => response_body)
38
38
  end
39
39
 
40
40
  def success?
41
41
  raise NotImplementedError, 'Please implement a #success? method'
42
42
  end
43
43
 
44
+ # Only care about headers in Search class for now
45
+ def response_header
46
+ nil
47
+ end
48
+
44
49
  def response_body
45
50
  raise NotImplementedError, 'Please implement a #response_body method'
46
51
  end
@@ -0,0 +1,67 @@
1
+ module NetSuite
2
+ module Support
3
+ class SearchResult
4
+ attr_accessor :response
5
+ attr_reader :total_records
6
+
7
+ # header from a basic customer search:
8
+
9
+ # <platformCore:searchResult xmlns:platformCore="urn:core_2012_1.platform.webservices.netsuite.com">
10
+ # <platformCore:status isSuccess="true"/>
11
+ # <platformCore:totalRecords>2770</platformCore:totalRecords>
12
+ # <platformCore:pageSize>5</platformCore:pageSize>
13
+ # <platformCore:totalPages>554</platformCore:totalPages>
14
+ # <platformCore:pageIndex>1</platformCore:pageIndex>
15
+ # <platformCore:searchId>WEBSERVICES_738944_SB2_03012013650784545962753432_28d96bd280</platformCore:searchId>
16
+
17
+ def initialize(response, result_class)
18
+ @response = response
19
+ @total_records = response.body[:total_records].to_i
20
+
21
+ if @total_records > 0
22
+ if @total_records == 1
23
+ [response.body[:record_list][:record]]
24
+ else
25
+ response.body[:record_list][:record]
26
+ end.each do |record|
27
+ results << result_class.new(record)
28
+ end
29
+ end
30
+
31
+ # search_results = []
32
+
33
+ # if !!response.body[:search_row_list] && !response.body[:search_row_list].empty?
34
+ # response.body[:search_row_list][:search_row].each do |record|
35
+ # search_result = NetSuite::Support::SearchResult.new(record)
36
+
37
+ # search_results << search_result
38
+ # end
39
+ # end
40
+
41
+ # search_id = response.header[:ns_id]
42
+ # page_index = response.body[:page_index]
43
+ # total_pages = response.body[:total_pages]
44
+
45
+ # response_hash[:search_id] = search_id
46
+ # response_hash[:page_index] = page_index
47
+ # response_hash[:total_pages] = total_pages
48
+ # response_hash[:search_results] = search_results
49
+ # puts response.body
50
+ # response_hash
51
+ end
52
+
53
+ def results
54
+ @results ||= []
55
+ end
56
+
57
+ def next_page
58
+
59
+ end
60
+
61
+ def all_pages
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -1,3 +1,3 @@
1
1
  module Netsuite
2
- VERSION = '0.0.49'
2
+ VERSION = '0.0.50'
3
3
  end
@@ -38,6 +38,14 @@ describe NetSuite::Configuration do
38
38
  config.wsdl.should match(/.*\/netsuite\/wsdl\/2011_2\.wsdl/)
39
39
  end
40
40
  end
41
+
42
+ context 'when the wsdl has not been set, but the API has been set' do
43
+ it 'should correctly return the full HTTP sandbox URL' do
44
+ config.api_version '2013_1'
45
+ config.sandbox false
46
+ config.wsdl.should eql('https://webservices.netsuite.com/wsdl/v2013_1_0/netsuite.wsdl')
47
+ end
48
+ end
41
49
  end
42
50
 
43
51
  describe '#auth_header' do
@@ -23,8 +23,9 @@ describe NetSuite::Records::NonInventorySaleItem do
23
23
  item.should have_field(field)
24
24
  end
25
25
 
26
- item.pricing_matrix.class.should == NetSuite::Records::PricingMatrix
26
+ # TODO there is a probably a more robust way to test this
27
27
  item.custom_field_list.class.should == NetSuite::Records::CustomFieldList
28
+ item.pricing_matrix.class.should == NetSuite::Records::PricingMatrix
28
29
  end
29
30
 
30
31
  it 'has the right record_refs' do
@@ -1,5 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe NetSuite::Support::RecordRefs do
4
- pending
4
+ let(:klass) { Class.new.send(:include, NetSuite::Support::Fields) }
5
+ let(:instance) { klass.new }
6
+
7
+ context "assigning record reference" do
8
+ it 'assigns a nil value for a field with a specified class' do
9
+ klass.field(:record_test, NetSuite::Records::RecordRef)
10
+ instance.record_test = nil
11
+ instance.attributes.has_key?(:record_test).should be_false
12
+ end
13
+ end
5
14
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: netsuite
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.49
5
+ version: 0.0.50
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ryan Moran
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-19 00:00:00.000000000 Z
13
+ date: 2013-04-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  version_requirements: !ruby/object:Gem::Requirement
@@ -112,10 +112,13 @@ files:
112
112
  - lib/netsuite/actions/delete.rb
113
113
  - lib/netsuite/actions/get.rb
114
114
  - lib/netsuite/actions/initialize.rb
115
+ - lib/netsuite/actions/search.rb
116
+ - lib/netsuite/actions/search_more_with_id.rb
115
117
  - lib/netsuite/actions/update.rb
116
118
  - lib/netsuite/configuration.rb
117
119
  - lib/netsuite/core_ext/string/lower_camelcase.rb
118
120
  - lib/netsuite/errors.rb
121
+ - lib/netsuite/namespaces/act_sched.rb
119
122
  - lib/netsuite/namespaces/list_acct.rb
120
123
  - lib/netsuite/namespaces/list_rel.rb
121
124
  - lib/netsuite/namespaces/platform_common.rb
@@ -128,7 +131,10 @@ files:
128
131
  - lib/netsuite/records/accounting_period.rb
129
132
  - lib/netsuite/records/assembly_item.rb
130
133
  - lib/netsuite/records/bill_address.rb
134
+ - lib/netsuite/records/bin_number_list.rb
131
135
  - lib/netsuite/records/classification.rb
136
+ - lib/netsuite/records/contact.rb
137
+ - lib/netsuite/records/contact_list.rb
132
138
  - lib/netsuite/records/credit_memo.rb
133
139
  - lib/netsuite/records/credit_memo_apply.rb
134
140
  - lib/netsuite/records/credit_memo_apply_list.rb
@@ -158,6 +164,7 @@ files:
158
164
  - lib/netsuite/records/journal_entry.rb
159
165
  - lib/netsuite/records/journal_entry_line.rb
160
166
  - lib/netsuite/records/journal_entry_line_list.rb
167
+ - lib/netsuite/records/kit_item.rb
161
168
  - lib/netsuite/records/location.rb
162
169
  - lib/netsuite/records/non_inventory_sale_item.rb
163
170
  - lib/netsuite/records/payment_method.rb
@@ -168,7 +175,9 @@ files:
168
175
  - lib/netsuite/records/sales_order_item.rb
169
176
  - lib/netsuite/records/sales_order_item_list.rb
170
177
  - lib/netsuite/records/ship_address.rb
178
+ - lib/netsuite/records/task.rb
171
179
  - lib/netsuite/records/term.rb
180
+ - lib/netsuite/records/transaction.rb
172
181
  - lib/netsuite/response.rb
173
182
  - lib/netsuite/support/actions.rb
174
183
  - lib/netsuite/support/attributes.rb
@@ -176,6 +185,7 @@ files:
176
185
  - lib/netsuite/support/record_refs.rb
177
186
  - lib/netsuite/support/records.rb
178
187
  - lib/netsuite/support/requests.rb
188
+ - lib/netsuite/support/search_result.rb
179
189
  - lib/netsuite/version.rb
180
190
  - lib/netsuite/xml_logger.rb
181
191
  - netsuite.gemspec