netsuite 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/Gemfile +0 -2
  2. data/README.md +154 -0
  3. data/lib/netsuite.rb +7 -0
  4. data/lib/netsuite/actions/add.rb +6 -21
  5. data/lib/netsuite/actions/delete.rb +7 -10
  6. data/lib/netsuite/actions/get.rb +7 -10
  7. data/lib/netsuite/actions/get_list.rb +77 -0
  8. data/lib/netsuite/actions/get_select_value.rb +71 -0
  9. data/lib/netsuite/actions/search.rb +151 -36
  10. data/lib/netsuite/actions/search_more_with_id.rb +3 -2
  11. data/lib/netsuite/actions/update.rb +16 -16
  12. data/lib/netsuite/configuration.rb +29 -15
  13. data/lib/netsuite/namespaces/list_support.rb +11 -0
  14. data/lib/netsuite/records/account.rb +1 -0
  15. data/lib/netsuite/records/base_ref_list.rb +33 -0
  16. data/lib/netsuite/records/cash_sale.rb +7 -0
  17. data/lib/netsuite/records/custom_field.rb +2 -1
  18. data/lib/netsuite/records/custom_field_list.rb +62 -12
  19. data/lib/netsuite/records/custom_record.rb +1 -7
  20. data/lib/netsuite/records/custom_record_ref.rb +7 -0
  21. data/lib/netsuite/records/customer.rb +4 -3
  22. data/lib/netsuite/records/phone_call.rb +29 -0
  23. data/lib/netsuite/records/record_ref.rb +1 -9
  24. data/lib/netsuite/records/sales_order.rb +2 -1
  25. data/lib/netsuite/records/support_case.rb +32 -0
  26. data/lib/netsuite/records/task.rb +4 -3
  27. data/lib/netsuite/records/transaction.rb +4 -21
  28. data/lib/netsuite/support/actions.rb +4 -0
  29. data/lib/netsuite/support/records.rb +3 -0
  30. data/lib/netsuite/support/search_result.rb +53 -29
  31. data/lib/netsuite/version.rb +1 -1
  32. data/netsuite.gemspec +1 -1
  33. data/spec/netsuite/actions/add_spec.rb +9 -13
  34. data/spec/netsuite/actions/delete_spec.rb +5 -8
  35. data/spec/netsuite/actions/get_spec.rb +39 -24
  36. data/spec/netsuite/actions/search_spec.rb +149 -0
  37. data/spec/netsuite/actions/update_spec.rb +9 -13
  38. data/spec/netsuite/configuration_spec.rb +3 -9
  39. data/spec/netsuite/records/custom_field_list_spec.rb +19 -5
  40. data/spec/netsuite/records/customer_spec.rb +2 -2
  41. data/spec/netsuite/records/phone_call_spec.rb +23 -0
  42. data/spec/netsuite/records/support_case_spec.rb +157 -0
  43. data/spec/support/fixtures/search/saved_search_customer.xml +54 -0
  44. data/spec/support/fixtures/search/saved_search_joined_custom_customer.xml +87 -0
  45. metadata +21 -4
@@ -7,7 +7,7 @@ module NetSuite
7
7
  include Support::Actions
8
8
  include Namespaces::SetupCustom
9
9
 
10
- actions :get, :add, :delete
10
+ actions :get, :get_list, :add, :delete, :search
11
11
 
12
12
  fields :allow_attachments, :allow_inline_editing, :allow_numbering_override, :allow_quick_search, :created,
13
13
  :custom_record_id, :description, :disclaimer, :enabl_email_merge, :enable_numbering, :include_name,
@@ -50,12 +50,6 @@ module NetSuite
50
50
  "#{record_namespace}:CustomRecord"
51
51
  end
52
52
 
53
- def to_record
54
- rec = super
55
- rec["#{record_namespace}:customFieldList!"] = rec.delete("#{record_namespace}:customFieldList")
56
- rec
57
- end
58
-
59
53
  end
60
54
  end
61
55
  end
@@ -37,6 +37,13 @@ module NetSuite
37
37
  end
38
38
  end
39
39
 
40
+ def to_record
41
+ rec = super
42
+ rec[:@internalId] = @internal_id if @internal_id
43
+ rec[:@typeId] = @type_id if @type_id
44
+ rec
45
+ end
46
+
40
47
  end
41
48
  end
42
49
  end
@@ -7,7 +7,7 @@ module NetSuite
7
7
  include Support::Actions
8
8
  include Namespaces::ListRel
9
9
 
10
- actions :get, :add, :update, :delete, :search
10
+ actions :get, :get_list, :add, :update, :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,
@@ -16,9 +16,9 @@ module NetSuite
16
16
  :download_list, :email, :email_preference, :email_transactions, :end_date, :entity_id,
17
17
  :estimated_budget, :fax, :fax_transactions, :first_name, :first_visit, :give_access, :global_subscription_status,
18
18
  :group_pricing_list, :home_phone, :image, :is_budget_approved, :is_inactive, :is_person, :item_pricing_list, :keywords,
19
- :language, :last_modified, :last_name, :last_page_visited, :last_visit, :lead_source, :middle_name, :mobile_phone,
19
+ :language, :last_modified_date, :last_name, :last_page_visited, :last_visit, :lead_source, :middle_name, :mobile_phone,
20
20
  :opening_balance, :opening_balance_account, :opening_balance_date, :parent, :partners_list,
21
- :password, :password_2, :phone, :phonetic_name, :pref_cc_processor, :price_level, :print_on_check_as,
21
+ :password, :password2, :phone, :phonetic_name, :pref_cc_processor, :price_level, :print_on_check_as,
22
22
  :print_transactions, :referrer, :reminder_days, :representing_subsidiary, :require_pwd_change, :resale_number,
23
23
  :sales_group, :sales_readiness, :sales_rep, :sales_team_list, :salutation, :send_email, :ship_complete, :shipping_item,
24
24
  :stage, :start_date, :subscriptions_list, :subsidiary, :sync_partner_teams, :tax_exempt, :tax_item, :taxable, :terms,
@@ -35,6 +35,7 @@ module NetSuite
35
35
 
36
36
  attr_reader :internal_id
37
37
  attr_accessor :external_id
38
+ attr_accessor :search_joins
38
39
 
39
40
  def initialize(attributes = {})
40
41
  @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
@@ -0,0 +1,29 @@
1
+ module NetSuite
2
+ module Records
3
+ class PhoneCall
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, :message, :phone, :status, :priority, :start_date, :end_date,
13
+ :start_time, :end_time, :completed_date, :timed_event, :access_level, :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
@@ -15,7 +15,7 @@ module NetSuite
15
15
  attributes.delete(:"@xmlns:platform_core")
16
16
  @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
17
17
  @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
18
- @type = attributes.delete(:type) || attributes.delete(:@type)
18
+ @type = attributes.delete(:type) || attributes.delete(:@type) || attributes.delete(:"@xsi:type")
19
19
  @attributes = attributes
20
20
  else
21
21
  record = attributes_or_record
@@ -33,14 +33,6 @@ module NetSuite
33
33
  end
34
34
  end
35
35
 
36
- def attributes!
37
- hash = {}
38
- hash[:internalId] = @internal_id if @internal_id
39
- hash[:externalId] = @external_id if @external_id
40
- hash[:type] = @type if @type
41
- hash
42
- end
43
-
44
36
  end
45
37
  end
46
38
  end
@@ -27,10 +27,11 @@ module NetSuite
27
27
 
28
28
  record_refs :account, :bill_address_list, :created_from, :currency, :custom_form, :department, :discount_item, :entity, :gift_cert,
29
29
  :handling_tax_code, :job, :klass, :lead_source, :location, :message_sel, :opportunity, :partner, :posting_period, :promo_code,
30
- :sales_group, :sales_rep, :ship_method, :shipping_tax_code, :subsidiary, :tax_item
30
+ :sales_group, :sales_rep, :ship_method, :shipping_tax_code, :subsidiary, :terms, :tax_item
31
31
 
32
32
  attr_reader :internal_id
33
33
  attr_accessor :external_id
34
+ attr_accessor :search_joins
34
35
 
35
36
  def initialize(attributes = {})
36
37
  @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
@@ -0,0 +1,32 @@
1
+ module NetSuite
2
+ module Records
3
+ class SupportCase
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::ListSupport
9
+
10
+ actions :get, :add, :delete, :update
11
+
12
+ fields :end_date, :incoming_message, :outgoing_message, :search_solution, :email_form,
13
+ :internal_only, :title, :case_number, :start_date, :email, :phone, :inbound_email,
14
+ :is_inactive, :help_desk
15
+
16
+ field :custom_field_list, CustomFieldList
17
+
18
+ read_only_fields :created_date, :last_modified_date, :last_message_date
19
+
20
+ record_refs :custom_form, :company, :contact, :issue, :status, :priority, :origin, :category, :assigned
21
+
22
+ attr_reader :internal_id
23
+ attr_accessor :external_id
24
+
25
+ def initialize(attributes = {})
26
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
27
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
28
+ initialize_from_attributes_hash(attributes)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -7,14 +7,15 @@ module NetSuite
7
7
  include Support::Actions
8
8
  include Namespaces::ActSched
9
9
 
10
- actions :get, :add, :delete, :update
10
+ actions :get, :add, :search, :delete, :update
11
11
 
12
12
  fields :title, :send_email, :message, :status, :access_level, :reminder_type,
13
- :reminder_minutes, :start_date, :end_date, :due_date, :timed_event
13
+ :reminder_minutes, :start_date, :end_date, :due_date, :timed_event,
14
+ :created_date, :last_modified_date
14
15
 
15
16
  field :contact_list, ContactList
16
17
 
17
- record_refs :assigned, :owner, :company, :contact
18
+ record_refs :assigned, :owner, :company, :contact, :transaction
18
19
 
19
20
  attr_reader :internal_id
20
21
  attr_accessor :external_id
@@ -1,26 +1,9 @@
1
+ # TODO specs
2
+ # TODO a transaction is actually the superclass of a SalesOrder, this is backwards
3
+
1
4
  module NetSuite
2
5
  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
6
+ class Transaction < SalesOrder
24
7
 
25
8
  def to_record
26
9
  rec = super
@@ -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 :get_list
22
+ self.send(:include, NetSuite::Actions::GetList::Support)
23
+ when :get_select_value
24
+ self.send(:include, NetSuite::Actions::GetSelectValue::Support)
21
25
  when :search
22
26
  self.send(:include, NetSuite::Actions::Search::Support)
23
27
  when :search_more_with_id
@@ -28,16 +28,19 @@ module NetSuite
28
28
  hash[:attributes!][kname] ||= {}
29
29
  hash[:attributes!][kname]['internalId'] = v.internal_id
30
30
  end
31
+
31
32
  if v.respond_to?(:external_id) && v.external_id
32
33
  hash[:attributes!] ||= {}
33
34
  hash[:attributes!][kname] ||= {}
34
35
  hash[:attributes!][kname]['externalId'] = v.external_id
35
36
  end
37
+
36
38
  if v.kind_of?(NetSuite::Records::RecordRef) && v.type
37
39
  hash[:attributes!] ||= {}
38
40
  hash[:attributes!][kname] ||= {}
39
41
  hash[:attributes!][kname]['type'] = v.type.lower_camelcase
40
42
  end
43
+
41
44
  if v.kind_of?(NetSuite::Records::CustomRecordRef) && v.type_id
42
45
  hash[:attributes!] ||= {}
43
46
  hash[:attributes!][kname] ||= {}
@@ -15,51 +15,75 @@ module NetSuite
15
15
  # <platformCore:searchId>WEBSERVICES_738944_SB2_03012013650784545962753432_28d96bd280</platformCore:searchId>
16
16
 
17
17
  def initialize(response, result_class)
18
+ @result_class = result_class
18
19
  @response = response
19
20
  @total_records = response.body[:total_records].to_i
20
21
 
21
22
  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
23
+ if response.body.has_key?(:record_list)
24
+ # basic search results
25
+ record_list = response.body[:record_list][:record]
26
+ record_list = [record_list] if @total_records == 1
30
27
 
31
- # search_results = []
28
+ record_list.each do |record|
29
+ results << result_class.new(record)
30
+ end
31
+ elsif response.body.has_key? :search_row_list
32
+ # advanced search results
33
+ record_list = response.body[:search_row_list][:search_row]
34
+ record_list = [record_list] if @total_records == 1
32
35
 
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
+ record_list.each do |record|
37
+ # TODO because of customFieldList we need to either make this recursive
38
+ # or handle the customFieldList as a special case
36
39
 
37
- # search_results << search_result
38
- # end
39
- # end
40
+ record.each_pair do |search_group, search_data|
41
+ # skip all attributes: look for :basic and all :xxx_join
42
+ next if search_group.to_s.start_with?('@')
40
43
 
41
- # search_id = response.header[:ns_id]
42
- # page_index = response.body[:page_index]
43
- # total_pages = response.body[:total_pages]
44
+ record[search_group].each_pair do |k, v|
45
+ # all return values are wrapped in a <SearchValue/>
46
+ # extract the value from <SearchValue/> to make results easier to work with
44
47
 
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
48
+ if v.is_a?(Hash) && v.has_key?(:search_value)
49
+ # search return values that are just internalIds are stored as attributes on the searchValue element
50
+ if v[:search_value].is_a?(Hash) && v[:search_value].has_key?(:'@internal_id')
51
+ record[search_group][k] = v[:search_value][:'@internal_id']
52
+ else
53
+ record[search_group][k] = v[:search_value]
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ result_wrapper = result_class.new(record.delete(:basic))
60
+ result_wrapper.search_joins = record
61
+ results << result_wrapper
62
+ end
63
+ else
64
+ raise "uncaught search result"
65
+ end
66
+ end
51
67
  end
52
68
 
53
69
  def results
54
70
  @results ||= []
55
71
  end
56
72
 
57
- def next_page
58
-
59
- end
73
+ def results_in_batches
74
+ while @response.body[:total_pages] != @response.body[:page_index]
75
+ yield results
76
+
77
+ next_search = @result_class.search(
78
+ search_id: @response.body[:search_id],
79
+ page_index: @response.body[:page_index].to_i + 1
80
+ )
81
+
82
+ @results = next_search.results
83
+ @response = next_search.response
84
+ end
60
85
 
61
- def all_pages
62
-
86
+ yield results
63
87
  end
64
88
 
65
89
  end
@@ -1,3 +1,3 @@
1
1
  module Netsuite
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ['lib']
16
16
  gem.version = Netsuite::VERSION
17
17
 
18
- gem.add_dependency 'savon', '~> 2.2.0'
18
+ gem.add_dependency 'savon', '~> 2.3.0'
19
19
 
20
20
  gem.add_development_dependency 'rspec', '~> 2.10'
21
21
  end
@@ -12,14 +12,12 @@ describe NetSuite::Actions::Add do
12
12
  before do
13
13
  savon.expects(:add).with(:message => {
14
14
  'platformMsgs:record' => {
15
- 'listRel:entityId' => 'Shutter Fly',
16
- 'listRel:companyName' => 'Shutter Fly, Inc.'
15
+ :content! => {
16
+ 'listRel:entityId' => 'Shutter Fly',
17
+ 'listRel:companyName' => 'Shutter Fly, Inc.'
18
+ },
19
+ '@xsi:type' => 'listRel:Customer'
17
20
  },
18
- :attributes! => {
19
- 'platformMsgs:record' => {
20
- 'xsi:type' => 'listRel:Customer'
21
- }
22
- }
23
21
  }).returns(File.read('spec/support/fixtures/add/add_customer.xml'))
24
22
  end
25
23
 
@@ -42,13 +40,11 @@ describe NetSuite::Actions::Add do
42
40
  before do
43
41
  savon.expects(:add).with(:message => {
44
42
  'platformMsgs:record' => {
45
- 'tranSales:source' => 'Google'
43
+ :content! => {
44
+ 'tranSales:source' => 'Google'
45
+ },
46
+ '@xsi:type' => 'tranSales:Invoice'
46
47
  },
47
- :attributes! => {
48
- 'platformMsgs:record' => {
49
- 'xsi:type' => 'tranSales:Invoice'
50
- }
51
- }
52
48
  }).returns(File.read('spec/support/fixtures/add/add_invoice.xml'))
53
49
  end
54
50
 
@@ -11,14 +11,11 @@ describe NetSuite::Actions::Delete do
11
11
 
12
12
  before do
13
13
  savon.expects(:delete).with(:message => {
14
- 'platformMsgs:baseRef' => {},
15
- :attributes! => {
16
- 'platformMsgs:baseRef' => {
17
- 'internalId' => '980',
18
- 'type' => 'customer',
19
- 'xsi:type' => 'platformCore:RecordRef'
20
- }
21
- }
14
+ 'platformMsgs:baseRef' => {
15
+ '@internalId' => '980',
16
+ '@type' => 'customer',
17
+ '@xsi:type' => 'platformCore:RecordRef'
18
+ },
22
19
  }).returns(File.read('spec/support/fixtures/delete/delete_customer.xml'))
23
20
  end
24
21
 
@@ -5,42 +5,57 @@ describe NetSuite::Actions::Get do
5
5
  after(:all) { savon.unmock! }
6
6
 
7
7
  describe 'Customer' do
8
- before do
9
- message = {
10
- 'platformMsgs:baseRef' => {},
11
- :attributes! => {
8
+ context 'retrieving with externalId' do
9
+ before do
10
+ message = {
12
11
  'platformMsgs:baseRef' => {
13
- 'externalId' => 1,
14
- 'type' => 'customer',
15
- 'xsi:type' => 'platformCore:RecordRef'
16
- }
12
+ '@externalId' => 1,
13
+ '@type' => 'customer',
14
+ '@xsi:type' => 'platformCore:RecordRef'
15
+ },
17
16
  }
18
- }
19
17
 
20
- savon.expects(:get).with(:message => message).returns(File.read('spec/support/fixtures/get/get_customer.xml'))
21
- end
18
+ savon.expects(:get).with(:message => message).returns(File.read('spec/support/fixtures/get/get_customer.xml'))
19
+ end
22
20
 
23
- it 'makes a valid request to the NetSuite API' do
24
- NetSuite::Actions::Get.call(NetSuite::Records::Customer, :external_id => 1)
21
+ it 'makes a valid request to the NetSuite API' do
22
+ NetSuite::Actions::Get.call(NetSuite::Records::Customer, :external_id => 1)
23
+ end
24
+
25
+ it 'returns a valid Response object' do
26
+ response = NetSuite::Actions::Get.call(NetSuite::Records::Customer, :external_id => 1)
27
+ response.should be_kind_of(NetSuite::Response)
28
+ end
25
29
  end
26
30
 
27
- it 'returns a valid Response object' do
28
- response = NetSuite::Actions::Get.call(NetSuite::Records::Customer, :external_id => 1)
29
- response.should be_kind_of(NetSuite::Response)
31
+ context "retrieving with internalId" do
32
+ before do
33
+ message = {
34
+ 'platformMsgs:baseRef' => {
35
+ '@internalId' => 1,
36
+ '@type' => 'customer',
37
+ '@xsi:type' => 'platformCore:RecordRef'
38
+ },
39
+ }
40
+
41
+ savon.expects(:get).with(:message => message).returns(File.read('spec/support/fixtures/get/get_customer.xml'))
42
+ end
43
+
44
+ it "makes a valid request to the NetSuite API" do
45
+ customer = NetSuite::Records::Customer.get(1)
46
+ customer.balance.should == '100.0'
47
+ end
30
48
  end
31
49
  end
32
50
 
33
51
  describe 'Invoice' do
34
52
  before do
35
53
  savon.expects(:get).with(:message => {
36
- 'platformMsgs:baseRef' => {},
37
- :attributes! => {
38
- 'platformMsgs:baseRef' => {
39
- 'externalId' => 1,
40
- 'type' => 'invoice',
41
- 'xsi:type' => 'platformCore:RecordRef'
42
- }
43
- }
54
+ 'platformMsgs:baseRef' => {
55
+ '@externalId' => 1,
56
+ '@type' => 'invoice',
57
+ '@xsi:type' => 'platformCore:RecordRef'
58
+ },
44
59
  }).returns(File.read('spec/support/fixtures/get/get_invoice.xml'))
45
60
  end
46
61