{"id":20463959,"url":"https://github.com/netsweet/netsuite_rails","last_synced_at":"2025-04-13T08:33:31.684Z","repository":{"id":24639427,"uuid":"28048763","full_name":"NetSweet/netsuite_rails","owner":"NetSweet","description":"Easily integrate a ruby on rails app with NetSuite","archived":false,"fork":false,"pushed_at":"2017-12-06T14:28:28.000Z","size":183,"stargazers_count":12,"open_issues_count":24,"forks_count":5,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-05-01T11:36:09.279Z","etag":null,"topics":["netsuite"],"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":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-15T17:52:48.000Z","updated_at":"2023-11-20T05:17:51.000Z","dependencies_parsed_at":"2022-08-21T00:50:11.601Z","dependency_job_id":null,"html_url":"https://github.com/NetSweet/netsuite_rails","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite_rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite_rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite_rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetSweet%2Fnetsuite_rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NetSweet","download_url":"https://codeload.github.com/NetSweet/netsuite_rails/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224784883,"owners_count":17369461,"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":["netsuite"],"created_at":"2024-11-15T13:13:41.507Z","updated_at":"2024-11-15T13:13:42.416Z","avatar_url":"https://github.com/NetSweet.png","language":"Ruby","readme":"[![Circle CI](https://circleci.com/gh/NetSweet/netsuite_rails.svg?style=svg)](https://circleci.com/gh/NetSweet/netsuite_rails)\n[![Slack Status](https://opensuite-slackin.herokuapp.com/badge.svg)](http://opensuite-slackin.herokuapp.com)\n\n# NetSuite Rails\n\n**\u003cspan style=\"color: red\"\u003eNote:\u003c/span\u003e** Documentation is horrible: PRs welcome. Look at the code for details.\n\nBuild Ruby on Rails applications that sync ActiveRecord (ActiveModel and plain old ruby objects too) in real-time to NetSuite. Here's an example:\n\n```ruby\nclass Item \u003c ActiveRecord::Base\n  include NetSuiteRails::RecordSync\n\n  # specify the NS record that your rails model maps to\n  netsuite_record_class NetSuite::Records::InventoryItem\n\n  netsuite_sync :read_write,\n    # specify the frequency that your app should poll NetSuite for updates\n    frequency: 1.day,\n    # it's possible to base syncing off of a saved search. Be sure that \"Internal ID\" is one of your search result columns\n    saved_search_id: 123,\n    # limit pushing to NetSuite based on conditional\n    if: -\u003e { self.a_condition? },\n    # limit pulling from NetSuite based on conditional. This is only\n    # considered when handling a single pull\n    pull_if: -\u003e { self.another_condition? },\n\n    # accepted values are :async and :sync. Default is :async\n    mode: :sync\n\n\n  # local =\u003e remote field mapping\n  netsuite_field_map({\n    :item_number =\u003e :item_id,\n    :name =\u003e :display_name,\n\n    # the corresponding NetSuite field must be manually specified in before_netsuite_push\n    :user =\u003e Proc.new do |local_rails_record, netsuite_record, sync_direction|\n    \tif direction == :pull\n\n    \telsif direction == :push\n\n    \tend\n    end,\n\n    :custom_field_list =\u003e {\n    \t:a_local_field =\u003e :custrecord_remote_field\n    \t:a_special_local_field =\u003e Proc.new do |local, ns_record, direction|\n    \t\tif direction == :push\n    \t\t  # if proc is used with a field mapping, the field must be specified in `netsuite_manual_fields`\n    \t\t  ns_record.custom_field_list.custentity_special_long = 1\n          ns_record.custom_field_list.custentity_special_long.type = 'platformCore:LongCustomFieldRef'\n    \t\tend\n    \tend\n    }\n  })\n\n  # sanitizes input from rails to ensure NS doesn't throw a fatal error\n  netsuite_field_hints({\n    :phone =\u003e :phone,\n    :email =\u003e :email\n  })\n\n  before_netsuite_push do |netsuite_record|\n    self.netsuite_manual_fields = [ :entity, :custom_field_list ]\n  end\nend\n```\n\nYour ruby model:\n\n* Needs to have a `netsuite_id` and `netsuite_id=` method\n* Does not need to be an `ActiveRecord` model. If you don't use ActiveRecord it is your responsibility\n  to trigger `Model#netsuite_push`.\n\nNotes:\n\n* If `sync_mode == :async` `model.save` will be run if a record is created referencing an existing NetSuite object: `model.create! netsuite_id: 123`\n* If you are using `update`, a `update` call will not be run if no changed fields are detected. If you are manually using fields specify them with `netsuite_manual_fields`\n\n## Using Upsert\n\nTODO generating external ID tag\n\nTODO configuring upsert\n\nTODO add vs upsert consideration\n\n## Installation\n\n```ruby\ngem 'netsuite_rails'\n```\n\nInstall the database migration to persist poll timestamps:\n\n```bash\nrails g netsuite_rails:install\n```\n\nThis helps netsuite_rails to know when the last time your rails DB was synced with the NS.\n\n## Date\n\n\n## Time\n\n\"Time of Day\" fields in NetSuite are especially tricky. To ensure that times don't shift when you push them to NetSuite here are some tips:\n\n1. Take a look at the company time zone setup. This is in Setup\n2. Ensure your WebService's Employee record has either:\n  * No time zone set\n  * The same time zone as the company\n3. Ensure that the WebService's GUI preferences have the same time zone settings as the company. This effects how times are translated via SuiteTalk.\n4. Set the `netsuite_instance_time_zone_offset` setting to your company's time zone\n\n```ruby\n# set your timezone offset\nNetSuiteRails::Configuration.netsuite_instance_time_zone_offset(-6)\n```\n\n### Changing WebService User's TimeZone Preferences\n\nIt might take a couple hours for time zone changes to take effect. [From my experience](http://mikebian.co/netsuite-suitetalk-user-role-edits-are-delayed/), either the time zone changes have some delay associated with them or the time zone implementation is extremely buggy.\n\n## Usage\n\n### Syncing Options\n\n```\nnetsuite_record_class NetSuite::Records::Customer\nnetsuite_record_class NetSuite::Records::CustomRecord, 123\n\nnetsuite_sync: :read\nnetsuite_sync: :read_write\n# TODO not after_netsuite_push replacement for aggressive sync\n\nnetsuite_sync: :read, frequency: :never\nnetsuite_sync: :read, frequency: 5.minutes\nnetsuite_sync: :read, if: -\u003e { self.condition_met? }\n\n```\n\nWhen using a proc in a NS mapping, you are responsible for setting local and remote values\n\nThe default sync frequency is [one day](https://github.com/NetSweet/netsuite_rails/blob/c453326a4190e68a2fd9d7690b2b1f2f105ec8b9/lib/netsuite_rails/poll_trigger.rb#L27).\n\nfor pushing tasks to DJ https://github.com/collectiveidea/delayed_job/wiki/Rake-Task-as-a-Delayed-Job\n\n`:if` for controlling when syncing occurs\n\nEasily disable/enable syncing via env vars:\n\n```ruby\nNetSuiteRails.configure do\n  netsuite_pull_disabled ENV['NETSUITE_PULL_DISABLED'].present? \u0026\u0026 ENV['NETSUITE_PULL_DISABLED'] == \"true\"\n  netsuite_push_disabled ENV['NETSUITE_PUSH_DISABLED'].present? \u0026\u0026 ENV['NETSUITE_PUSH_DISABLED'] == \"true\"\n\n  if ENV['NETSUITE_DISABLE_SYNC'].present? \u0026\u0026 ENV['NETSUITE_DISABLE_SYNC'] == \"true\"\n    netsuite_pull_disabled true\n    netsuite_push_disabled true\n  end\nend\n\n```\n\n### Hooks\n\n```ruby\n# the netsuite record is passed a single argument to this block (or method reference)\n# this provides the opportunity to set custom fields or run custom logic to prepare\n# the record for the NetSuite envoirnment\nbefore_netsuite_push\nafter_netsuite_push\n\n# netsuite_pulling? is true when this callback is executed\nafter_netsuite_pull\n```\n\n### Rake Tasks for Syncing\n\n```bash\n# update \u0026 create local records modified in netsuite sync the last sync time\nrake netsuite:sync\n\n# pull all records in NetSuite and update/create local records\nrake netsuite:fresh_sync\n\n# only update records that have already been synced\nrake netsuite:sync_local RECORD_MODELS=YourModel LIST_MODELS=YourListModel\n```\n\nCaveats:\n\n* If you have date time fields, or custom fields that will trigger `changed_attributes` this might cause issues when pulling an existing record\n* `changed_attributes` doesn't work well with `store`s\n\n### Delayed Job\n\nThe more records that use netsuite_rails, the longer you'll need your job timeout to be:\n\n```ruby\n# config/initializers/delayed_job.rb\nDelayed::Worker.max_run_time = 80.minutes\n```\n\n## Non-AR Backed Model\n\nImplement `changed_attributes` in your non-AR backed model\n\n## Testing\n\n```ruby\n# in spec_helper.rb\nrequire 'netsuite_rails/spec/spec_helper'\n```\n\nUsing the helpers:\n\n```ruby\nit 'does something' do\n  # setup...\n\n  # action\n\n  ns_customer = get_last_netsuite_object(NetSuite::Records::Customer)\nend\n```\n\n# Syncing Using Rake Tasks\n\n```ruby\n# clockwork.rb\nevery(1.minutes, 'netsuite sync') {\n  # prevent multiple netsuite:sync DJ commands from being added; only one is needed in the queue at a time\n  unless Delayed::Job.where(failed_at: nil, locked_by: nil).detect { |j| j.payload_object.class == DelayedRake \u0026\u0026 j.payload_object.task == 'netsuite:sync'}\n    Delayed::Job.enqueue DelayedRake.new(\"netsuite:sync\")\n  end\n}\n\n# schedule.rb\n# DelayedRake: https://github.com/collectiveidea/delayed_job/wiki/Rake-Task-as-a-Delayed-Job\nevery 2.minutes do\n  runner 'Delayed::Job.enqueue(DelayedRake.new(\"netsuite:sync\"),priority:1,run_at: Time.now);'\nend\n```\n\n## Author\n\n* Michael Bianco @iloveitaly\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetsweet%2Fnetsuite_rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnetsweet%2Fnetsuite_rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetsweet%2Fnetsuite_rails/lists"}