netsuite 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +69 -20
  3. data/lib/netsuite.rb +11 -0
  4. data/lib/netsuite/actions/search.rb +1 -6
  5. data/lib/netsuite/actions/update_list.rb +109 -0
  6. data/lib/netsuite/records/bin_transfer.rb +38 -0
  7. data/lib/netsuite/records/bin_transfer_inventory.rb +20 -0
  8. data/lib/netsuite/records/bin_transfer_inventory_list.rb +10 -0
  9. data/lib/netsuite/records/cash_refund_item.rb +1 -1
  10. data/lib/netsuite/records/classification.rb +1 -1
  11. data/lib/netsuite/records/inbound_shipment.rb +33 -0
  12. data/lib/netsuite/records/inbound_shipment_item.rb +39 -0
  13. data/lib/netsuite/records/inbound_shipment_item_list.rb +11 -0
  14. data/lib/netsuite/records/inter_company_journal_entry.rb +48 -0
  15. data/lib/netsuite/records/inter_company_journal_entry_line.rb +28 -0
  16. data/lib/netsuite/records/inter_company_journal_entry_line_list.rb +14 -0
  17. data/lib/netsuite/records/inventory_item.rb +1 -1
  18. data/lib/netsuite/records/price_level.rb +26 -0
  19. data/lib/netsuite/records/return_authorization_item.rb +1 -1
  20. data/lib/netsuite/records/sales_order_item.rb +12 -5
  21. data/lib/netsuite/records/support_case.rb +1 -1
  22. data/lib/netsuite/support/actions.rb +2 -0
  23. data/lib/netsuite/support/search_result.rb +7 -3
  24. data/lib/netsuite/utilities.rb +26 -0
  25. data/lib/netsuite/version.rb +1 -1
  26. data/spec/netsuite/actions/update_list_spec.rb +107 -0
  27. data/spec/netsuite/records/basic_record_spec.rb +5 -1
  28. data/spec/netsuite/records/inter_company_journal_entry_line_list_spec.rb +26 -0
  29. data/spec/netsuite/records/inter_company_journal_entry_line_spec.rb +60 -0
  30. data/spec/netsuite/records/inter_company_journal_entry_spec.rb +156 -0
  31. data/spec/netsuite/records/inventory_item_spec.rb +57 -0
  32. data/spec/netsuite/records/price_level_spec.rb +16 -0
  33. data/spec/netsuite/records/return_authorization_item_spec.rb +1 -1
  34. data/spec/netsuite/records/sales_order_item_spec.rb +11 -5
  35. data/spec/netsuite/utilities_spec.rb +21 -0
  36. data/spec/support/fixtures/update_list/update_list_items.xml +22 -0
  37. data/spec/support/fixtures/update_list/update_list_one_item.xml +18 -0
  38. data/spec/support/fixtures/update_list/update_list_with_errors.xml +32 -0
  39. metadata +29 -2
@@ -0,0 +1,33 @@
1
+ module NetSuite
2
+ module Records
3
+ class InboundShipment
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::TranPurch
9
+
10
+ actions :get, :get_list, :add, :initialize, :delete, :update, :upsert, :upsert_list, :search, :update_list
11
+
12
+ fields :shipment_number, :external_document_number, :shipment_status, :expected_shipping_date,
13
+ :actual_shipping_date, :expected_delivery_date, :actual_delivery_date, :shipment_memo,
14
+ :vessel_number, :bill_of_lading
15
+
16
+ field :items_list, InboundShipmentItemList
17
+ field :custom_field_list, CustomFieldList
18
+
19
+ record_refs :custom_form
20
+
21
+ attr_reader :internal_id
22
+ attr_accessor :external_id
23
+ attr_accessor :search_joins
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
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ module NetSuite
2
+ module Records
3
+ class InboundShipmentItem
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Namespaces::TranPurch
8
+
9
+ fields :id, :shipment_item_description, :po_vendor, :quantity_received, :quantity_expected,
10
+ :quantity_remaining, :po_rate, :expected_rate, :shipment_item_amount
11
+
12
+ field :custom_field_list, CustomFieldList
13
+
14
+ record_refs :purchase_order, :shipment_item, :receiving_location, :po_currency, :incoterm
15
+
16
+ def initialize(attributes_or_record = {})
17
+ case attributes_or_record
18
+ when Hash
19
+ initialize_from_attributes_hash(attributes_or_record)
20
+ when self.class
21
+ initialize_from_record(attributes_or_record)
22
+ end
23
+ end
24
+
25
+ def initialize_from_record(record)
26
+ self.attributes = record.send(:attributes)
27
+ end
28
+
29
+ def to_record
30
+ rec = super
31
+ if rec["#{record_namespace}:customFieldList"]
32
+ rec["#{record_namespace}:customFieldList!"] = rec.delete("#{record_namespace}:customFieldList")
33
+ end
34
+ rec
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ module NetSuite
2
+ module Records
3
+ class InboundShipmentItemList < Support::Sublist
4
+ include Namespaces::TranPurch
5
+
6
+ sublist :inbound_shipment_items, InboundShipmentItem
7
+
8
+ alias :items_list :inbound_shipment_items
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ module NetSuite
2
+ module Records
3
+ class InterCompanyJournalEntry
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Support::Actions
8
+ include Namespaces::TranGeneral
9
+
10
+ actions :get, :get_list, :add, :delete, :search, :upsert
11
+
12
+ fields :approved, :created_date, :exchange_rate, :is_book_specific, :last_modified_date, :memo, :reversal_date, :reversal_defer,
13
+ :reversal_entry, :tran_date, :tran_id
14
+
15
+ field :custom_field_list, CustomFieldList
16
+ field :line_list, InterCompanyJournalEntryLineList
17
+
18
+ record_refs :created_from, :currency, :custom_form, :department, :klass, :location, :parent_expense_alloc,
19
+ :posting_period, :subsidiary, :to_subsidiary
20
+
21
+ attr_reader :internal_id
22
+ attr_accessor :external_id
23
+
24
+ def initialize(attributes = {})
25
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
26
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
27
+ initialize_from_attributes_hash(attributes)
28
+ end
29
+
30
+ def to_record
31
+ rec = super
32
+ if rec["#{record_namespace}:customFieldList"]
33
+ rec["#{record_namespace}:customFieldList!"] = rec.delete("#{record_namespace}:customFieldList")
34
+ end
35
+ rec
36
+ end
37
+
38
+ def self.search_class_name
39
+ "Transaction"
40
+ end
41
+
42
+ def self.search_class_namespace
43
+ "tranSales"
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ module NetSuite
2
+ module Records
3
+ class InterCompanyJournalEntryLine
4
+ include Support::Fields
5
+ include Support::RecordRefs
6
+ include Support::Records
7
+ include Namespaces::TranGeneral
8
+
9
+ fields :amortization_end_date, :amortization_residual, :amortiz_start_date, :credit, :debit, :eliminate, :end_date, :gross_amt, :memo,
10
+ :residual, :start_date, :tax1_amt, :tax_rate1
11
+ field :custom_field_list, CustomFieldList
12
+ record_refs :account, :department, :entity, :klass, :line_subsidiary, :location, :schedule, :schedule_num, :tax1_acct, :tax_code
13
+
14
+ def initialize(attributes = {})
15
+ initialize_from_attributes_hash(attributes)
16
+ end
17
+
18
+ def to_record
19
+ rec = super
20
+ if rec["#{record_namespace}:customFieldList"]
21
+ rec["#{record_namespace}:customFieldList!"] = rec.delete("#{record_namespace}:customFieldList")
22
+ end
23
+ rec
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ module NetSuite
2
+ module Records
3
+ class InterCompanyJournalEntryLineList < Support::Sublist
4
+ include Namespaces::TranGeneral
5
+
6
+ attr_accessor :replace_all
7
+
8
+ sublist :line, InterCompanyJournalEntryLine
9
+
10
+ alias :lines :line
11
+
12
+ end
13
+ end
14
+ end
@@ -20,7 +20,7 @@ module NetSuite
20
20
  # }
21
21
  # ]
22
22
  #
23
- actions :get, :get_list, :add, :delete, :search, :update, :upsert
23
+ actions :get, :get_list, :add, :delete, :search, :update, :upsert, :update_list
24
24
 
25
25
  fields :auto_lead_time, :auto_preferred_stock_level, :auto_reorder_point, :available_to_partners, :average_cost,
26
26
  :copy_description, :cost, :cost_estimate, :cost_estimate_type, :cost_estimate_units, :cost_units, :costing_method,
@@ -0,0 +1,26 @@
1
+ module NetSuite
2
+ module Records
3
+ class PriceLevel
4
+ include Support::Fields
5
+ include Support::Records
6
+ include Support::Actions
7
+ include Support::RecordRefs
8
+ include Namespaces::ListAcct
9
+
10
+ actions :get, :update, :get_list, :add, :delete, :search, :upsert
11
+
12
+ # http://www.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2017_1/schema/record/pricelevel.html
13
+ fields :discountpct, :name, :is_online, :update_existing_prices,
14
+ :is_inactive
15
+
16
+ attr_reader :internal_id
17
+ attr_accessor :external_id
18
+
19
+ def initialize(attributes = {})
20
+ @internal_id = attributes.delete(:internal_id) || attributes.delete(:@internal_id)
21
+ @external_id = attributes.delete(:external_id) || attributes.delete(:@external_id)
22
+ initialize_from_attributes_hash(attributes)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -10,7 +10,7 @@ module NetSuite
10
10
  :cost_estimate, :cost_estimate_rate, :cost_estimate_type, :days_before_expiration, :defer_rev_rec, :description,
11
11
  :gift_cert_from, :gift_cert_message, :gift_cert_recipient_email, :gift_cert_recipient_name, :id, :inventory_detail,
12
12
  :is_closed, :is_drop_shipment, :is_taxable, :is_vsoe_bundle, :item_subtype, :item_type, :line, :line_number,
13
- :matrix_type, :options, :print_items, :quantity, :quantity_billed, :quantity_received, :quantity_rev_committed,
13
+ :matrix_type, :options, :order_line, :print_items, :quantity, :quantity_billed, :quantity_received, :quantity_rev_committed,
14
14
  :rate, :rate_schedule, :rev_rec_end_date, :rev_rec_start_date, :tax_rate1, :vsoe_allocation, :vsoe_amount,
15
15
  :vsoe_deferral, :vsoe_delivered, :vsoe_is_estimate, :vsoe_permit_discount, :vsoe_price, :vsoe_sop_group
16
16
 
@@ -6,11 +6,18 @@ module NetSuite
6
6
  include Support::Records
7
7
  include Namespaces::TranSales
8
8
 
9
- fields :amount, :bin_numbers, :cost_estimate, :cost_estimate_type, :defer_rev_rec, :description, :gift_cert_from,
10
- :gift_cert_message, :gift_cert_number, :gift_cert_recipient_email, :gift_cert_recipient_name, :gross_amt, :is_taxable,
11
- :line, :order_line, :po_currency, :quantity, :rate, :rev_rec_end_date, :rev_rec_start_date, :rev_rec_term_in_months,
12
- :serial_numbers, :shipping_cost, :tax1_amt, :tax_rate1, :tax_rate2, :vsoe_allocation, :vsoe_amount, :vsoe_deferral,
13
- :vsoe_delivered, :vsoe_permit_discount, :vsoe_price, :is_closed, :quantity_commited, :quantity_fulfilled
9
+ fields :amount, :bin_numbers, :cost_estimate,
10
+ :cost_estimate_type, :defer_rev_rec, :description,
11
+ :expand_item_group, :gift_cert_from, :gift_cert_message,
12
+ :gift_cert_number, :gift_cert_recipient_email,
13
+ :gift_cert_recipient_name, :gross_amt, :is_closed,
14
+ :is_taxable, :line, :order_line, :po_currency, :quantity,
15
+ :quantity_back_ordered, :quantity_billed, :quantity_committed,
16
+ :quantity_fulfilled, :rate, :rev_rec_end_date,
17
+ :rev_rec_start_date, :rev_rec_term_in_months, :serial_numbers,
18
+ :shipping_cost, :tax1_amt, :tax_rate1, :tax_rate2,
19
+ :vsoe_allocation, :vsoe_amount, :vsoe_deferral,
20
+ :vsoe_delivered, :vsoe_permit_discount, :vsoe_price
14
21
 
15
22
  field :custom_field_list, CustomFieldList
16
23
 
@@ -7,7 +7,7 @@ module NetSuite
7
7
  include Support::Actions
8
8
  include Namespaces::ListSupport
9
9
 
10
- actions :get, :get_list, :add, :delete, :update, :upsert
10
+ actions :get, :get_list, :add, :delete, :update, :upsert, :search
11
11
 
12
12
  fields :end_date, :incoming_message, :outgoing_message, :search_solution, :email_form,
13
13
  :internal_only, :title, :case_number, :start_date, :email, :phone, :inbound_email,
@@ -42,6 +42,8 @@ module NetSuite
42
42
  self.send(:include, NetSuite::Actions::DeleteList::Support)
43
43
  when :update
44
44
  self.send(:include, NetSuite::Actions::Update::Support)
45
+ when :update_list
46
+ self.send(:include, NetSuite::Actions::UpdateList::Support)
45
47
  when :initialize
46
48
  self.send(:include, NetSuite::Actions::Initialize::Support)
47
49
  else
@@ -17,9 +17,10 @@ module NetSuite
17
17
  # <platformCore:pageIndex>1</platformCore:pageIndex>
18
18
  # <platformCore:searchId>WEBSERVICES_738944_SB2_03012013650784545962753432_28d96bd280</platformCore:searchId>
19
19
 
20
- def initialize(response, result_class)
20
+ def initialize(response, result_class, credentials)
21
21
  @result_class = result_class
22
22
  @response = response
23
+ @credentials = credentials
23
24
 
24
25
  @total_records = response.body[:total_records].to_i
25
26
  @total_pages = response.body[:total_pages].to_i
@@ -98,8 +99,11 @@ module NetSuite
98
99
  yield results
99
100
 
100
101
  next_search = @result_class.search(
101
- search_id: @response.body[:search_id],
102
- page_index: @response.body[:page_index].to_i + 1
102
+ {
103
+ search_id: @response.body[:search_id],
104
+ page_index: @response.body[:page_index].to_i + 1
105
+ },
106
+ @credentials
103
107
  )
104
108
 
105
109
  @results = next_search.results
@@ -37,6 +37,28 @@ module NetSuite
37
37
  server_time_response.body[:get_server_time_response][:get_server_time_result][:server_time]
38
38
  end
39
39
 
40
+ def netsuite_data_center_urls(account_id)
41
+ data_center_call_response = NetSuite::Configuration.connection({
42
+ # NOTE force a production WSDL so the sandbox settings are ignored
43
+ wsdl: 'https://webservices.netsuite.com/wsdl/v2017_2_0/netsuite.wsdl',
44
+
45
+ # NOTE don't inherit default namespace settings, it includes the API version
46
+ namespaces: {
47
+ 'xmlns:platformCore' => "urn:core_2017_2.platform.webservices.netsuite.com"
48
+ },
49
+
50
+ soap_header: {}
51
+ }).call(:get_data_center_urls, message: {
52
+ 'platformMsgs:account' => account_id
53
+ })
54
+
55
+ if data_center_call_response.success?
56
+ data_center_call_response.body[:get_data_center_urls_response][:get_data_center_urls_result][:data_center_urls]
57
+ else
58
+ false
59
+ end
60
+ end
61
+
40
62
  def backoff(options = {})
41
63
  # TODO the default backoff attempts should be customizable the global config
42
64
  options[:attempts] ||= 8
@@ -86,9 +108,13 @@ module NetSuite
86
108
  !e.message.include?('com.netledger.common.exceptions.NLDatabaseOfflineException') &&
87
109
  !e.message.include?('com.netledger.database.NLConnectionUtil$NoCompanyDbsOnlineException') &&
88
110
  !e.message.include?('com.netledger.cache.CacheUnavailableException') &&
111
+ !e.message.include?('java.lang.IllegalStateException') &&
89
112
  !e.message.include?('An unexpected error occurred.') &&
113
+ !e.message.include?('An unexpected error has occurred. Technical Support has been alerted to this problem.') &&
90
114
  !e.message.include?('Session invalidation is in progress with different thread') &&
91
115
  !e.message.include?('SuiteTalk concurrent request limit exceeded. Request blocked.') &&
116
+ # maintenance is the new outage: this message is being used for intermittent errors
117
+ !e.message.include?('The account you are trying to access is currently unavailable while we undergo our regularly scheduled maintenance.') &&
92
118
  !e.message.include?('The Connection Pool is not intialized.') &&
93
119
  # it looks like NetSuite mispelled their error message...
94
120
  !e.message.include?('The Connection Pool is not intiialized.')
@@ -1,3 +1,3 @@
1
1
  module NetSuite
2
- VERSION = '0.8.2'
2
+ VERSION = '0.8.3'
3
3
  end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ describe NetSuite::Actions::UpdateList do
4
+ before { savon.mock! }
5
+ after { savon.unmock! }
6
+
7
+ context 'Items' do
8
+ context 'one item' do
9
+ let(:item) do
10
+ [
11
+ NetSuite::Records::InventoryItem.new(internal_id: '624113', item_id: 'Target', upccode: 'Target')
12
+ ]
13
+ end
14
+
15
+ before do
16
+ savon.expects(:update_list).with(:message =>
17
+ {
18
+ 'record' => [{
19
+ 'listAcct:itemId' => 'Target',
20
+ '@xsi:type' => 'listAcct:InventoryItem',
21
+ '@internalId' => '624113'
22
+ }]
23
+ }).returns(File.read('spec/support/fixtures/update_list/update_list_one_item.xml'))
24
+ end
25
+
26
+ it 'makes a valid request to the NetSuite API' do
27
+ NetSuite::Actions::UpdateList.call(item)
28
+ end
29
+
30
+ it 'returns a valid Response object' do
31
+ response = NetSuite::Actions::UpdateList.call(item)
32
+ expect(response).to be_kind_of(NetSuite::Response)
33
+ expect(response).to be_success
34
+ end
35
+ end
36
+
37
+ context 'two items' do
38
+ let(:items) do
39
+ [
40
+ NetSuite::Records::InventoryItem.new(internal_id: '624172', item_id: 'Shutter Fly', upccode: 'Shutter Fly, Inc.'),
41
+ NetSuite::Records::InventoryItem.new(internal_id: '624113', item_id: 'Target', upccode: 'Target')
42
+ ]
43
+ end
44
+
45
+ before do
46
+ savon.expects(:update_list).with(:message =>
47
+ {
48
+ 'record' => [{
49
+ 'listAcct:itemId' => 'Shutter Fly',
50
+ '@xsi:type' => 'listAcct:InventoryItem',
51
+ '@internalId' => '624172'
52
+ },
53
+ {
54
+ 'listAcct:itemId' => 'Target',
55
+ '@xsi:type' => 'listAcct:InventoryItem',
56
+ '@internalId' => '624113'
57
+ }
58
+ ]
59
+ }).returns(File.read('spec/support/fixtures/update_list/update_list_items.xml'))
60
+ end
61
+
62
+ it 'makes a valid request to the NetSuite API' do
63
+ NetSuite::Actions::UpdateList.call(items)
64
+ end
65
+
66
+ it 'returns a valid Response object' do
67
+ response = NetSuite::Actions::UpdateList.call(items)
68
+ expect(response).to be_kind_of(NetSuite::Response)
69
+ expect(response).to be_success
70
+ end
71
+ end
72
+ end
73
+
74
+ context 'with errors' do
75
+ let(:items) do
76
+ [
77
+ NetSuite::Records::InventoryItem.new(internal_id: '624172-bad', item_id: 'Shutter Fly', upccode: 'Shutter Fly, Inc.'),
78
+ NetSuite::Records::InventoryItem.new(internal_id: '624113-bad', item_id: 'Target', upccode: 'Target')
79
+ ]
80
+ end
81
+
82
+ before do
83
+ savon.expects(:update_list).with(:message =>
84
+ {
85
+ 'record' => [{
86
+ 'listAcct:itemId' => 'Shutter Fly',
87
+ '@xsi:type' => 'listAcct:InventoryItem',
88
+ '@internalId' => '624172-bad'
89
+ },
90
+ {
91
+ 'listAcct:itemId' => 'Target',
92
+ '@xsi:type' => 'listAcct:InventoryItem',
93
+ '@internalId' => '624113-bad'
94
+ }
95
+ ]
96
+ }).returns(File.read('spec/support/fixtures/update_list/update_list_with_errors.xml'))
97
+ end
98
+
99
+ it 'constructs error objects' do
100
+ response = NetSuite::Actions::UpdateList.call(items)
101
+ expect(response.errors.keys).to match_array(['624172', '624113'])
102
+ expect(response.errors['624172'].first.code).to eq('USER_ERROR')
103
+ expect(response.errors['624172'].first.message).to eq('Please enter value(s) for: ItemId')
104
+ expect(response.errors['624172'].first.type).to eq('ERROR')
105
+ end
106
+ end
107
+ end