Depositing Transaction Records in NetSuite

Moving transactions in NetSuite from "Not Deposited" to "Deposited" is not straightforward when using NetSuite SuiteTalk.

You need to ensure that undocumented requirements for each record type are met. After you’ve validated that your records are properly configured, you can include them in a new deposit using the following structure:

ns_deposit = NetSuite::Records::Deposit.new ns_deposit.payment_list << { deposit: true, # internalId of a CashSale, CustomerPayment, CustomerRefund, CustomerDeposit, etc id: 123, # the amount is not required if the currency of the payment is the same as the currency # of the bank account that the deposit is posting to payment_amount: 100.0 } ns_deposit.add

Note that you don’t need doc, type, or any of the other fields available on the a DepositPayment item. It’s also important to note that the other fields on the DepositPayment don’t actually effect how NetSuite handles the referenced transaction record. deposit, id, and payment_amount are the only fields that matter when referencing a NetSuite transaction on a deposit.

Continue Reading

Using Browser Sync with Rails and Local SSL

I’ve always been a fan of LiveReload since I first saw it years ago. However, the rack-livereload gem hasn’t been updated in a long while, and the livereload parent project seems to have died out too. rack-liveload doesn’t support reloading on local HTTPS connections, which was a requirement on a recent project—I needed a new tool.

BrowserSync came to the rescue.

In most cases, using BrowserSync’s proxy mode to reverse proxy a rails app running via a *.dev pow domain works well. For this particular project, there were a couple of specific issues:

Many pages on the project force HTTPS. A common pattern is to disable HTTPS in development: I prefer to keep the development and production delta as small as possible, and keep any HTTPS redirects enabled in production active in development as well. The site used oauth for local user authentication. Each oauth redirect URL needs to be specified in each oauth provider’s admin. This is a pain to manage, and becomes more a pain when a reverse proxy with a unique domain is used sometimes. The SSL certs served by tunnelss were not valid for the BrowserSync server causing browsers to throw invalid certificate errors.

After a bit of digging, I found a way to use my existing *.dev setup with BrowserSync. Here’s how:

Setup Apache + Pow + Tunnelss

Read through this blog post to setup your development environment to serve SSL.

Point Browser Sync to Tunnelss-generated SSL Certificates

BrowserSync has a bunch of configuration options. Some are inaccessible via the command line interface and need to be specified in a javascript file passed to the command line app.

Here’s how to configure BrowserSync to use the tunnelss-generated SSL certs to serve the secure web sockets it uses for syncing:

// https://www.browsersync.io/docs/options module.exports = { // "logLevel": "debug", "files": [ '**/*.css*', '**/*.html*', '**/*_controller.rb', '**/*.md' ], "https": { "key": process.env['HOME'] + "/.tunnelss/key.pem", "cert": process.env['HOME'] + "/.tunnelss/server.crt" } };

Throw this configuration file in bs-config.js in the root level of your project and start the BrowserSync server with:

browser-sync start --config bs-config.js Connect Your Rails (or Sintra) App to BrowserSync

To connect your application to the BrowserSync server, throw this simple javascript tag in your layout:

<% if Rails.env.development? %> <script type='text/javascript' id="__bs_script__">//<![CDATA[ document.write("<script async src='https://HOST:3000/browser-sync/browser-sync-client.js'></script>".replace("HOST", location.hostname)); //]]></script> <% end %>

Note that the above snippet does not define a browser sync version in the javascript. This makes this code a bit more resilient to browser sync updates.

I prefer this simple JS snippet to a middleware (like rack-livereload) that inserts the JS snippet into your site automatically. Keeping this code in your layout makes it easy to upgrade the code in the future, and is more declarative to new developers working on the project.

Continue Reading

Using HTTPS Locally: Pow, SSL, Rails, and Apache

Using HTTPS local development or testing environments is a hassle. A common practice is to disable SSL in development, but I’ve had too many cases of minor bugs creeping in when routing works differently between environments. I also don’t like manually having to let my browsers know that a self-signed certificate is valid.

Here’s the configuration that I use to easily add https support on my development machine to any application served—or proxied—through port 80.

Pow

I use Pow as my development rails server. However, because I often work with other languages as well, I run Apache (you could just as easily use nginx) and reverse proxy Rails application requests to Pow.

To do this, you’ll need Pow to bind to a port that is not port 80. Here’s how to configure Pow to use port 88.

Apache

I use nginx in production environments, but I’ve been using Apache on my local development machines for years and just haven’t gotten around to changing my dotfiles to use nginx. You could just as easily use nginx here.

In any case, here’s what I was looking to achieve in my apache config:

Throw error logs in ~/Sites/logs Support vhosts + xip.io domains Access WordPress sites using site-name.wp.dev Access Rails applications using site-name.dev. These requests need to reverse proxy to pow on port 88. Support proxying *.test domains to a customized port. Having a *.test domain allows you to run SSL-enabled integration tests for a multi-domain Rails application against the rspec-run rails server. Checkout my series on rails testing for more details on why this is important.

I was able to get all of this working with a single file thrown in /etc/apache2/other.

Rails: Tunnelss to the Rescue

Tunnelss is a little gem that brings it all together. It looks at your pow config, generates self-signed certificates for those domains, and adds that certificate to your keychain so your browsers accept the self-signed certificate. Brilliant.

The only downside here is you must keep tunnelss running in the background. Right now, the project doesn’t have a launchd plist available. So, for now, you have to start the application manually on each system restart.

Continue Reading

How to Refund a Customer Deposit using NetSuite SuiteTalk

When refunding a NetSuite CustomerRefund or CashSale you’ll add it to apply_list:

refund.apply_list = { apply: [ { doc: 123, # internalId of the CustomerRefund apply: true } ] }

Refunding a CustomerDeposit works a bit differently. If you inspect the SuiteTalk XML response for a CustomerRefund created for a CustomerDeposit it will appear under the apply_list XML tag. If you use that XML tag when creating the CustomerRefund you’ll get the following error:

Unable to find a matching line for sublist apply with key

Instead, you need to use the deposit_list tag:

refund.deposit_list = { customer_refund_deposit: [ { doc: 123, # internalId of CustomerDeposit apply: true } ] }

Continue Reading

Determine Deposited Status of CustomerRefunds in NetSuite

Most payment and refund records in NetSuite have a straightforward way to determine if they’ve been deposited or not. The CustomerRefund is one exception: there is no way to determine from the GUI, or from the SuiteTalk API response for a CustomerRefund, if a given CustomRefund has been linked to a deposit record.

However, you can search for CustomerRefunds which are deposited or undeposited using a couple poorly documented search filters:

NetSuite::Records::CustomerRefund.search( criteria: { basic: [ { field: 'type', operator: 'anyOf', value: [ '_customerRefund' ] }, { field: 'applyingLinkType', operator: 'noneOf', value: [ '_depositRefundCheck' ] }, { field: 'mainLine', value: true }, { field: 'account', operator: 'anyOf', # undep funds account reference value: [ NetSuite::Records::RecordRef.new(internal_id: 6) ] } ] }, preferences: { page_size: 250 } )

Continue Reading

How to Find the NetSuite Web Services Account Number

Finding the account number for NetSuite’s SuiteTalk WebService API isn’t very straightforward. This number is required for any applications with connect to NetSuite via their SuiteTalk API.

Navigate to the SuiteTalk preferences via "Setup > Integration > Web Services Preferences", by searching for "page: Web Services Preferences" in the global search bar, or by visiting this URL (may not work depending on your NetSuite data center). You’ll see the following screen containing the Account Number (also called the Account ID):

Some things to keep in mind:

The account number is not always a number. It can be a string of letters. A sandbox and production account number is identical The “Disable Service SuiteScript and Workflow Triggers” checkbox can have massive effects on your SuiteTalk integration. SuiteScripts and Workflows can trigger fatal errors in your SuiteTalk calls which are impossible to debug without diving into the SuiteScripts embedded within a NetSuite instance.

Continue Reading

Resolving the NetSuite LIST_ID_REQD Deposit Record Error

Creating deposit records in NetSuite using the SuiteTalk API can sometimes result in the following error:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Header> <platformMsgs:documentInfo xmlns:platformMsgs="urn:messages_2015_1.platform.webservices.netsuite.com"> <platformMsgs:nsId>REDACTED</platformMsgs:nsId> </platformMsgs:documentInfo> </soapenv:Header> <soapenv:Body> <upsertResponse xmlns="urn:messages_2015_1.platform.webservices.netsuite.com"> <writeResponse> <platformCore:status xmlns:platformCore="urn:core_2015_1.platform.webservices.netsuite.com" isSuccess="false"> <platformCore:statusDetail type="ERROR"> <platformCore:code>LIST_ID_REQD</platformCore:code> <platformCore:message>Required field missing in a related list. You must set lineId.</platformCore:message> </platformCore:statusDetail> </platformCore:status> <baseRef xmlns:platformCore="urn:core_2015_1.platform.webservices.netsuite.com" externalId="external_id" type="deposit" xsi:type="platformCore:RecordRef"/> </writeResponse> </upsertResponse> </soapenv:Body> </soapenv:Envelope>

This error is not well documented at all and does not point to a single issue. In order to properly handle and prevent this error from occurring you need to implementation validation before upserting (or adding, updating, etc) the record.

Here are the various causes of the error:

The included refund’s (CashRefund, CustomerRefund) account field is not set to the NetSuite instance’s “Undeposited Funds” account. The easiest way to determine what the “Undeposited Funds” account internalId is to search for the account by name and grab the first search result. The name of the “Undeposited funds account can be customized. If this is the case, you need to hard code the internalId in your app’s config. The payment (CustomerPayment, CashPayment, CustomerDeposit) is already deposited. You can test this through the status field on these record. The status field will have a value of Deposited or Not Deposited. The payment sublist filters in the deposit sublist are causing the given payment record to be unavailable for the deposit. For example, if your GUI payment sublist date filters are configured to only show payments for the current day, and your deposit request using SuiteTalk includes a record created yesterday. In this case, the GUI settings (which cannot be modified or inspecting by SuiteTalk) are causing a fatal error in SuiteTalk. Instructions on how to resolve this error are detailed below. The subsidiary or bank account restrictions are causing your included payment to be unavailable. The best way to see if this is the case is to attempt to mimic the SuiteTalk request in the GUI. Clearing the Deposit Payment Sublist Filters

Here’s how you clear the deposit payment sublist date filters.

Go to the new deposit record page: https://system.netsuite.com/app/accounting/transactions/deposit.nl. Note that this URL can be different depending on your data center URL. Choose “All” on the date filter drop down. The From/To filters should disappear. Press cancel Go to the new deposit page again and ensure that the changes “stuck” Logout Wait 8-24 hours for the NetSuite user cache to invalidate. This is an internal cache that you cannot monitor.

In addition to clearing the date filter, click the customize button and ensure that no additional filters have been added to the payment sublist filters. You are aiming for a completely stock configuration.

Continue Reading

How to Close a NetSuite SalesOrder Using SuiteTalk

Many things in NetSuite’s SuiteTalk XML API are not intuitive or obvious: closing a SalesOrder is one of them.

When you create a SalesOrder using upsert or add you can set the order_status to _pendingFulfillment or _pendingApproval directly through the order_status field. However, you can’t simply update the order_status field on a SalesOrder to close the record, you need to set is_closed field on each line item in the SalesOrder.

ns_order.item_list.items do |item| item.is_closed = true end ns_order.update({ item_list: ns_order.item_list })

Other SalesOrder states work in a similar way: _partiallyFulfilled, _fulfilled, etc are only achieved by modified the item_list sublist or by creating a separate record (ItemFulfillment, Invoice, etc).

Continue Reading

NetSuite SuiteTalk User & Role Edits Are Delayed

If you work with NetSuite’s SuiteTalk API, you know that:

You must access the API via employee/user record in NetSuite API calls specify the user role to use You will get different results based on which user and which user role you are accessing the API with. For instance, if the employee record has the “Sales Rep” option checked, all Sales Orders created using that employee/user credentials will have the “Sales Rep” field assigned to the user’s record. Each role has a different set of permissions. You will get different fatal error messages depending on which user role you are operating under.

Often, when developing a NetSuite integration, you’ll need to make edits to the SuiteTalk user/employee record or the role.

Changes to the role or user do not take effect immediately. They can take anywhere from a couple minutes to a couple hours to take effect. This destroys the developer feedback loop and makes it exponentially harder to track down the source of specific NetSuite SuiteTalk errors.

This means that if you make a change in your sandbox or production account relating to either employee or user records there is no way to know if the change fixed the issue you were debugging aside from waiting a couple hours and retrying the operation.

Often, what I’ve done, is write a failing feature test for my NetSuite integration issue I’m encountering and then rerun the test in the morning after making user or role tweaks.

If you have access to the private NetSuite forums, you can read this post for more information.

Continue Reading

Install PHP with PNG Support on Apache in Yosemite

Yosemite comes with Apache + PHP out of the box, but its PHP binary is a bit handicapped. It doesn’t come with built in PNG support.

This causes issues if you are developing WordPress sites: resizing PNG images will silently fail. If you have custom image sizes set in your WordPress theme, they will not be created at all if PHP is not compiled with PNG support.

Here’s how to recompile PHP on Yosemite via Brew:

brew tap homebrew/dupes brew tap homebrew/versions brew tap homebrew/homebrew-php brew install freetype jpeg libpng gd brew install php56

You’ll have to edit your Apache configuration to use the new PHP installation:

nano /etc/apache2/httpd.conf # add the following line to the above file LoadModule php5_module /usr/local/opt/php56/libexec/apache2/libphp5.so # then save, exit nano, and run this command sudo apachectl restart

There are also some additional WordPress & PHP tools you can install via brew.

Continue Reading