Skip to main content

Transaction Link Tables

NetSuite provides specialized junction tables for querying relationships between linked transactions. These tables are essential when the createdfrom field is not exposed in SuiteQL.


Overview

TRANSACTION LINK TABLES
═══════════════════════════════════════════════════════════════════════════════

HEADER-LEVEL LINE-LEVEL
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
Parent ───▶ │ NextTransactionLink │ Parent ──▶│NextTransactionLineLink│
│ │ │ │
└─────────────────────┘ └─────────────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
Child ────▶ │PreviousTransactionLink│ Child ──▶│PreviousTransactionLineLink│
│ │ │ │
└─────────────────────┘ └─────────────────────┘

Use Case:
- Sales Order → Invoice, Fulfillment, Credit Memo
- Purchase Order → Item Receipt, Vendor Bill
- Transfer Order → Fulfillment, Receipt

TableLevelDirectionUse Case
NextTransactionLineLinkLineParent → ChildGet child transactions from parent
PreviousTransactionLineLinkLineChild → ParentGet parent from child transaction
NextTransactionLinkHeaderParent → ChildHeader-only linking
PreviousTransactionLinkHeaderChild → ParentHeader-only reverse lookup
When to Use Which
  • Use Line-Level tables when you need to track quantities, amounts, or line-specific relationships
  • Use Header-Level tables for simpler parent-child lookups without line detail
  • Both directions contain the same data - choose based on your starting point

The primary table for querying transaction relationships at the line level.

ColumnTypeDescription
PreviousDocIntegerSource/parent transaction ID
PreviousTypeStringSource transaction type (e.g., SalesOrd)
PreviousLineIntegerSource transaction line number
NextDocIntegerDestination/child transaction ID
NextTypeStringDestination transaction type (e.g., CustInvc, ItemShip)
NextLineIntegerDestination transaction line number
LinkTypeStringRelationship type code
QuantityDecimalQuantity transferred between transactions
ForeignAmountDecimalLine item amount in transaction currency
StatusStringLink status (active, pending, completed)
LastModifiedDateDateWhen the link was established

Common LinkType Values

LinkTypeMeaningExample Flow
ShipRcptShip/ReceiptSO → Item Fulfillment
OrdBillOrder to BillSO → Invoice
DropShipDrop ShipSO → PO (drop ship)
RcptBillReceipt to BillItem Receipt → Vendor Bill
OrdRcptOrder to ReceiptPO → Item Receipt

Common Transaction Type Codes

CodeTransaction Type
SalesOrdSales Order
CustInvcCustomer Invoice
ItemShipItem Fulfillment
CustCredCredit Memo
PurchOrdPurchase Order
ItemRcptItem Receipt
VendBillVendor Bill
RtnAuthReturn Authorization (RMA)
TrnfrOrdTransfer Order

Basic Queries

Get All Child Transactions from Sales Order

-- Find all transactions created from Sales Order ID 12345
SELECT DISTINCT
NTLL.NextDoc AS child_id,
NTLL.NextType AS child_type,
t.tranid AS child_tranid,
t.trandate,
BUILTIN.DF(t.status) AS status,
t.foreigntotal AS amount
FROM NextTransactionLineLink NTLL
INNER JOIN Transaction t ON t.id = NTLL.NextDoc
WHERE NTLL.PreviousDoc = 12345
ORDER BY t.trandate, t.id

Sample Result:

child_idchild_typechild_tranidtrandatestatusamount
12350ItemShipIF-0012024-12-01ShippedNULL
12355CustInvcINV-0012024-12-02Open5000.00

Get Parent Transaction from Invoice

-- Find the Sales Order that created Invoice ID 12355
SELECT DISTINCT
PTLL.PreviousDoc AS parent_id,
PTLL.PreviousType AS parent_type,
t.tranid AS parent_tranid,
t.trandate
FROM PreviousTransactionLineLink PTLL
INNER JOIN Transaction t ON t.id = PTLL.PreviousDoc
WHERE PTLL.NextDoc = 12355

Get Complete Transaction Chain

-- Full chain: SO → Fulfillment → Invoice
SELECT DISTINCT
so.tranid AS sales_order,
so.trandate AS order_date,
ship.tranid AS fulfillment,
ship.trandate AS ship_date,
inv.tranid AS invoice,
inv.trandate AS invoice_date
FROM Transaction so
-- SO to Fulfillment
LEFT JOIN NextTransactionLineLink ntll1
ON ntll1.PreviousDoc = so.id
AND ntll1.NextType = 'ItemShip'
LEFT JOIN Transaction ship ON ship.id = ntll1.NextDoc
-- SO to Invoice
LEFT JOIN NextTransactionLineLink ntll2
ON ntll2.PreviousDoc = so.id
AND ntll2.NextType = 'CustInvc'
LEFT JOIN Transaction inv ON inv.id = ntll2.NextDoc
WHERE so.type = 'SalesOrd'
AND so.id = 12345

Line-Level Queries

Track Quantity Flow Between Transactions

-- See how quantities flow from SO lines to fulfillments
SELECT
so.tranid AS sales_order,
sol.linesequencenumber AS so_line,
sol.item,
BUILTIN.DF(sol.item) AS item_name,
sol.quantity AS ordered_qty,
NTLL.Quantity AS shipped_qty,
ship.tranid AS fulfillment,
shipl.linesequencenumber AS ship_line
FROM Transaction so
INNER JOIN TransactionLine sol ON sol.transaction = so.id
INNER JOIN NextTransactionLineLink NTLL
ON NTLL.PreviousDoc = so.id
AND NTLL.PreviousLine = sol.linesequencenumber
INNER JOIN Transaction ship ON ship.id = NTLL.NextDoc
INNER JOIN TransactionLine shipl
ON shipl.transaction = ship.id
AND shipl.linesequencenumber = NTLL.NextLine
WHERE so.type = 'SalesOrd'
AND so.id = 12345
AND NTLL.NextType = 'ItemShip'
ORDER BY sol.linesequencenumber

Sample Result:

sales_orderso_lineitemitem_nameordered_qtyshipped_qtyfulfillmentship_line
SO-0011100Widget A1010IF-0011
SO-0012101Widget B2015IF-0012
SO-0012101Widget B205IF-0021

Find Partially Fulfilled Orders

-- Orders where shipped qty < ordered qty
WITH OrderedQty AS (
SELECT
t.id AS order_id,
t.tranid,
tl.linesequencenumber AS line_num,
tl.item,
tl.quantity AS ordered
FROM Transaction t
INNER JOIN TransactionLine tl ON tl.transaction = t.id
WHERE t.type = 'SalesOrd'
AND tl.mainline = 'F'
AND tl.item IS NOT NULL
),
ShippedQty AS (
SELECT
NTLL.PreviousDoc AS order_id,
NTLL.PreviousLine AS line_num,
SUM(NTLL.Quantity) AS shipped
FROM NextTransactionLineLink NTLL
WHERE NTLL.NextType = 'ItemShip'
GROUP BY NTLL.PreviousDoc, NTLL.PreviousLine
)
SELECT
o.tranid,
o.line_num,
BUILTIN.DF(o.item) AS item,
o.ordered,
NVL(s.shipped, 0) AS shipped,
o.ordered - NVL(s.shipped, 0) AS remaining
FROM OrderedQty o
LEFT JOIN ShippedQty s
ON s.order_id = o.order_id
AND s.line_num = o.line_num
WHERE o.ordered > NVL(s.shipped, 0)
ORDER BY o.tranid, o.line_num

Header-Level Queries

Simple Parent-Child Lookup

-- Using header-level tables (simpler, no line detail)
SELECT DISTINCT
parent.tranid AS parent_doc,
parent.type AS parent_type,
child.tranid AS child_doc,
child.type AS child_type,
child.trandate
FROM NextTransactionLink NTL
INNER JOIN Transaction parent ON parent.id = NTL.PreviousDoc
INNER JOIN Transaction child ON child.id = NTL.NextDoc
WHERE NTL.PreviousDoc = 12345
ORDER BY child.trandate

Find Orders Without Invoices

-- Sales Orders that have no linked invoices
SELECT
so.id,
so.tranid,
so.trandate,
so.foreigntotal,
BUILTIN.DF(so.entity) AS customer
FROM Transaction so
WHERE so.type = 'SalesOrd'
AND so.id NOT IN (
SELECT DISTINCT NTL.PreviousDoc
FROM NextTransactionLink NTL
WHERE NTL.NextType = 'CustInvc'
)
AND so.trandate >= ADD_MONTHS(SYSDATE, -6)
ORDER BY so.trandate DESC

Purchase Cycle Queries

PO to Receipt to Bill Chain

-- Track Purchase Order through Receipt to Vendor Bill
SELECT DISTINCT
po.tranid AS purchase_order,
po.trandate AS po_date,
BUILTIN.DF(po.entity) AS vendor,
rcpt.tranid AS item_receipt,
rcpt.trandate AS receipt_date,
bill.tranid AS vendor_bill,
bill.trandate AS bill_date,
bill.foreigntotal AS bill_amount
FROM Transaction po
-- PO to Receipt
LEFT JOIN NextTransactionLineLink ntll1
ON ntll1.PreviousDoc = po.id
AND ntll1.NextType = 'ItemRcpt'
LEFT JOIN Transaction rcpt ON rcpt.id = ntll1.NextDoc
-- Receipt to Bill
LEFT JOIN NextTransactionLineLink ntll2
ON ntll2.PreviousDoc = rcpt.id
AND ntll2.NextType = 'VendBill'
LEFT JOIN Transaction bill ON bill.id = ntll2.NextDoc
WHERE po.type = 'PurchOrd'
AND po.trandate >= TO_DATE('2024-01-01', 'YYYY-MM-DD')
ORDER BY po.trandate DESC

Unbilled Receipts Report

-- Item Receipts without Vendor Bills
SELECT
rcpt.tranid AS receipt,
rcpt.trandate AS receipt_date,
BUILTIN.DF(rcpt.entity) AS vendor,
po.tranid AS purchase_order,
ROUND(SYSDATE - rcpt.trandate) AS days_unbilled
FROM Transaction rcpt
INNER JOIN PreviousTransactionLineLink ptll
ON ptll.NextDoc = rcpt.id
AND ptll.PreviousType = 'PurchOrd'
INNER JOIN Transaction po ON po.id = ptll.PreviousDoc
WHERE rcpt.type = 'ItemRcpt'
AND rcpt.id NOT IN (
SELECT DISTINCT ntll.PreviousDoc
FROM NextTransactionLineLink ntll
WHERE ntll.NextType = 'VendBill'
)
ORDER BY days_unbilled DESC

Drop Ship Queries

-- Find drop ship POs created from Sales Orders
SELECT DISTINCT
so.tranid AS sales_order,
BUILTIN.DF(so.entity) AS customer,
po.tranid AS drop_ship_po,
BUILTIN.DF(po.entity) AS vendor,
po.trandate AS po_date
FROM Transaction so
INNER JOIN NextTransactionLineLink ntll
ON ntll.PreviousDoc = so.id
AND ntll.LinkType = 'DropShip'
INNER JOIN Transaction po ON po.id = ntll.NextDoc
WHERE so.type = 'SalesOrd'
ORDER BY so.tranid

Performance Tips

Use DISTINCT

Since these are line-level joins, always use DISTINCT when you only need transaction-level results:

-- Without DISTINCT: May return duplicate rows for each line
SELECT t.tranid FROM NextTransactionLineLink NTLL
INNER JOIN Transaction t ON t.id = NTLL.NextDoc
WHERE NTLL.PreviousDoc = 12345

-- With DISTINCT: One row per transaction
SELECT DISTINCT t.tranid FROM NextTransactionLineLink NTLL
INNER JOIN Transaction t ON t.id = NTLL.NextDoc
WHERE NTLL.PreviousDoc = 12345

Filter Early

Apply filters before joining to reduce the dataset:

-- Good: Filter in subquery
SELECT *
FROM (
SELECT DISTINCT NextDoc
FROM NextTransactionLineLink
WHERE PreviousDoc = 12345
AND NextType = 'CustInvc'
) links
INNER JOIN Transaction t ON t.id = links.NextDoc

-- Avoid: Filter after large join
SELECT DISTINCT t.*
FROM NextTransactionLineLink NTLL
INNER JOIN Transaction t ON t.id = NTLL.NextDoc
WHERE NTLL.PreviousDoc = 12345
AND t.type = 'CustInvc'

Alternative: CreatedFrom on TransactionLine

For some use cases, the createdfrom column on TransactionLine may be simpler:

-- Using TransactionLine.createdfrom
SELECT
child.tranid AS child_doc,
child.type AS child_type,
parent.tranid AS parent_doc
FROM Transaction child
INNER JOIN TransactionLine tl ON tl.transaction = child.id
INNER JOIN Transaction parent ON parent.id = tl.createdfrom
WHERE child.id = 12355
AND tl.mainline = 'T'
Limitation

The createdfrom field is not available on the Transaction table in SuiteQL, only on TransactionLine. For header-level lookups, use the link tables.


Quick Reference

Find Children (Next)

SELECT DISTINCT NextDoc, NextType
FROM NextTransactionLineLink
WHERE PreviousDoc = {parent_id}

Find Parent (Previous)

SELECT DISTINCT PreviousDoc, PreviousType
FROM PreviousTransactionLineLink
WHERE NextDoc = {child_id}

Common Join Pattern

FROM Transaction parent
INNER JOIN NextTransactionLineLink NTLL ON NTLL.PreviousDoc = parent.id
INNER JOIN Transaction child ON child.id = NTLL.NextDoc
WHERE parent.id = {id}

See Also