Building One Integration to Multiple Ledgers

Modern Treasury now supports syncing to both NetSuite and QuickBooks. The transition to supporting multiple accounting systems introduced a variety of technical challenges, as each individual system is unique. This post will be an overview of the approach we took and the lessons we learned.
Overview
Accounting systems such as NetSuite and QuickBooks are used by millions of businesses to keep track of finances, manage inventory, and more. We’ve described the role they play in .
Complexity in supporting multiple accounting systems stems from the uniqueness of each individual system. We refer to objects in accounting systems as resources. Examples of resources are customers, vendors, accounts, and transactions.
There are three challenges we encountered in the process of adding Netsuite. The first challenge is that the resources available to developers differ across accounting systems. For example, QuickBooks has a single Deposit resource but NetSuite has both Deposit and CustomerDeposit resources. Even when the same resource exists across accounting systems, a resource for a given system may have different attributes from its counterparts in other systems. Finally, there is varying support for operations we can perform on a resource, such as creation or deletion. A pronounced example of this is in QuickBooks, where an account resource cannot be deleted.
Accounting software systems typically support the concepts of vendors and customers. We have built support for these objects into LedgerSync, our open-sourced ledger-connector library. LedgerSync tries to remain “unopinionated” about accounting systems, giving developers the freedom to choose the most suitable resources to use for their use-case. The goal of the library is to have resources, attributes, and operations that match as closely to the API as possible.
Imagine you run Modern Brinery, a gourmet pickle vendor, and you need to sync your supplier Sonoma Salt to both NetSuite and QuickBooks. In Modern Treasury, a counterparty is an entity outside of your organization that you want to send or receive money from. A counterparty can be an individual or a business. Here is a simplified counterparty object for Sonoma Salt:
1{
2 "id": "928db55e-6552-4aaf-96d7-10c693922b1f",
3 "name": "Sonoma Salt",
4 "email": "salt@sonomasalt.com"
5}
Approach #1 Creating Resources Directly
Our first approach will be to create the LedgerSync resources directly and then perform an operation that will create a vendor object in our desired accounting system. Here's how that looks for QuickBooks:
1vendor = LedgerSync::Ledgers::QuickBooks::Vendor.new(
2external_id: counterparty.id,
3display_name: counterparty.name,
4email: counterparty.email
5)
6# client is an authenticated LedgerSync client
7quickbooks_online_client.operation_for(method: :create, resource: vendor).perform
And for NetSuite:
1vendor = LedgerSync::Ledgers::NetSuite::Vendor.new(
2external_id: counterparty.id,
3company_name: counterparty.name,
4email: counterparty.email
5)
6netsuite_client.operation_for(method: :create, resource: vendor).perform
The issue with this approach is that it requires a developer to have system-specific knowledge for every interaction with LedgerSync or to define a new resource, operation, and validation layers entirely.
Approach #2 Using LedgerSync Converters
To handle more complex resources, we built a resource converter class into LedgerSync. We can use this utility class to convert our counterparty into a LedgerSync resource.
Here's what a converter class looks like for a QuickBooks Vendor:
1module QuickBooks
2 class VendorExporter < LedgerSync::Util::ResourceConverter
3 attribute :external_id, source_attribute: :id
4 attribute :display_name, source_attribute: :name
5 attribute :email
6 end
7end
And for a NetSuite Vendor:
1module NetSuite
2 class VendorExporter < LedgerSync::Util::ResourceConverter
3 attribute :external_id, source_attribute: :id
4 attribute :company_name, source_attribute: :name
5 attribute :email
6 end
7end
And here's how we call them:
1# QuickBooks
2quickbooks_online_vendor = LedgerSync::Ledgers::QuickBooks::Vendor.new()
3vendor = QuickBooks::VendorExporter.new.convert(source: counterparty, destination: quickbooks_online_vendor)
4
5# NetSuite
6ns_vendor = LedgerSync::Ledgers::NetSuite::Vendor.new()
7vendor = NetSuite::VendorExporter.new.convert(source: counterparty, destination: ns_vendor)
8
9# To sync to the ledger
10client.operation_for(method: :create, resource: vendor).perform
Converters simplify operations by providing a common interface across ledgers and are especially useful for more complex objects with many references.
Ledger integrations at Modern Treasury have been built to exclusively use converters. This scalable approach means we can add additional ledger integrations faster and provide more granular configuration options for our customers.
Summary
Moving to a new ERP system doesn’t have to be difficult. Our aim is to make shipping payments data to any ERP easy and seamless. If you’re interested in learning how your company can use Modern Treasury, please reach out.
Try Modern Treasury
See how smooth payment operations can be.
Subscribe to Journal updates
Discover product features and get primers on the payments industry.