{"id":15288783,"url":"https://github.com/netsweet/netsuite","last_synced_at":"2025-05-15T01:07:05.357Z","repository":{"id":2088810,"uuid":"3028718","full_name":"NetSweet/netsuite","owner":"NetSweet","description":"NetSuite SuiteTalk API Wrapper","archived":false,"fork":false,"pushed_at":"2024-09-02T17:53:12.000Z","size":2362,"stargazers_count":255,"open_issues_count":63,"forks_count":273,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-04-11T14:16:51.890Z","etag":null,"topics":["erp","netsuite","netsuite-api","netsuite-suitetalk","rails","ruby"],"latest_commit_sha":null,"homepage":"http://opensuite-slackin.herokuapp.com","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NetSweet.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["iloveitaly"]}},"created_at":"2011-12-21T18:22:17.000Z","updated_at":"2025-04-01T01:52:39.000Z","dependencies_parsed_at":"2023-10-04T06:13:51.424Z","dependency_job_id":"d7799a2b-a705-4ea5-9d9e-776c3528ce81","html_url":"https://github.com/NetSweet/netsuite","commit_stats":{"total_commits":1444,"total_committers":128,"mean_commits":11.28125,"dds":0.5166204986149585,"last_synced_commit":"33a5659cc68d11e0ac2ee3ca0c14ed4a12113dea"},"previous_names":[],"tags_count":87,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NetSweet","download_url":"https://codeload.github.com/NetSweet/netsuite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254190422,"owners_count":22029639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["erp","netsuite","netsuite-api","netsuite-suitetalk","rails","ruby"],"created_at":"2024-09-30T15:53:10.234Z","updated_at":"2025-05-15T01:07:00.330Z","avatar_url":"https://github.com/NetSweet.png","language":"Ruby","readme":"[![Ruby](https://github.com/NetSweet/netsuite/actions/workflows/main.yml/badge.svg)](https://github.com/NetSweet/netsuite/actions/workflows/main.yml)\n[![Slack Status](https://netsuite-slackin.fly.dev/badge.svg)](https://netsuite-slackin.fly.dev)\n[![Gem Version](https://badge.fury.io/rb/netsuite.svg)](http://badge.fury.io/rb/netsuite)\n\n\u003e [!WARNING]  \n\u003e I ([@ilovetaly](https://github.com/iloveitaly) am not actively using this gem for any projects (I'm using [this python package](https://github.com/jacobsvante/netsuite) instead) and do not have time for further maintenance. I'll need monthly sponsors, paid consulting around this gem, or if anyone else wants to help with maintenance, please drop me a note. Apologies!\n\n# NetSuite SuiteTalk API Ruby Gem\n\n* This gem will act as a wrapper around the NetSuite SuiteTalk Web Services API.\n* The gem does not cover the entire API, only the subset contributors have used so far. Please submit a PR for any functionality that's missing!\n* NetSuite is a complex system. There's a lot to learn and sparse resources available to learn from. Here's a list of [NetSuite Development Resources](https://github.com/NetSweet/netsuite/wiki/NetSuite-Development-Resources).\n\n# Help \u0026 Support\n\nJoin the [Slack channel](https://netsuite-slackin.fly.dev) for help with NetSuite issues. Please do not post usage questions as issues in GitHub.\n\nThere are some additional helpful resources for NetSuite development [listed here](https://dashboard.suitesync.io/docs/resources#netsuite).\n\n# Testing\n\nBefore contributing a patch make sure all existing tests pass.\n\n```shell\ngit clone git://github.com/NetSweet/netsuite.git\ncd netsuite\n\nbundle\nbundle exec rspec\n```\n\n# Installation\n\nAdd this line to your application's Gemfile:\n\n```\ngem 'netsuite'\n```\n\nIf you'd like more accurate time conversion support, include the `tzinfo` gem.\n\nThis gem is built for Ruby 2.6.x+, but should work on older versions down to 1.9. There's a [1-8-stable](https://github.com/NetSweet/netsuite/tree/1-8-stable) branch for Ruby 1.8.x support.\n\n## Configuration\n\nThe most important thing you'll need is your NetSuite account ID. Not sure how to find your account ID? [Here's a guide.](http://mikebian.co/find-netsuite-web-services-account-number/)\n\nHow you connect to NetSuite has changed a lot over the years and differs between API versions. For instance:\n\n* Older API versions (~2015) allowed authentication via username and password\n* Newer API versions (\u003e 2016) still allowed for username and password authentication, but required an application ID\n* \"OAuth\", which requires four separate keys to be manually generated, was supported sometime after 2015\n* API versions greater than 2018_2 require `endpoint` to be set directly ([more info](https://github.com/NetSweet/netsuite/pull/473))\n\nHere's an example connection configuration. You don't want to actually use username + password config; Token Based Authentication is detailed [in a separate section](#token-based-authentication):\n\n```ruby\nNetSuite.configure do\n  reset!\n\n  # production \u0026 sandbox account numbers will differ\n  account  'TSTDRV1576318'\n  api_version '2018_2'\n\n  # password-based login information\n  # in most cases you should use Token Based Authentication instead\n  email 'email@example.com'\n  password 'password'\n  role 10\n\n  # recent API versions require an account-specific endpoint to be set\n  # use `NetSuite::Utilities.data_center_url('TSTDRV1576318')` to retrieve WSDL URL\n  # you'll want to do this in a background process and strip the protocol out of the return string\n  wsdl_domain 'tstdrv1576318.suitetalk.api.netsuite.com'\n\n  # the endpoint indicated in the \u003e 2018_2 wsdl is invalid\n  # you must set the endpoint directly\n  # https://github.com/NetSweet/netsuite/pull/473\n  endpoint \"https://#{wsdl_domain}/services/NetSuitePort_#{api_version}\"\nend\n```\n\nThe `wsdl_domain` configuration is most important. Note that if you use `wsdl` or other configuration options below, you'll want to look at the configuration source to understand more about how the different options interact with each other. Some of the configuration options will mutate the state of other options.\n\nHere's the various options that are are available for configuration:\n\n```ruby\nNetSuite.configure do\n  reset!\n\n  api_version\t'2018_2'\n\n  # optionally specify full WSDL URL (to switch to sandbox, for example)\n  wsdl          \"https://webservices.sandbox.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl\"\n\n  # if your datacenter is being switched, you'll have to manually set your WSDL location\n  wsdl          \"https://webservices.na2.netsuite.com/wsdl/v#{api_version}_0/netsuite.wsdl\"\n\n  # or specify the wsdl_domain if you want to specify the datacenter and let the configuration\n  # construct the full wsdl location - e.g. \"https://#{wsdl_domain}/wsdl/v#{api_version}_0/netsuite.wsdl\"\n  wsdl_domain   \"webservices.na2.netsuite.com\"\n\n  # often the NetSuite servers will hang which would cause a timeout exception to be raised\n  # if you don't mind waiting (e.g. processing NS via a background worker), increasing the timeout should fix the issue\n  read_timeout  100_000\n\n  # you can specify a file or file descriptor to send the log output to (defaults to STDOUT)\n  # If using within a Rails app, consider setting to `Rails.logger` to leverage existing\n  # application-level log configuration\n  log           File.join(Rails.root, 'log/netsuite.log')\n\n  # Defaults to :debug level logging for Savon API calls. Decrease the verbosity\n  # by setting log_level to `:info`, for example\n  # log_level   :debug\n\n  # password-based login information\n  # in most cases you should use Token Based Authentication instead\n  email    \t  'email@domain.com'\n  password \t  'password'\n  account   \t'12345'\n  role        1111\n\n  # optional, ensures that read-only fields don't cause API errors\n  soap_header\t'platformMsgs:preferences' =\u003e {\n    'platformMsgs:ignoreReadOnlyFields' =\u003e true,\n  }\nend\n```\n\nIf you are using username + password authentication (which you shouldn't be!) *and* you'd like to use an API endpoint greater than 2015_1, you'll need to specify an application ID:\n\n```ruby\nNetSuite::Configuration.soap_header = {\n   'platformMsgs:ApplicationInfo' =\u003e {\n      'platformMsgs:applicationId' =\u003e 'your-netsuite-app-id'\n   }\n}\n```\n\n### Token Based Authentication\n\nOAuth credentials are supported and the recommended authentication approach. [Learn more about how to set up Token Based Authentication here](http://mikebian.co/using-netsuites-token-based-authentication-with-suitetalk/).\n\n```ruby\nNetSuite.configure do\n  reset!\n\n  account       ENV['NETSUITE_ACCOUNT']\n\n  consumer_key     ENV['NETSUITE_CONSUMER_KEY']\n  consumer_secret  ENV['NETSUITE_CONSUMER_SECRET']\n  token_id         ENV['NETSUITE_TOKEN_ID']\n  token_secret     ENV['NETSUITE_TOKEN_SECRET']\n\n  # oauth does not work with API versions less than 2015_2\n  api_version      '2016_2'\n\n  # the endpoint indicated in the \u003e 2018_2 WSDL is invalid\n  # you must set the endpoint directly\n  # https://github.com/NetSweet/netsuite/pull/473\n  endpoint \"https://#{wsdl_domain}/services/NetSuitePort_#{api_version}\"\nend\n```\n\n### Multi-Tenancy\n\nIf you're interacting with multiple NetSuite accounts, each in separate threads, you can enable multi-tenancy to prevent your configuration and caches from being shared between threads.\n\nFrom your main thread, you'd want to enable multi-tenancy:\n\n```ruby\nNetSuite.configure do\n  multi_tentant!\nend\n```\n\nNote that `multi_tenant!` is a special configuration option which is _not_ effected by `reset!`.\n\nThen in each child thread, you'd perform any configuration specific to the NetSuite account you're interacting with for that thread, all of which will be specific to that thread only:\n\n```ruby\nNetSuite.configure do\n  reset!\n\n  account ENV['NETSUITE_ACCOUNT']\n\n  # The rest of your usual configuration...\nend\n```\n\n# Usage\n\n## CRUD Operations\n\n```ruby\n# get a customer\ncustomer = NetSuite::Records::Customer.get(:internal_id =\u003e 4)\ncustomer.is_person\n\n# or\nNetSuite::Records::Customer.get(4)\n\n\n# get a list of customers\ncustomers = NetSuite::Records::Customer.get_list(:list =\u003e [4, 5, 6])\n\n# randomly assign a task\ncustomer_support_reps = [12345, 12346]\n\ntask = NetSuite::Records::Task.new(\n\t:title =\u003e 'Take Care of a Customer',\n\t:assigned =\u003e NetSuite::Records::RecordRef.new(internal_id: customer_support_reps.sample),\n\t:due_date =\u003e DateTime.now + 1,\n\t:message =\u003e \"Take care of this\"\n)\n\ntask.add\n\n# this will only work on OS X, open a browser to the record that was just created\n`open https://system.sandbox.netsuite.com/app/crm/calendar/task.nl?id=#{invoice.internal_id}`\n\n# update a field on a record\ntask.update(message: 'New Message')\n\n# delete a record\ntask.delete\n\n# refresh/reload a record (helpful after adding the record for the first time)\ntask.refresh\n\n# using get_select_value with a standard record\nNetSuite::Records::BaseRefList.get_select_value(\n  recordType: 'serviceSaleItem',\n  field: 'taxSchedule'\n)\n\n# get options for a custom sublist field (i.e. transaction column fields)\nNetSuite::Records::BaseRefList.get_select_value(\n  field: 'custcol69_2',\n  sublist: 'itemList',\n  recordType: 'salesOrder'\n)\n\n# output names of options available for a custom field\noptions = NetSuite::Records::BaseRefList.get_select_value(\n  field: 'custbody_order_source',\n  recordType: 'invoice'\n)\noptions.base_refs.map(\u0026:name)\n```\n\n## Uploading/Attaching Files\n\n```ruby\nfile = NetSuite::Records::File.new(\n  content: Base64.encode64(File.read('/path/to/file')),\n  name: 'Invoice.pdf',\n)\nfile.add\n\ninvoice = NetSuite::Records::Invoice.get(internal_id: 1)\ninvoice.attach_file(NetSuite::Records::RecordRef.new(internal_id: file.internal_id))\n```\n\n## Custom Records \u0026 Fields\n\n```ruby\n# updating a custom field list on a record\n\n# you need to push ALL the values of ALL of the custom fields that you want set on the record\n# you can't just push the values of the fields that you want to update: all of the values of\n# other fields will then fall back to their default values\n\ncontact = NetSuite::Records::Contact.get(12345)\ncontact.custom_field_list.custentity_alistfield = { internal_id: 1 }\ncontact.custom_field_list.custentity_abooleanfield = true\ncontact.update(custom_field_list: contact.custom_field_list)\n\n# getting a custom record\nrecord = NetSuite::Records::CustomRecord.get(\n  # custom record type\n  type_id: 10,\n  # reference to instance of the custom record type\n  internal_id: 100\n)\n\n# getting a list of custom records\nrecords = NetSuite::Records::CustomRecord.get_list(\n  # netsuite internalIDs\n  list: [1,2,3],\n  # only needed for a custom record\n  type_id: 1234\n)\n\n# adding a custom record\nrecord = NetSuite::Records::CustomRecord.new\nrecord.rec_type = NetSuite::Records::CustomRecord.new(internal_id: 10)\nrecord.custom_field_list.custrecord_locationstate = \"New Jersey\"\nrecord.add\n\n# updating a custom record\nrecord = NetSuite::Records::CustomRecord.new(internal_id: 100)\nrecord.custom_field_list.custrecord_locationstate = \"New Jersey\"\nrecord.update(custom_field_list: record.custom_field_list, rec_type: NetSuite::Records::CustomRecord.new(internal_id: 10))\n\n# using get_select_value with a custom record\nNetSuite::Records::BaseRefList.get_select_value(\n  field: 'custrecord_something',\n  customRecordType: {\n    '@internalId' =\u003e 10,\n    '@xsi:type' =\u003e 'customRecord'\n  }\n)\n```\n\n## Null Fields\n\n```ruby\n# updating a field on a record to be null\ninvoice = NetSuite::Records::Invoice.get(12345)\ninvoice.update(null_field_list: 'shipMethod')\n\n# updating multiple fields on a record to be null\ninvoice.update(null_field_list: ['shipAddressList', 'shipMethod'])\n\n# updating a custom fields on a record to be null, using custom field ID\ninvoice.update(null_field_list: 'custBody9')\n```\n\n## Searching\n\n```ruby\n# basic search\nsearch = NetSuite::Records::Customer.search({\n  basic: [\n    {\n      field: 'companyName',\n      operator: 'contains',\n      value: company_name\n    }\n  ]\n})\n\n`open https://system.netsuite.com/app/common/entity/custjob.nl?id=#{search.results.first.internal_id}`\n\n# find the Avalara tax item. Some records don't support search.\nall_sales_taxes = NetSuite::Utilities.backoff { NetSuite::Records::SalesTaxItem.get_all }\nns_tax_code = all_sales_taxes.detect { |st| st.item_id == 'AVATAX' }\n\n# searching for custom records\nNetSuite::Records::CustomRecord.search(\n  basic: [\n    {\n      field: 'recType',\n      operator: 'is',\n      # custom record type\n      value: NetSuite::Records::CustomRecordRef.new(:internal_id =\u003e 10),\n    }\n  ]\n).results\n\n# advanced search building on saved search\nNetSuite::Records::Customer.search({\n  saved: 500,\t# your saved search internalId\n  basic: [\n    {\n      field: 'entityId',\n      operator: 'hasKeywords',\n      value: 'Assumption',\n    },\n    {\n      field: 'stage',\n      operator: 'anyOf',\n      type: 'SearchMultiSelectCustomField',\n      value: [\n        '_lead', '_customer'\n      ]\n    },\n    {\n      field: 'customFieldList',\n      value: [\n        {\n          field: 'custentity_acustomfield',\n          operator: 'anyOf',\n          # type is needed for all search fields\n          type: 'SearchMultiSelectCustomField',\n          value: [\n            NetSuite::Records::CustomRecordRef.new(:internal_id =\u003e 1),\n            NetSuite::Records::CustomRecordRef.new(:internal_id =\u003e 2),\n          ]\n        },\n      \t{\n      \t  field: 'custbody_internetorder',\n      \t  type: 'SearchBooleanCustomField',\n      \t  value: true\n      \t}\n      ]\n    }\n  ]\n}).results\n\nNetSuite::Records::SalesOrder.search({\n  criteria: {\n    basic: [\n      # NOTE do not search for more than one transaction type at a time!\n      {\n        field: 'type',\n        operator: 'anyOf',\n        value: [ \"_invoice\"]\n      },\n      {\n        field: 'tranDate',\n        operator: 'within',\n        # this is needed for date range search requests, for date requests with a single param type is not needed\n        type: 'SearchDateField',\n        value: [\n          # NetSuite requires iso8601 time format\n          # need to require the time library for this to work\n          Time.parse(\"01/01/2012\").iso8601,\n          Time.parse(\"30/07/2013\").iso8601,\n\n          # or you can use a string. Note that the format below is different from the format of the above code\n          # but it matches exactly what NS returns\n          # \"2012-01-01T22:00:00.000-07:00\",\n          # \"2013-07-30T22:00:00.000-07:00\"\n        ]\n      }\n    ],\n\n    # equivilent to the 'Account' label in the GUI\n    accountJoin: [\n      {\n        field: 'internalId',\n        operator: 'noneOf',\n        value: [ NetSuite::Records::Account.new(:internal_id =\u003e 215) ]\n      }\n    ],\n\n    itemJoin: [\n      {\n        field: 'customFieldList',\n        value: [\n          {\n            field: 'custitem_apcategoryforsales',\n            operator: 'anyOf',\n            type: 'SearchMultiSelectCustomField',\n            value: [\n              NetSuite::Records::Customer.new(:internal_id =\u003e 1),\n              NetSuite::Records::Customer.new(:internal_id =\u003e 2),\n            ]\n          }\n        ]\n      }\n    ]\n  },\n\n  # the column syntax is a WIP. This will change in the future.\n  columns: {\n    'tranSales:basic' =\u003e [\n      'platformCommon:internalId/' =\u003e {},\n      'platformCommon:email/' =\u003e {},\n      'platformCommon:tranDate/' =\u003e {},\n      # If you include columns that are only part of the *SearchRowBasic (ie. TransactionSearchRowBasic),\n      # they'll be readable on the resulting record just like regular fields (my_record.close_date).\n      'platformCommon:closeDate/' =\u003e {}\n    ],\n    'tranSales:accountJoin' =\u003e [\n      'platformCommon:internalId/' =\u003e {}\n    ],\n    'tranSales:contactPrimaryJoin' =\u003e [\n      'platformCommon:internalId/' =\u003e {}\n    ],\n    'tranSales:customerJoin' =\u003e [\n      'platformCommon:internalId/' =\u003e {}\n    ],\n    'tranSales:itemJoin' =\u003e [\n      'platformCommon:customFieldList' =\u003e [\n        'platformCore:customField/' =\u003e {\n          '@scriptId' =\u003e 'custitem_apcategoryforsales',\n          # Or, for API versions 2013.1 and older:\n          # '@internalId' =\u003e 'custitem_apcategoryforsales',\n          '@xsi:type' =\u003e \"platformCore:SearchColumnSelectCustomField\"\n        }\n      ]\n    ]\n  },\n\n  preferences: {\n    page_size: 10,\n\n    # only returning body fields increases performance!\n    body_fields_only: true\n  }\n}).results\n\n# Search for SalesOrder records with a \"Pending Approval\" status using the TransactionStatus enum value.\n# https://system.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2016_2/schema/enum/transactionstatus.html\n\nNetSuite::Records::SalesOrder.search(\n  criteria: {\n    basic: [\n      {\n        field: 'type',\n        operator: 'anyOf',\n        value: ['_salesOrder'],\n      },\n      {\n        field: 'status',\n        operator: 'anyOf',\n        value: ['_salesOrderPendingApproval'],\n      },\n    ],\n  },\n)\n\nNetSuite::Records::ItemFulfillment.search({\n  criteria: {\n    basic: [\n      {\n        field: 'type',\n        operator: 'anyOf',\n        type: 'SearchEnumMultiSelectField',\n        value: [\"_itemFulfillment\"]\n      },\n      {\n        field: 'lastModifiedDate',\n        type: 'SearchDateField',\n        operator: 'within',\n        value: [\n          DateTime.now - 2.hours,\n          DateTime.now\n        ]\n      }\n    ],\n    createdFromJoin: [\n      {\n        field: 'type',\n        operator: 'anyOf',\n        value: [ '_salesOrder' ]\n      },\n      {\n        field: 'internalIdNumber',\n        operator: 'notEmpty'\n      }\n    ]\n  },\n  preferences: {\n    pageSize: 1000,\n    bodyFieldsOnly: false\n  }\n}).results\n\n# basic search with pagination / SearchMorewithId\nsearch = NetSuite::Records::Customer.search(\n  criteria: {\n    basic: [\n      {\n        # no operator for booleans\n        field: 'isInactive',\n        value: false,\n      },\n    ]\n  },\n\n  preferences: {\n    page_size: 10,\n  }\n)\n\nsearch.results_in_batches do |batch|\n  puts batch.map(\u0026:internal_id)\nend\n\n# item search\nNetSuite::Records::InventoryItem.search({\n  criteria: {\n    basic: [\n      {\n        field: 'type',\n        operator: 'anyOf',\n        type: 'SearchEnumMultiSelectField',\n        value: [\n          '_inventoryItem',\n\n          # note that the naming conventions aren't consistent: AssemblyItem != _assemblyItem\n          # https://system.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2014_1/schema/enum/itemtype.html\n          '_assembly'\n        ]\n      },\n      {\n        field: 'isInactive',\n        value: false\n      }\n    ]\n  }\n}).results.first\n\n# set body_fields_only = false to include the majority of lists associated with records in the XML response\n# Some lists you just can't include with searches (a customer's AddressBookList, for example)\n\n# In order to get the full record data for those records whose lists aren't included when body_fields_only = false\n# you will have to run a get_list call on the resulting internalIds returned from the search you've executed\n\nsearch = NetSuite::Records::File.search({\n  preferences: {\n    body_fields_only: false,\n    page_size: 20\n  }\n})\n\nsearch.results_in_batches do |batch|\n  batch.each do |file|\n    next unless file.file_type == \"_JAVASCRIPT\"\n    puts Base64.decode64(file.content)\n  end\nend\n\n# the getList operation\nNetSuite::Records::CustomRecord.get_list(\n  # netsuite internalIDs\n  list: [1,2,3],\n  # only needed for a custom record\n  type_id: 1234,\n  # allow inclomplete results (defaults to false)\n  allow_incomplete: true\n).each do |record|\n  # do your thing...\nend\n\n# Adding a Customer Deposit example. The customer associated with the\n# sales order would be linked to the deposit.\n\ndeposit = CustomerDeposit.new\ndeposit.sales_order = RecordRef.new(internal_id: 7279)\ndeposit.payment = 20\ndeposit.add\n```\n\n## Getting Deleted Records\n\n```ruby\nresponse = NetSuite::Records::LotNumberedInventoryItem.get_deleted({\n  criteria: [\n    {\n      # If you don't specify a type criteria, you'll get all deleted records,\n      # regardless of the type of record you called this on.\n      field: 'type',\n      operator: 'anyOf',\n      value: 'lotNumberedInventoryItem',\n    }\n  ],\n})\n\nArray(response.body.fetch(:deleted_record_list)).first\n# =\u003e {\n#      :deleted_date =\u003e Wed, 16 Feb 2022 17:43:45 -0800,\n#      :record =\u003e {\n#        :name =\u003e \"My Item\",\n#        :@internal_id =\u003e \"12485\",\n#        :@type =\u003e \"lotNumberedInventoryItem\",\n#        :\"@xsi:type\" =\u003e \"platformCore:RecordRef\"\n#      }\n#    }\n\n# deleted_record_list could be:\n#   nil - No records matching criteria were deleted\n#   Hash - A single record matching criteria was deleted\n#   Array - Multiple records matching criteria were deleted\n\n# Simple pagination\npage = 1\nbegin\n  response = NetSuite::Records::LotNumberedInventoryItem.get_deleted({\n    criteria: [\n      # your criteria\n    ],\n    page: page,\n  })\n\n  # Do your thing with response.body.fetch(:deleted_record_list)\n\n  page += 1\nend until page \u003e Integer(response.fetch(:total_pages))\n```\n\n## Non-standard Operations\n\n```ruby\n# making a call that hasn't been implemented yet\nNetSuite::Configuration.connection.call :get_customization_id, message: {\n  'platformMsgs:customizationType' =\u003e { '@getCustomizationType' =\u003e 'customRecordType'},\n  'platformMsgs:includeInactives' =\u003e 'false'\n}\n\nserver_time_response = NetSuite::Configuration.connection.call :get_server_time\nserver_time_response.body[:get_server_time_response][:get_server_time_result][:server_time]\n\n# getting a list of states\nstates = NetSuite::Configuration.connection.call(:get_all, message: {\n  'platformCore:record' =\u003e {\n    '@recordType' =\u003e 'state'\n  }\n})\nstates.to_array.first[:get_all_response][:get_all_result][:record_list][:record].map { |r| { country: r[:country], abbr: r[:shortname], name: r[:full_name] } }\n```\n","funding_links":["https://github.com/sponsors/iloveitaly"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetsweet%2Fnetsuite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnetsweet%2Fnetsuite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetsweet%2Fnetsuite/lists"}