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