netsuite 0.0.49 → 0.0.50
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.
- data/README.md +47 -103
- data/lib/netsuite.rb +16 -6
- data/lib/netsuite/actions/add.rb +3 -0
- data/lib/netsuite/actions/search.rb +126 -0
- data/lib/netsuite/actions/search_more_with_id.rb +106 -0
- data/lib/netsuite/actions/update.rb +1 -0
- data/lib/netsuite/configuration.rb +52 -3
- data/lib/netsuite/namespaces/act_sched.rb +11 -0
- data/lib/netsuite/records/assembly_item.rb +2 -0
- data/lib/netsuite/records/bin_number_list.rb +48 -0
- data/lib/netsuite/records/contact.rb +44 -0
- data/lib/netsuite/records/contact_list.rb +52 -0
- data/lib/netsuite/records/custom_field.rb +4 -0
- data/lib/netsuite/records/custom_field_list.rb +11 -2
- data/lib/netsuite/records/custom_record_ref.rb +2 -0
- data/lib/netsuite/records/customer.rb +1 -1
- data/lib/netsuite/records/inventory_item.rb +3 -1
- data/lib/netsuite/records/invoice.rb +5 -4
- data/lib/netsuite/records/invoice_item.rb +3 -1
- data/lib/netsuite/records/kit_item.rb +7 -0
- data/lib/netsuite/records/non_inventory_sale_item.rb +1 -0
- data/lib/netsuite/records/sales_order.rb +1 -1
- data/lib/netsuite/records/task.rb +29 -0
- data/lib/netsuite/records/transaction.rb +35 -0
- data/lib/netsuite/response.rb +2 -1
- data/lib/netsuite/support/actions.rb +4 -0
- data/lib/netsuite/support/fields.rb +11 -1
- data/lib/netsuite/support/requests.rb +6 -1
- data/lib/netsuite/support/search_result.rb +67 -0
- data/lib/netsuite/version.rb +1 -1
- data/spec/netsuite/configuration_spec.rb +8 -0
- data/spec/netsuite/records/non_inventory_sale_item_spec.rb +2 -1
- data/spec/netsuite/support/record_refs_spec.rb +10 -1
- 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.
|
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/
|
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
|
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
|
46
|
-
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
|
-
###
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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,
|
35
|
-
autoload :Delete,
|
36
|
-
autoload :Get,
|
37
|
-
autoload :Initialize,
|
38
|
-
autoload :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::
|
102
|
+
config.logger = NetSuite::Configuration.logger
|
93
103
|
end
|
94
104
|
end
|
95
105
|
|
data/lib/netsuite/actions/add.rb
CHANGED
@@ -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]
|
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
|
-
|
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
|
@@ -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
|
@@ -6,9 +6,9 @@ module NetSuite
|
|
6
6
|
def initialize(attributes = {})
|
7
7
|
case attributes[:custom_field]
|
8
8
|
when Hash
|
9
|
-
|
9
|
+
extract_custom_field(attributes[:custom_field])
|
10
10
|
when Array
|
11
|
-
attributes[:custom_field].each { |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
|
@@ -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,
|
@@ -9,13 +9,13 @@ module NetSuite
|
|
9
9
|
|
10
10
|
actions :get, :initialize, :add, :delete
|
11
11
|
|
12
|
-
fields :alt_handling_cost, :
|
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, :
|
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,
|
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, :
|
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 = {})
|
@@ -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
|
data/lib/netsuite/response.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/netsuite/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
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-
|
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
|