particle-iot/netsuite

Name: netsuite

Owner: Particle

Description: NetSuite SuiteTalk API Wrapper

Forked from: NetSweet/netsuite

Created: 2017-07-05 20:03:55.0

Updated: 2017-07-05 20:03:57.0

Pushed: 2018-01-03 18:35:01.0

Homepage: http://opensuite-slackin.herokuapp.com

Size: 1830

Language: Ruby

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Table of Contents generated with DocToc

Circle CI Slack Status Gem Version Dependency Status

NetSuite SuiteTalk API Ruby Gem

Help & Support

Join the slack channel for help with any NetSuite issues. Please do not post usage questions as issues in GitHub.

Messages in the Slack ground are archived here. Search the archives to see if your question has been answered before.

There is some additional helpful resources for NetSuite development listed here.

Testing

Before contributing a patch make sure all existing tests pass.

clone git://github.com/NetSweet/netsuite.git
etsuite
le
le exec rspec

Installation

Add this line to your application's Gemfile:

'netsuite'

This gem is built for ruby 1.9.x+, checkout the 1-8-stable branch for ruby 1.8.x support.

Configuration

Not sure how to find your account id? Search for “web service preferences” in the NetSuite global search.

uite.configure do
set!

optional, defaults to 2011_2
i_version   '2012_1'

optionally specify full wsdl URL (to switch to sandbox, for example)
dl          "https://webservices.sandbox.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl"

if your datacenter is being switched, you'll have to manually set your wsdl location
dl          "https://webservices.na2.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl"

or specify the wsdl_domain if you want to specify the datacenter and let the configuration
construct the full wsdl location - e.g. "https://#{wsdl_domain}/wsdl/v#{api_version}_0/netsuite.wsdl"
dl_domain   "webservices.na2.netsuite.com"

or specify the sandbox flag if you don't want to deal with specifying a full URL
ndbox       true

often the netsuite servers will hang which would cause a timeout exception to be raised
if you don't mind waiting (e.g. processing NS via DJ), increasing the timeout should fix the issue
ad_timeout  100000

you can specify a file or file descriptor to send the log output to (defaults to STDOUT)
g           File.join(Rails.root, 'log/netsuite.log')

login information
ail     'email@domain.com'
ssword  'password'
count       '12345'
le          1111

optional, ensures that read-only fields don't cause API errors
ap_header   'platformMsgs:preferences' => {
'platformMsgs:ignoreReadOnlyFields' => true,


If you'd like to use a API endpoints greater than 2015_1 you'll need to specify an application ID:

uite::Configuration.soap_header = {
platformMsgs:ApplicationInfo' => {
  'platformMsgs:applicationId' => 'your-netsuite-app-id'


Token based Authentication

OAuth credentials are also supported. Learn more about how to set up token based authentication here.

uite.configure do
set!

count       ENV['NETSUITE_ACCOUNT']

nsumer_key     ENV['NETSUITE_CONSUMER_KEY']
nsumer_secret  ENV['NETSUITE_CONSUMER_SECRET']
ken_id         ENV['NETSUITE_TOKEN_ID']
ken_secret     ENV['NETSUITE_TOKEN_SECRET']

oauth does not work with API versions less than 2015_2
i_version      '2016_2'

Usage

CRUD Operations
t a customer
omer = NetSuite::Records::Customer.get(:internal_id => 4)
omer.is_person


uite::Records::Customer.get(4)


t a list of customers
omers = NetSuite::Records::Customer.get_list(:list => [4, 5, 6])

ndomly assign a task
omer_support_reps = [12345, 12346]

 = NetSuite::Records::Task.new(
:title => 'Take Care of a Customer',
:assigned => NetSuite::Records::RecordRef.new(internal_id: customer_support_reps.sample),
:due_date => DateTime.now + 1,
:message => "Take care of this"


.add

is will only work on OS X, open a browser to the record that was just created
n https://system.sandbox.netsuite.com/app/crm/calendar/task.nl?id=#{invoice.internal_id}`

date a field on a record
.update(message: 'New Message')

lete a record
.delete

fresh/reload a record (helpful after adding the record for the first time)
.reload

ing get_select_value with a standard record
uite::Records::BaseRefList.get_select_value(
cordType: 'serviceSaleItem',
eld: 'taxSchedule'


t options for a custom sublist field (i.e. transaction column fields)
uite::Records::BaseRefList.get_select_value(
eld: 'custcol69_2',
blist: 'itemList',
cordType: 'salesOrder'


tput names of options available for a custom field
ons = NetSuite::Records::BaseRefList.get_select_value(
eld: 'custbody_order_source',
cordType: 'invoice'

ons.base_refs.map(&:name)
Custom Records & Fields
dating a custom field list on a record

u need to push ALL the values of ALL of the custom fields that you want set on the record
u can't just push the values of the fields that you want to update: all of the values of
her fields will then fall back to their default values

act = NetSuite::Records::Contact.get(12345)
act.custom_field_list.custentity_alistfield = { internal_id: 1 }
act.custom_field_list.custentity_abooleanfield = true
act.update(custom_field_list: contact.custom_field_list)

tting a custom record
rd = NetSuite::Records::CustomRecord.get(
custom record type
pe_id: 10,
reference to instance of the custom record type
ternal_id: 100


tting a list of custom records
rds = NetSuite::Records::CustomRecord.get_list(
netsuite internalIDs
st: [1,2,3],
only needed for a custom record
pe_id: 1234


ding a custom record
rd = NetSuite::Records::CustomRecord.new
rd.rec_type = NetSuite::Records::CustomRecord.new(internal_id: 10)
rd.custom_field_list.custrecord_locationstate = "New Jersey"
rd.add

dating a custom record
rd = NetSuite::Records::CustomRecord.new(internal_id: 100)
rd.custom_field_list.custrecord_locationstate = "New Jersey"
rd.update(custom_field_list: record.custom_field_list, rec_type: NetSuite::Records::CustomRecord.new(internal_id: 10))

ing get_select_value with a custom record
uite::Records::BaseRefList.get_select_value(
eld: 'custrecord_something',
stomRecordType: {
'@internalId' => 10,
'@xsi:type' => 'customRecord'


Searching
sic search
ch = NetSuite::Records::Customer.search({
sic: [
{
  field: 'companyName',
  operator: 'contains',
  value: company_name
}



n https://system.netsuite.com/app/common/entity/custjob.nl?id=#{search.results.first.internal_id}`

nd the avalara tax item. Some records don't support search.
sales_taxes = NetSuite::Utilities.backoff { NetSuite::Records::SalesTaxItem.get_all }
ax_code = all_sales_taxes.detect { |st| st.item_id == 'AVATAX' }

arching for custom records
uite::Records::CustomRecord.search(
sic: [
{
  field: 'recType',
  operator: 'is',
  # custom record type
  value: NetSuite::Records::CustomRecordRef.new(:internal_id => 10),
}

sults

vanced search building on saved search
uite::Records::Customer.search({
ved: 500,   # your saved search internalId
sic: [
{
  field: 'entityId',
  operator: 'hasKeywords',
  value: 'Assumption',
},
{
  field: 'stage',
  operator: 'anyOf',
  type: 'SearchMultiSelectCustomField',
  value: [
    '_lead', '_customer'
  ]
},
{
  field: 'customFieldList',
  value: [
    {
      field: 'custentity_acustomfield',
      operator: 'anyOf',
      # type is needed for all search fields
      type: 'SearchMultiSelectCustomField',
      value: [
        NetSuite::Records::CustomRecordRef.new(:internal_id => 1),
        NetSuite::Records::CustomRecordRef.new(:internal_id => 2),
      ]
    },
    {
      field: 'custbody_internetorder',
      type: 'SearchBooleanCustomField',
      value: true
    }
  ]
}

esults

uite::Records::SalesOrder.search({
iteria: {
basic: [
  # NOTE do not search for more than one transaction type at a time!
  {
    field: 'type',
    operator: 'anyOf',
    value: [ "_invoice"]
  },
  {
    field: 'tranDate',
    operator: 'within',
    # this is needed for date range search requests, for date requests with a single param type is not needed
    type: 'SearchDateField',
    value: [
      # NetSuite requires iso8601 time format
      # need to require the time library for this to work
      Time.parse("01/01/2012").iso8601,
      Time.parse("30/07/2013").iso8601,

      # or you can use a string. Note that the format below is different from the format of the above code
      # but it matches exactly what NS returns
      # "2012-01-01T22:00:00.000-07:00",
      # "2013-07-30T22:00:00.000-07:00"
    ]
  }
],

# equivilent to the 'Account' label in the GUI
accountJoin: [
  {
    field: 'internalId',
    operator: 'noneOf',
    value: [ NetSuite::Records::Account.new(:internal_id => 215) ]
  }
],

itemJoin: [
  {
    field: 'customFieldList',
    value: [
      {
        field: 'custitem_apcategoryforsales',
        operator: 'anyOf',
        type: 'SearchMultiSelectCustomField',
        value: [
          NetSuite::Records::Customer.new(:internal_id => 1),
          NetSuite::Records::Customer.new(:internal_id => 2),
        ]
      }
    ]
  }
]


the column syntax is a WIP. This will change in the future
lumns: {
'tranSales:basic' => [
  'platformCommon:internalId/' => {},
  'platformCommon:email/' => {},
  'platformCommon:tranDate/' => {}
],
'tranSales:accountJoin' => [
  'platformCommon:internalId/' => {}
],
'tranSales:contactPrimaryJoin' => [
  'platformCommon:internalId/' => {}
],
'tranSales:customerJoin' => [
  'platformCommon:internalId/' => {}
],
'tranSales:itemJoin' => [
  'platformCommon:customFieldList' => [
    'platformCore:customField/' => {
      '@internalId' => 'custitem_apcategoryforsales',
      '@xsi:type' => "platformCore:SearchColumnSelectCustomField"
    }
  ]
]


eferences: {
page_size: 10,

# only returning body fields increases performance!
body_fields_only: true

esults

uite::Records::ItemFulfillment.search({
iteria: {
basic: [
  {
    field: 'type',
    operator: 'anyOf',
    type: 'SearchEnumMultiSelectField',
    value: ["_itemFulfillment"]
  },
  {
    field: 'lastModifiedDate',
    type: 'SearchDateField',
    operator: 'within',
    value: [
      DateTime.now - 2.hours,
      DateTime.now
    ]
  }
],
createdFromJoin: [
  {
    field: 'type',
    operator: 'anyOf',
    value: [ '_salesOrder' ]
  },
  {
    field: 'internalIdNumber',
    operator: 'notEmpty'
  }
]

eferences: {
pageSize: 1000,
bodyFieldsOnly: false

esults

sic search with pagination / SearchMorewithId
ch = NetSuite::Records::Customer.search(
iteria: {
basic: [
  {
    # no operator for booleans
    field: 'isInactive',
    value: false,
  },
]


eferences: {
page_size: 10,



ch.results_in_batches do |batch|
ts batch.map(&:internal_id)


em search
uite::Records::InventoryItem.search({
iteria: {
basic: [
  {
    field: 'type',
    operator: 'anyOf',
    type: 'SearchEnumMultiSelectField',
    value: [
      '_inventoryItem',

      # note that the naming conventions aren't consistent: AssemblyItem != _assemblyItem
      # https://system.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2014_1/schema/enum/itemtype.html
      '_assembly'
    ]
  },
  {
    field: 'isInactive',
    value: false
  }
]

esults.first

t body_fields_only = false to include the majority of lists associated with records in the XML response
me lists you just can't include with searches (a customer's AddressBookList, for example)

 order to get the full record data for those records whose lists aren't included when body_fields_only = false
u will have to run a get_list call on the resulting internalIds returned from the search you've executed

ch = NetSuite::Records::File.search({
eferences: {
body_fields_only: false,
page_size: 20



ch.results_in_batches do |batch|
tch.each do |file|
next unless file.file_type == "_JAVASCRIPT"
puts Base64.decode64(file.content)
d


e getList operation
uite::Records::CustomRecord.get_list(
netsuite internalIDs
st: [1,2,3],
only needed for a custom record
pe_id: 1234,
allow inclomplete results (defaults to false)
low_incomplete: true
ch do |record|
do your thing...


ding a Customer Deposit example. The customer associated with the
les order would be linked to the deposit.

sit = CustomerDeposit.new
sit.sales_order = RecordRef.new(internal_id: 7279)
sit.payment = 20
sit.add
Non-standard Operations
king a call that hasn't been implemented yet
uite::Configuration.connection.call :get_customization_id, message: {
latformMsgs:customizationType' => { '@getCustomizationType' => 'customRecordType'},
latformMsgs:includeInactives' => 'false'


er_time_response = NetSuite::Configuration.connection.call :get_server_time
er_time_response.body[:get_server_time_response][:get_server_time_result][:server_time]

tting a list of states
es = NetSuite::Configuration.connection.call(:get_all, message: {
latformCore:record' => {
'@recordType' => 'state'


es.to_array.first[:get_all_response][:get_all_result][:record_list][:record].map { |r| { country: r[:country], abbr: r[:shortname], name: r[:full_name] } }

About SuiteSync

SuiteSync, the Stripe-NetSuite integration uses this gem and funds the majority of it's development and maintenance.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.