Git Product home page Git Product logo

netsuite's Introduction

Circle CI
Slack Status
Gem Version
Dependency Status

NetSuite Ruby SuiteTalk Gem

  • This gem will act as a wrapper around the NetSuite SuiteTalk WebServices API. Wow, that is a mouthful.
  • The gem does not cover the entire API, only the subset that we have found useful to cover so far.
  • Extending the wrapper is pretty simple, check out recent commits for an example of how to add support for additional records.
  • NetSuite development is overall a pretty poor experience. We have a list of NetSuite Development Resources that might make things a bit less painful.

Help & Support

Join the slack channel for help with any NetSuite issues.

Installation

Add this line to your application's Gemfile:

gem 'netsuite'

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

Testing

Before contributing a patch make sure all existing tests pass.

git clone git://github.com/NetSweet/netsuite.git
cd netsuite
bundle
bundle exec rspec

Usage

Configuration

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

NetSuite.configure do
  reset!

  # optional, defaults to 2011_2
  api_version	'2012_1'

  # optionally specify full wsdl URL (to switch to sandbox, for example)
  wsdl          "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
  wsdl "https://webservices.na2.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl"

  # or specify the sandbox flag if you don't want to deal with specifying a full URL
  sandbox	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
  read_timeout  100000

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

  # login information
  email    	'[email protected]'
  password 	'password'
  account   	'12345'
  role      	1111
end

There is a convenience method to configure NetSuite based on ENV variables.

OAuth credentials are also supported:

NetSuite.configure do
  reset!

  account       ENV['NETSUITE_ACCOUNT']

  consumer_key     ENV['NETSUITE_CONSUMER_KEY']
  consumer_secret  ENV['NETSUITE_CONSUMER_SECRET']
  token_id         ENV['NETSUITE_TOKEN_ID']
  token_secret     ENV['NETSUITE_TOKEN_SECRET']
  
  # oauth does not work with API versions less than 2015_2
  api_version      '2015_2'
end

Examples

CRUD Operations

# get a customer
customer = NetSuite::Records::Customer.get(:internal_id => 4)
customer.is_person

# or
NetSuite::Records::Customer.get(4).is_person

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

# randomly assign a task
customer_support_reps = [12345, 12346]

task = 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"
)

task.add

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

task.update :message => 'New Message'

task.delete

# using get_select_value with a standard record
NetSuite::Records::BaseRefList.get_select_value(
  recordType: 'serviceSaleItem',
  field: 'taxSchedule'
)

Custom Records & Fields

# updating a custom field list on a record

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

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

# getting a custom record
record = NetSuite::Records::CustomRecord.get(
  # custom record type
  type_id: 10,
  # reference to instance of the custom record type
  internal_id: 100
)

# getting a list of custom records
records = NetSuite::Records::CustomRecord.get_list(
  # netsuite internalIDs
  list: [1,2,3],
  # only needed for a custom record
  type_id: 1234
)

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

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

# using get_select_value with a custom record
NetSuite::Records::BaseRefList.get_select_value(
  field: 'custrecord_something',
  customRecordType: {
    '@internalId' => 10,
    '@xsi:type' => 'customRecord'
  }
)

Searching

# basic search
search = NetSuite::Records::Customer.search({
  basic: [
    {
      field: 'companyName',
      operator: 'contains',
      value: company_name
    }
  ]
})

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

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

# advanced search building on saved search
NetSuite::Records::Customer.search({
  saved: 500,	# your saved search internalId
  basic: [
    {
      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
      	}
      ]
    }
  ]
}).results

# advanced search from scratch
NetSuite::Records::Transaction.search({
  criteria: {
    basic: [
      {
        field: 'type',
        operator: 'anyOf',
        type: 'SearchEnumMultiSelectField',
        value: [ "_invoice", "_salesOrder" ]
      },
      {
        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: [
          # the following format is equivilent to ISO 8601
          # Date.parse("1/1/2012").strftime("%Y-%m-%dT%H:%M:%S%z"),
          # Date.parse("30/07/2013").strftime("%Y-%m-%dT%H:%M:%S%z")

          # 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
  columns: {
    '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"
        }
      ]
    ]
  },

  preferences: {
    page_size: 10
  }
}).results

NetSuite::Records::ItemFulfillment.search({
  criteria: {
    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'
      }
    ]
  },
  preferences: {
    pageSize: 1000,
    bodyFieldsOnly: false
  }
}).results

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

  preferences: {
    page_size: 10,
  }
)

search.results_in_batches do |batch|
  puts batch.map(&:internal_id)
end

# item search
NetSuite::Records::InventoryItem.search({
  criteria: {
    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
      }
    ]
  }
}).results.first

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

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

search = NetSuite::Records::File.search({
  preferences: {
    body_fields_only: false,
    page_size: 20
  }
})

search.results_in_batches do |batch|
  batch.each do |file|
    next unless file.file_type == "_JAVASCRIPT"
    puts Base64.decode64(file.content)
  end
end

# the getList operation
NetSuite::Records::CustomRecord.get_list(
  # netsuite internalIDs
  list: [1,2,3],
  # only needed for a custom record
  type_id: 1234,
  # allow inclomplete results (defaults to false)
  allow_incomplete: true
).each do |record|
  # do your thing...
end

# Adding a Customer Deposit example. The customer associated with the
# sales order would be linked to the deposit.

deposit = CustomerDeposit.new
deposit.sales_order = RecordRef.new(internal_id: 7279)
deposit.payment = 20
deposit.add

Non-standard Operations

# making a call that hasn't been implemented yet
NetSuite::Configuration.connection.call :get_customization_id, message: {
  'platformMsgs:customizationType' => { '@getCustomizationType' => 'customRecordType'},
  'platformMsgs:includeInactives' => 'false'
}

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

# getting a list of states
states = NetSuite::Configuration.connection.call(:get_all, message: {
  'platformCore:record' => {
    '@recordType' => 'state'
  }
})
states.to_array.first[:get_all_response][:get_all_result][:record_list][:record].map { |r| { country: r[:country], abbr: r[:shortname], name: r[:full_name] } }

2015_2 ApplicationId Support

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

netsuite's People

Contributors

buccolo avatar charger avatar davidlaprade avatar drewish avatar feigner avatar greggroth avatar huoxito avatar iloveitaly avatar jasdeepgosal avatar jcroteau avatar jdfrens avatar jeperkins4 avatar jeremysmithco avatar jhirbour avatar jordan-allan avatar jpterry avatar kfalconer avatar kiran-stripe avatar kjc-stripe avatar kristyfrey avatar kruppel avatar pospischil avatar prburke avatar roeyby avatar rudyyazdi avatar ryanmoran avatar sauberia avatar soulcutter avatar ssherbondy avatar tspen avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.